Skip to content

Bug: Additional Attribute in Snapshot Creation leading to a failed Creation for LXC Containers #527

Description

@Jobonabas

📄 Description

When creating a snapshot the API does not differ between LXC Container and Qemu VMs. Qemu VMs need the attribute vmstate, while it is not needed for LXC VMs . This leads to an error on the API side when creating a snapshot of an LXC Container.

👣 Steps to reproduce

For this to work you need to have at least one LXC VM

  1. Try to create a Snapshot using the Create function in the snapshot.go file with the ConfigSnapshot struct
package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	proxmox "github.com/Telmate/proxmox-api-go/proxmox"
	"github.com/joho/godotenv"
)

func main() {
	fmt.Println("Backend running")
	http.HandleFunc("/snapshot/create", CreateSnapshot)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

type CreateSnapshotRequest struct {
	VMID        uint32 `json:"vmid"`
	Name        string `json:"name"`
	Description string `json:"description"`
}

// loadEnv ensures .env is loaded and returns Proxmox API credentials
func loadEnv() (string, string, string, error) {
	if err := godotenv.Load("./.env"); err != nil {
		return "", "", "", fmt.Errorf("failed to load .env: %w", err)
	}
	apiURL := os.Getenv("API_URL")
	tokenID := os.Getenv("TOKEN_ID")
	tokenSecret := os.Getenv("TOKEN_SECRET")
	if apiURL == "" || tokenID == "" || tokenSecret == "" {
		return "", "", "", fmt.Errorf("missing required environment variables")
	}
	return apiURL, tokenID, tokenSecret, nil
}

func enableCors(w http.ResponseWriter) {
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
}

func newProxmoxClient(ctx context.Context) (*proxmox.Client, error) {
	apiURL, tokenID, tokenSecret, err := loadEnv()
	if err != nil {
		return nil, err
	}

	tlsConfig := &tls.Config{InsecureSkipVerify: true}
	hclient := &http.Client{
		Transport: &http.Transport{TLSClientConfig: tlsConfig},
	}

	client, err := proxmox.NewClient(apiURL, hclient, "", tlsConfig, "", 300)
	if err != nil {
		return nil, fmt.Errorf("failed to create client: %w", err)
	}

	client.SetAPIToken(tokenID, tokenSecret)
	return client, nil
}


func CreateSnapshot(w http.ResponseWriter, r *http.Request) {
	enableCors(w)

	// Preflight-Request behandeln
	if r.Method == http.MethodOptions {
		w.WriteHeader(http.StatusOK)
		return
	}

	// Nur POST zulassen
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req CreateSnapshotRequest

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest)
		return
	}

	fmt.Println("VMID:", req.VMID)
	fmt.Println("Name:", req.Name)
	fmt.Println("Description:", req.Description)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
	defer cancel()
	client, err := newProxmoxClient(ctx)

	if err != nil {
		http.Error(w, "Failed to connect to Proxmox", http.StatusInternalServerError)
		return
	}

	vmr, err := client.GetVmRefById(ctx, proxmox.GuestID(req.VMID))
	if err != nil {
		log.Printf("failed to get VM ref: %v", err)
		http.Error(w, "Failed to get VM ref: "+err.Error(), http.StatusInternalServerError)
		return
	}
	snapshot := proxmox.ConfigSnapshot{
		Name:        proxmox.SnapshotName(req.Name),
		Description: req.Description,
	}

       // Those methods were copied from the Create-method only the print statements were inserted for debugging purposes

	if err = client.CheckVmRef(ctx, vmr); err != nil {
		fmt.Printf("VMRef %v\n", err)
	}
	if err = snapshot.Validate(); err != nil {
		fmt.Printf("Snapshot %v\n", err)
	}
	

	if err := snapshot.CreateNoCheck(ctx, client, vmr); err != nil {
		log.Printf("failed to create snapshot: %v\n", err)
		http.Error(w, "Failed to create snapshot: ", http.StatusInternalServerError)
		return
	}

}

❌ Current behaviour

This leads to the following error:
2025/12/02 15:05:58 failed to create snapshot: error creating Snapshot: 400 Parameter verification failed., (params: {"description":"Test3","snapname":"Test3","vmstate":false})
Leaving out the vmstate false in the struct leads to the same error, as the vmstate is automatically replaced.

Image

Using the Proxmox UI I am able to create snapshots as usual.

✔ Expected behaviour

Snapshot creation should differ between Qemu VMs and LXC Containers and send the attributes according to the Proxmox API:
LXC: https://pve.proxmox.com/pve-docs/api-viewer/#/nodes/{node}/lxc/{vmid}/snapshot
Qemu: https://pve.proxmox.com/pve-docs/api-viewer/#/nodes/{node}/qemu/{vmid}/snapshot

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions