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:
parent
a9616648dc
commit
94679a3f5e
@ -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
|
||||
|
||||
|
10
README.md
10
README.md
@ -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`.
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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{
|
||||
|
Loading…
Reference in New Issue
Block a user