From 1d590a6cf28ea5aa5ef6e517e8c7b1e299b61ee1 Mon Sep 17 00:00:00 2001 From: sp-yduck Date: Tue, 12 Dec 2023 14:28:37 +0000 Subject: [PATCH] add vm clone/config --- api/qemu_type.go | 41 ++++++++++++++++++++++++++++++++++++++--- proxmox/qemu.go | 20 ++++++++++++++++++++ rest/qemu.go | 22 ++++++++++++++++++++++ rest/qemu_test.go | 26 ++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 3 deletions(-) diff --git a/api/qemu_type.go b/api/qemu_type.go index aa6633b..188ffdb 100644 --- a/api/qemu_type.go +++ b/api/qemu_type.go @@ -33,6 +33,7 @@ const ( type Arch string type OSType string type ScsiHw string +type StorageFormat string const ( X86_64 Arch = "x86_64" @@ -66,6 +67,12 @@ const ( Pvscsi = "pvscsi" ) +const ( + Raw StorageFormat = "raw" + Qcow2 StorageFormat = "qcow2" + Vmdk StorageFormat = "vmdk" +) + type Ide struct { Ide0 string `json:"ide0,omitempty"` Ide1 string `json:"ide1,omitempty"` @@ -479,11 +486,11 @@ type VirtualMachineConfig struct { // Use STORAGE_ID:0 and the 'import-from' parameter to import from an existing volume. Ide `json:",inline"` IPConfig `json:",inline"` - IvshMem string `json:"ivshmem,omitempty"` + IVshMem string `json:"ivshmem,omitempty"` KeepHugePages int8 `json:"keephugepages,omitempty"` Keyboard string `json:"keyboard,omitempty"` // enable/disable KVM hardware virtualization - Kvm int8 `json:"kvm,omitempty"` + KVM int8 `json:"kvm,omitempty"` LocalTime int8 `json:"localtime,omitempty"` Lock string `json:"lock,omitempty"` // specifies the QEMU machine type @@ -498,7 +505,8 @@ type VirtualMachineConfig struct { NameServer string `json:"nameserver,omitempty"` // network device Net `json:",inline"` - Numa int8 `json:"numa,omitempty"` + Node string `json:"-"` + Numa int8 `json:"numa,omitempty"` NumaS `json:",inline"` // specifies whether a VM will be started during system bootup OnBoot int8 `json:"onboot,omitempty"` @@ -581,3 +589,30 @@ type VirtualMachineStopOption struct { SkipLock int8 `json:"skiplock,omitempty"` TimeOut int `json:"timeout,omitempty"` } + +type VirtualMachineCloneOption struct { + // VMID for the clone. + NewID int `json:"newid"` + // The cluster node name. + Node string `json:"node"` + // The (unique) ID of the VM. + VMID int `json:"vmid"` + // Override I/O bandwidth limit (in KiB/s). + BWLimit int `json:"bwlimit,omitempty"` + // Description for the new VM. + Description string `json:"description,omitempty"` + // Target format for file storage. Only valid for full clone. + Format StorageFormat `json:"format,omitempty"` + // Create a full copy of all disks. This is always done when you clone a normal VM. For VM templates, we try to create a linked clone by default. + Full int8 `json:"full,omitempty"` + // Set a name for the new VM. + Name string `json:"name,omitempty"` + // Add the new VM to the specified pool. + Pool string `json:"pool,omitempty"` + // The name of the snapshot. + SnapName string `json:"snapname,omitempty"` + // Target storage for full clone. + Storage string `json:"storage,omitempty"` + // Target node. Only allowed if the original VM is on shared storage. + Target string `json:"target,omitempty"` +} diff --git a/proxmox/qemu.go b/proxmox/qemu.go index 0e5bdda..4267ea1 100644 --- a/proxmox/qemu.go +++ b/proxmox/qemu.go @@ -69,6 +69,17 @@ func (s *Service) CreateVirtualMachine(ctx context.Context, node string, vmid in return s.VirtualMachine(ctx, vmid) } +func (s *Service) CloneVirtualMachine(ctx context.Context, node string, vmid int, newid int, option api.VirtualMachineCloneOption) (*VirtualMachine, error) { + taskid, err := s.restclient.CreateVirtualMachineClone(ctx, node, vmid, newid, option) + if err != nil { + return nil, err + } + if err := s.EnsureTaskDone(ctx, node, *taskid); err != nil { + return nil, err + } + return s.VirtualMachine(ctx, newid) +} + // VirtualMachineFromUUID attempts to find virtual machine based on SMBIOS UUID. It will ignore any error that prevents // it from inspecting additional virtual machines (e.g. offline node, vm config not accessible, malformed uuids) func (s *Service) VirtualMachineFromUUID(ctx context.Context, uuid string) (*VirtualMachine, error) { @@ -149,6 +160,15 @@ func (c *VirtualMachine) GetConfig(ctx context.Context) (*api.VirtualMachineConf return c.config, err } +// Set virtual machine options (asynchrounous API). +func (c *VirtualMachine) SetConfigAsync(ctx context.Context, config api.VirtualMachineConfig) error { + taskid, err := c.restclient.SetVirtualMachineConfigAsync(ctx, c.Node, c.VM.VMID, config) + if err != nil { + return err + } + return c.service.EnsureTaskDone(ctx, c.Node, *taskid) +} + func (c *VirtualMachine) GetOSInfo(ctx context.Context) (*api.OSInfo, error) { var osInfo *api.OSInfo path := fmt.Sprintf("/nodes/%s/qemu/%d/agent/get-osinfo", c.Node, c.VM.VMID) diff --git a/rest/qemu.go b/rest/qemu.go index 6291103..724449c 100644 --- a/rest/qemu.go +++ b/rest/qemu.go @@ -57,6 +57,16 @@ func (c *RESTClient) GetVirtualMachineConfig(ctx context.Context, node string, v return config, nil } +// Set virtual machine options (asynchrounous API). +func (c *RESTClient) SetVirtualMachineConfigAsync(ctx context.Context, node string, vmid int, options api.VirtualMachineConfig) (*string, error) { + path := fmt.Sprintf("/nodes/%s/qemu/%d/config", node, vmid) + var upid *string + if err := c.Post(ctx, path, options, &upid); err != nil { + return nil, err + } + return upid, nil +} + func (c *RESTClient) GetVirtualMachineStatus(ctx context.Context, node string, vmid int) (*api.ProcessStatus, error) { path := fmt.Sprintf("/nodes/%s/qemu/%d/status", node, vmid) var status *api.ProcessStatus @@ -65,3 +75,15 @@ func (c *RESTClient) GetVirtualMachineStatus(ctx context.Context, node string, v } return status, nil } + +func (c *RESTClient) CreateVirtualMachineClone(ctx context.Context, node string, templateid, vmid int, option api.VirtualMachineCloneOption) (*string, error) { + option.Node = node + option.VMID = templateid + option.NewID = vmid + path := fmt.Sprintf("/nodes/%s/qemu/%d/clone", node, templateid) + var upid *string + if err := c.Post(ctx, path, option, &upid); err != nil { + return nil, err + } + return upid, nil +} diff --git a/rest/qemu_test.go b/rest/qemu_test.go index bca00d6..152a2e7 100644 --- a/rest/qemu_test.go +++ b/rest/qemu_test.go @@ -43,3 +43,29 @@ func (s *TestSuite) TestGetVirtualMachineConfig() { } s.T().Logf("get vm config: %v", *config) } + +func (s *TestSuite) TestSetVirtualMachineConfigAsync() { + nodeName := "assam" + vmid := 999 + config := api.VirtualMachineConfig{ + CiPassword: "pve", + CiUser: "pve", + } + upid, err := s.restclient.SetVirtualMachineConfigAsync(context.TODO(), nodeName, vmid, config) + if err != nil { + s.T().Fatalf("failed to set vm: %v", err) + } + s.T().Logf("set vm config: %s", *upid) + +} + +func (s *TestSuite) TestCreateVirtualMachineClone() { + nodeName := s.GetTestNode().Node + vmid := s.GetTestVM().VMID + option := api.VirtualMachineCloneOption{} + upid, err := s.restclient.CreateVirtualMachineClone(context.TODO(), nodeName, vmid, 999, option) + if err != nil { + s.T().Fatalf("failed to clone vm: %v", err) + } + s.T().Logf("clone vm: %s", *upid) +}