0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-01 02:52:58 +00:00

Initial support for VM cloning

This commit is contained in:
Dan Petersen 2020-01-19 03:40:10 +01:00
parent a9616648dc
commit 94679a3f5e
7 changed files with 759 additions and 155 deletions

View File

@ -4,11 +4,14 @@ ENHANCEMENTS:
* resource/virtual_environment_container: Add `disk` argument
* resource/virtual_environment_vm: Add `audio_device` argument
* resource/virtual_environment_vm: Add `clone` argument
* resource/virtual_environment_vm: Add `serial_device` argument
* resource/virtual_environment_vm: Add `template` argument
BUG FIXES:
* resource/virtual_environment_vm: Fix `network_device` deletion issue
* resource/virtual_environment_vm: Fix slow refresh when VM is stopped and agent is enabled
## 0.2.0

View File

@ -491,6 +491,10 @@ This resource doesn't expose any additional attributes.
* `cdrom` - (Optional) The CDROM configuration
* `enabled` - (Optional) Whether to enable the CDROM drive (defaults to `false`)
* `file_id` - (Optional) A file ID for an ISO file (defaults to `cdrom` as in the physical drive)
* `clone` - (Optional) The cloning configuration
* `datastore_id` - (Optional) The ID of the target datastore
* `node_name` - (Optional) The name of the source node (leave blank, if equal to the `node_name` argument)
* `vm_id` - (Required) The ID of the source VM
* `cpu` - (Optional) The CPU configuration
* `architecture` - (Optional) The CPU architecture (defaults to `x86_64`)
* `aarch64` - ARM (64 bit)
@ -637,6 +641,7 @@ This resource doesn't expose any additional attributes.
* `socket` - A unix socket
* `started` - (Optional) Whether to start the virtual machine (defaults to `true`)
* `tablet_device` - (Optional) Whether to enable the USB tablet device (defaults to `true`)
* `template` - (Optional) Whether to create a template (defaults to `false`)
* `vga` - (Optional) The VGA configuration
* `enabled` - (Optional) Whether to enable the VGA device (defaults to `true`)
* `memory` - (Optional) The VGA memory in megabytes (defaults to `16`)
@ -653,7 +658,7 @@ This resource doesn't expose any additional attributes.
* `std` - Standard VGA
* `virtio` - VirtIO-GPU
* `vmware` - VMware Compatible
* `vm_id` - (Optional) The ID
* `vm_id` - (Optional) The VM identifier
###### Attributes
* `ipv4_addresses` - The IPv4 addresses per network interface published by the QEMU agent (empty list when `agent.enabled` is `false`)
@ -661,6 +666,9 @@ This resource doesn't expose any additional attributes.
* `mac_addresses` - The MAC addresses published by the QEMU agent with fallback to the network device configuration, if the agent is disabled
* `network_interface_names` - The network interface names published by the QEMU agent (empty list when `agent.enabled` is `false`)
###### Notes
When cloning an existing virtual machine, whether it's a template or not, the resource will only detect changes to the arguments which are not set to their default values.
## Developing the Provider
If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (version 1.13+ is *required*). You'll also need to correctly setup a [GOPATH](http://golang.org/doc/code.html#GOPATH), as well as adding `$GOPATH/bin` to your `$PATH`.

View File

@ -1,4 +1,4 @@
resource "proxmox_virtual_environment_vm" "example" {
resource "proxmox_virtual_environment_vm" "example_template" {
agent {
enabled = true
}
@ -30,7 +30,7 @@ resource "proxmox_virtual_environment_vm" "example" {
user_data_file_id = "${proxmox_virtual_environment_file.cloud_config.id}"
}
name = "terraform-provider-proxmox-example"
name = "terraform-provider-proxmox-example-template"
network_device {}
@ -44,7 +44,23 @@ resource "proxmox_virtual_environment_vm" "example" {
serial_device {}
vm_id = 2038
template = true
vm_id = 2040
}
resource "proxmox_virtual_environment_vm" "example" {
name = "terraform-provider-proxmox-example"
node_name = "${data.proxmox_virtual_environment_nodes.example.names[0]}"
pool_id = "${proxmox_virtual_environment_pool.example.id}"
vm_id = 2041
clone {
vm_id = proxmox_virtual_environment_vm.example_template.id
}
memory {
dedicated = 768
}
connection {
type = "ssh"

View File

@ -12,6 +12,11 @@ import (
"time"
)
// CloneVM clones a virtual machine.
func (c *VirtualEnvironmentClient) CloneVM(nodeName string, vmID int, d *VirtualEnvironmentVMCloneRequestBody) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/clone", url.PathEscape(nodeName), vmID), d, nil)
}
// CreateVM creates a virtual machine.
func (c *VirtualEnvironmentClient) CreateVM(nodeName string, d *VirtualEnvironmentVMCreateRequestBody) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu", url.PathEscape(nodeName)), d, nil)
@ -185,6 +190,36 @@ func (c *VirtualEnvironmentClient) WaitForNoNetworkInterfacesFromVMAgent(nodeNam
return fmt.Errorf("Timeout while waiting for the QEMU agent on VM \"%d\" to unpublish the network interfaces", vmID)
}
// WaitForVMConfigUnlock waits for a virtual machine configuration to become unlocked.
func (c *VirtualEnvironmentClient) WaitForVMConfigUnlock(nodeName string, vmID int, timeout int, delay int) error {
timeDelay := int64(delay)
timeMax := float64(timeout)
timeStart := time.Now()
timeElapsed := timeStart.Sub(timeStart)
for timeElapsed.Seconds() < timeMax {
if int64(timeElapsed.Seconds())%timeDelay == 0 {
data, err := c.GetVMStatus(nodeName, vmID)
if err != nil {
return err
}
if data.Lock == nil || *data.Lock == "" {
return nil
}
time.Sleep(1 * time.Second)
}
time.Sleep(200 * time.Millisecond)
timeElapsed = time.Now().Sub(timeStart)
}
return fmt.Errorf("Timeout while waiting for VM \"%d\" configuration to become unlocked", vmID)
}
// WaitForVMState waits for a virtual machine to reach a specific state.
func (c *VirtualEnvironmentClient) WaitForVMState(nodeName string, vmID int, state string, timeout int, delay int) error {
state = strings.ToLower(state)

View File

@ -199,6 +199,20 @@ type CustomWatchdogDevice struct {
Model string `json:"model" url:"model"`
}
// VirtualEnvironmentVMCloneRequestBody contains the data for an virtual machine clone request.
type VirtualEnvironmentVMCloneRequestBody struct {
BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"`
Description *string `json:"description,omitempty" url:"description,omitempty"`
FullCopy *CustomBool `json:"full,omitempty" url:"full,omitempty,int"`
Name *string `json:"name,omitempty" url:"name,omitempty"`
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
SnapshotName *string `json:"snapname,omitempty" url:"snapname,omitempty"`
TargetNodeName *string `json:"target,omitempty" url:"target,omitempty"`
TargetStorage *string `json:"storage,omitempty" url:"storage,omitempty"`
TargetStorageFormat *string `json:"format,omitempty" url:"format,omitempty"`
VMIDNew int `json:"newid" url:"newid"`
}
// VirtualEnvironmentVMCreateRequestBody contains the data for an virtual machine create request.
type VirtualEnvironmentVMCreateRequestBody struct {
ACPI *CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"`

File diff suppressed because it is too large Load Diff

View File

@ -33,10 +33,11 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMAudioDevice,
mkResourceVirtualEnvironmentVMBIOS,
mkResourceVirtualEnvironmentVMCDROM,
mkResourceVirtualEnvironmentVMInitialization,
mkResourceVirtualEnvironmentVMClone,
mkResourceVirtualEnvironmentVMCPU,
mkResourceVirtualEnvironmentVMDescription,
mkResourceVirtualEnvironmentVMDisk,
mkResourceVirtualEnvironmentVMInitialization,
mkResourceVirtualEnvironmentVMKeyboardLayout,
mkResourceVirtualEnvironmentVMMemory,
mkResourceVirtualEnvironmentVMName,
@ -46,6 +47,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMSerialDevice,
mkResourceVirtualEnvironmentVMStarted,
mkResourceVirtualEnvironmentVMTabletDevice,
mkResourceVirtualEnvironmentVMTemplate,
mkResourceVirtualEnvironmentVMVMID,
})
@ -62,10 +64,10 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMAudioDevice,
mkResourceVirtualEnvironmentVMBIOS,
mkResourceVirtualEnvironmentVMCDROM,
mkResourceVirtualEnvironmentVMInitialization,
mkResourceVirtualEnvironmentVMCPU,
mkResourceVirtualEnvironmentVMDescription,
mkResourceVirtualEnvironmentVMDisk,
mkResourceVirtualEnvironmentVMInitialization,
mkResourceVirtualEnvironmentVMIPv4Addresses,
mkResourceVirtualEnvironmentVMIPv6Addresses,
mkResourceVirtualEnvironmentVMKeyboardLayout,
@ -79,6 +81,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMSerialDevice,
mkResourceVirtualEnvironmentVMStarted,
mkResourceVirtualEnvironmentVMTabletDevice,
mkResourceVirtualEnvironmentVMTemplate,
mkResourceVirtualEnvironmentVMVMID,
}, []schema.ValueType{
schema.TypeBool,
@ -87,11 +90,11 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
schema.TypeString,
schema.TypeList,
schema.TypeList,
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeList,
schema.TypeList,
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeString,
@ -103,6 +106,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
schema.TypeList,
schema.TypeBool,
schema.TypeBool,
schema.TypeBool,
schema.TypeInt,
})
@ -154,6 +158,27 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
schema.TypeString,
})
cloneSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMClone)
testRequiredArguments(t, cloneSchema, []string{
mkResourceVirtualEnvironmentVMCloneVMID,
})
testOptionalArguments(t, cloneSchema, []string{
mkResourceVirtualEnvironmentVMCloneDatastoreID,
mkResourceVirtualEnvironmentVMCloneNodeName,
})
testSchemaValueTypes(t, cloneSchema, []string{
mkResourceVirtualEnvironmentVMCloneDatastoreID,
mkResourceVirtualEnvironmentVMCloneNodeName,
mkResourceVirtualEnvironmentVMCloneVMID,
}, []schema.ValueType{
schema.TypeString,
schema.TypeString,
schema.TypeInt,
})
cpuSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMCPU)
testOptionalArguments(t, cpuSchema, []string{