mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-02 03:22:59 +00:00
Initial support for VM cloning
This commit is contained in:
parent
a9616648dc
commit
94679a3f5e
@ -4,11 +4,14 @@ ENHANCEMENTS:
|
|||||||
|
|
||||||
* resource/virtual_environment_container: Add `disk` argument
|
* resource/virtual_environment_container: Add `disk` argument
|
||||||
* resource/virtual_environment_vm: Add `audio_device` 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 `serial_device` argument
|
||||||
|
* resource/virtual_environment_vm: Add `template` argument
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
|
|
||||||
* resource/virtual_environment_vm: Fix `network_device` deletion issue
|
* 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
|
## 0.2.0
|
||||||
|
|
||||||
|
10
README.md
10
README.md
@ -491,6 +491,10 @@ This resource doesn't expose any additional attributes.
|
|||||||
* `cdrom` - (Optional) The CDROM configuration
|
* `cdrom` - (Optional) The CDROM configuration
|
||||||
* `enabled` - (Optional) Whether to enable the CDROM drive (defaults to `false`)
|
* `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)
|
* `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
|
* `cpu` - (Optional) The CPU configuration
|
||||||
* `architecture` - (Optional) The CPU architecture (defaults to `x86_64`)
|
* `architecture` - (Optional) The CPU architecture (defaults to `x86_64`)
|
||||||
* `aarch64` - ARM (64 bit)
|
* `aarch64` - ARM (64 bit)
|
||||||
@ -637,6 +641,7 @@ This resource doesn't expose any additional attributes.
|
|||||||
* `socket` - A unix socket
|
* `socket` - A unix socket
|
||||||
* `started` - (Optional) Whether to start the virtual machine (defaults to `true`)
|
* `started` - (Optional) Whether to start the virtual machine (defaults to `true`)
|
||||||
* `tablet_device` - (Optional) Whether to enable the USB tablet device (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
|
* `vga` - (Optional) The VGA configuration
|
||||||
* `enabled` - (Optional) Whether to enable the VGA device (defaults to `true`)
|
* `enabled` - (Optional) Whether to enable the VGA device (defaults to `true`)
|
||||||
* `memory` - (Optional) The VGA memory in megabytes (defaults to `16`)
|
* `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
|
* `std` - Standard VGA
|
||||||
* `virtio` - VirtIO-GPU
|
* `virtio` - VirtIO-GPU
|
||||||
* `vmware` - VMware Compatible
|
* `vmware` - VMware Compatible
|
||||||
* `vm_id` - (Optional) The ID
|
* `vm_id` - (Optional) The VM identifier
|
||||||
|
|
||||||
###### Attributes
|
###### Attributes
|
||||||
* `ipv4_addresses` - The IPv4 addresses per network interface published by the QEMU agent (empty list when `agent.enabled` is `false`)
|
* `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
|
* `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`)
|
* `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
|
## 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`.
|
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`.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
resource "proxmox_virtual_environment_vm" "example" {
|
resource "proxmox_virtual_environment_vm" "example_template" {
|
||||||
agent {
|
agent {
|
||||||
enabled = true
|
enabled = true
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ resource "proxmox_virtual_environment_vm" "example" {
|
|||||||
user_data_file_id = "${proxmox_virtual_environment_file.cloud_config.id}"
|
user_data_file_id = "${proxmox_virtual_environment_file.cloud_config.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
name = "terraform-provider-proxmox-example"
|
name = "terraform-provider-proxmox-example-template"
|
||||||
|
|
||||||
network_device {}
|
network_device {}
|
||||||
|
|
||||||
@ -44,7 +44,23 @@ resource "proxmox_virtual_environment_vm" "example" {
|
|||||||
|
|
||||||
serial_device {}
|
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 {
|
connection {
|
||||||
type = "ssh"
|
type = "ssh"
|
||||||
|
@ -12,6 +12,11 @@ import (
|
|||||||
"time"
|
"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.
|
// CreateVM creates a virtual machine.
|
||||||
func (c *VirtualEnvironmentClient) CreateVM(nodeName string, d *VirtualEnvironmentVMCreateRequestBody) error {
|
func (c *VirtualEnvironmentClient) CreateVM(nodeName string, d *VirtualEnvironmentVMCreateRequestBody) error {
|
||||||
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu", url.PathEscape(nodeName)), d, nil)
|
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)
|
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.
|
// 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 {
|
func (c *VirtualEnvironmentClient) WaitForVMState(nodeName string, vmID int, state string, timeout int, delay int) error {
|
||||||
state = strings.ToLower(state)
|
state = strings.ToLower(state)
|
||||||
|
@ -199,6 +199,20 @@ type CustomWatchdogDevice struct {
|
|||||||
Model string `json:"model" url:"model"`
|
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.
|
// VirtualEnvironmentVMCreateRequestBody contains the data for an virtual machine create request.
|
||||||
type VirtualEnvironmentVMCreateRequestBody struct {
|
type VirtualEnvironmentVMCreateRequestBody struct {
|
||||||
ACPI *CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"`
|
ACPI *CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"`
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -33,10 +33,11 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMAudioDevice,
|
mkResourceVirtualEnvironmentVMAudioDevice,
|
||||||
mkResourceVirtualEnvironmentVMBIOS,
|
mkResourceVirtualEnvironmentVMBIOS,
|
||||||
mkResourceVirtualEnvironmentVMCDROM,
|
mkResourceVirtualEnvironmentVMCDROM,
|
||||||
mkResourceVirtualEnvironmentVMInitialization,
|
mkResourceVirtualEnvironmentVMClone,
|
||||||
mkResourceVirtualEnvironmentVMCPU,
|
mkResourceVirtualEnvironmentVMCPU,
|
||||||
mkResourceVirtualEnvironmentVMDescription,
|
mkResourceVirtualEnvironmentVMDescription,
|
||||||
mkResourceVirtualEnvironmentVMDisk,
|
mkResourceVirtualEnvironmentVMDisk,
|
||||||
|
mkResourceVirtualEnvironmentVMInitialization,
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
||||||
mkResourceVirtualEnvironmentVMMemory,
|
mkResourceVirtualEnvironmentVMMemory,
|
||||||
mkResourceVirtualEnvironmentVMName,
|
mkResourceVirtualEnvironmentVMName,
|
||||||
@ -46,6 +47,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMSerialDevice,
|
mkResourceVirtualEnvironmentVMSerialDevice,
|
||||||
mkResourceVirtualEnvironmentVMStarted,
|
mkResourceVirtualEnvironmentVMStarted,
|
||||||
mkResourceVirtualEnvironmentVMTabletDevice,
|
mkResourceVirtualEnvironmentVMTabletDevice,
|
||||||
|
mkResourceVirtualEnvironmentVMTemplate,
|
||||||
mkResourceVirtualEnvironmentVMVMID,
|
mkResourceVirtualEnvironmentVMVMID,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -62,10 +64,10 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMAudioDevice,
|
mkResourceVirtualEnvironmentVMAudioDevice,
|
||||||
mkResourceVirtualEnvironmentVMBIOS,
|
mkResourceVirtualEnvironmentVMBIOS,
|
||||||
mkResourceVirtualEnvironmentVMCDROM,
|
mkResourceVirtualEnvironmentVMCDROM,
|
||||||
mkResourceVirtualEnvironmentVMInitialization,
|
|
||||||
mkResourceVirtualEnvironmentVMCPU,
|
mkResourceVirtualEnvironmentVMCPU,
|
||||||
mkResourceVirtualEnvironmentVMDescription,
|
mkResourceVirtualEnvironmentVMDescription,
|
||||||
mkResourceVirtualEnvironmentVMDisk,
|
mkResourceVirtualEnvironmentVMDisk,
|
||||||
|
mkResourceVirtualEnvironmentVMInitialization,
|
||||||
mkResourceVirtualEnvironmentVMIPv4Addresses,
|
mkResourceVirtualEnvironmentVMIPv4Addresses,
|
||||||
mkResourceVirtualEnvironmentVMIPv6Addresses,
|
mkResourceVirtualEnvironmentVMIPv6Addresses,
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
||||||
@ -79,6 +81,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMSerialDevice,
|
mkResourceVirtualEnvironmentVMSerialDevice,
|
||||||
mkResourceVirtualEnvironmentVMStarted,
|
mkResourceVirtualEnvironmentVMStarted,
|
||||||
mkResourceVirtualEnvironmentVMTabletDevice,
|
mkResourceVirtualEnvironmentVMTabletDevice,
|
||||||
|
mkResourceVirtualEnvironmentVMTemplate,
|
||||||
mkResourceVirtualEnvironmentVMVMID,
|
mkResourceVirtualEnvironmentVMVMID,
|
||||||
}, []schema.ValueType{
|
}, []schema.ValueType{
|
||||||
schema.TypeBool,
|
schema.TypeBool,
|
||||||
@ -87,11 +90,11 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeList,
|
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
@ -103,6 +106,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeBool,
|
schema.TypeBool,
|
||||||
schema.TypeBool,
|
schema.TypeBool,
|
||||||
|
schema.TypeBool,
|
||||||
schema.TypeInt,
|
schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -154,6 +158,27 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
schema.TypeString,
|
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)
|
cpuSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMCPU)
|
||||||
|
|
||||||
testOptionalArguments(t, cpuSchema, []string{
|
testOptionalArguments(t, cpuSchema, []string{
|
||||||
|
Loading…
Reference in New Issue
Block a user