0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-06-30 02:31:10 +00:00

Continued work on VM resource

This commit is contained in:
Dan Petersen 2019-12-27 19:13:40 +01:00
parent 408da5f420
commit 08e389dac8
11 changed files with 343 additions and 28 deletions

View File

@ -314,6 +314,10 @@ This resource doesn't expose any additional attributes.
##### VM (proxmox_virtual_environment_vm)
###### Arguments
* `agent` - (Optional) The QEMU agent configuration
* `enabled` - (Optional) Whether to enable the QEMU agent (defaults to `false`)
* `trim` - (Optional) Whether to enable the FSTRIM feature in the QEMU agent (defaults to `false`)
* `type` - (Optional) The QEMU agent interface type (defaults to `virtio`)
* `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)
@ -333,7 +337,9 @@ This resource doesn't expose any additional attributes.
* `username` - (Required) The SSH username
* `cpu` - (Optional) The CPU configuration
* `cores` - (Optional) The number of CPU cores (defaults to `1`)
* `hotplugged` - (Optional) The number of hotplugged vCPUs (defaults to `0`)
* `sockets` - (Optional) The number of CPU sockets (defaults to `1`)
* `description` - (Optional) The description
* `disk` - (Optional) The disk configuration (multiple blocks supported)
* `datastore_id` - (Optional) The ID of the datastore to create the disk in (defaults to `local-lvm`)
* `enabled` - (Optional) Whether to enable the disk (defaults to `true`)
@ -345,7 +351,7 @@ This resource doesn't expose any additional attributes.
* `dedicated` - (Optional) The dedicated memory in megabytes (defaults to `512`)
* `floating` - (Optional) The floating memory in megabytes (defaults to `0`)
* `shared` - (Optional) The shared memory in megabytes (defaults to `0`)
* `name` - (Optional) The virtual machine name
* `name` - (Optional) The name
* `network_device` - (Optional) The network device configuration (multiple blocks supported)
* `bridge` - (Optional) The name of the network bridge (defaults to `vmbr0`)
* `enabled` - (Optional) Whether to enable the network device (defaults to `true`)
@ -354,7 +360,8 @@ This resource doesn't expose any additional attributes.
* `vlan_id` - (Optional) The VLAN identifier
* `node_name` - (Required) The name of the node to assign the virtual machine to
* `os_type` - (Optional) The OS type (defaults to `other`)
* `vm_id` - (Optional) The virtual machine ID
* `pool_id` - (Optional) The ID of a pool to assign the virtual machine to
* `vm_id` - (Optional) The ID
###### Attributes
This resource doesn't expose any additional attributes.

View File

@ -25,6 +25,8 @@ resource "proxmox_virtual_environment_vm" "example" {
node_name = "${data.proxmox_virtual_environment_nodes.example.names[0]}"
os_type = "l26"
pool_id = "${proxmox_virtual_environment_pool.example.id}"
vm_id = 2038
}
resource "local_file" "example_ssh_private_key" {

View File

@ -8,19 +8,21 @@ import (
"errors"
"fmt"
"net/url"
"strings"
"time"
)
// CreateVM creates an virtual machine.
// 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)
}
// DeleteVM deletes an virtual machine.
// DeleteVM deletes a virtual machine.
func (c *VirtualEnvironmentClient) DeleteVM(nodeName string, vmID int) error {
return c.DoRequest(hmDELETE, fmt.Sprintf("nodes/%s/qemu/%d", url.PathEscape(nodeName), vmID), nil, nil)
}
// GetVM retrieves an virtual machine.
// GetVM retrieves a virtual machine.
func (c *VirtualEnvironmentClient) GetVM(nodeName string, vmID int) (*VirtualEnvironmentVMGetResponseData, error) {
resBody := &VirtualEnvironmentVMGetResponseBody{}
err := c.DoRequest(hmGET, fmt.Sprintf("nodes/%s/qemu/%d/config", url.PathEscape(nodeName), vmID), nil, resBody)
@ -64,11 +66,42 @@ VMID:
return nil, errors.New("Unable to retrieve the next available VM identifier")
}
// GetVMStatus retrieves the status for a virtual machine.
func (c *VirtualEnvironmentClient) GetVMStatus(nodeName string, vmID int) (*VirtualEnvironmentVMGetStatusResponseData, error) {
resBody := &VirtualEnvironmentVMGetStatusResponseBody{}
err := c.DoRequest(hmGET, fmt.Sprintf("nodes/%s/qemu/%d/status/current", url.PathEscape(nodeName), vmID), nil, resBody)
if err != nil {
return nil, err
}
if resBody.Data == nil {
return nil, errors.New("The server did not include a data object in the response")
}
return resBody.Data, nil
}
// ListVMs retrieves a list of virtual machines.
func (c *VirtualEnvironmentClient) ListVMs() ([]*VirtualEnvironmentVMListResponseData, error) {
return nil, errors.New("Not implemented")
}
// ShutdownVM shuts down a virtual machine.
func (c *VirtualEnvironmentClient) ShutdownVM(nodeName string, vmID int, d *VirtualEnvironmentVMShutdownRequestBody) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/status/shutdown", url.PathEscape(nodeName), vmID), d, nil)
}
// StartVM starts a virtual machine.
func (c *VirtualEnvironmentClient) StartVM(nodeName string, vmID int) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/status/start", url.PathEscape(nodeName), vmID), nil, nil)
}
// StopVM stops a virtual machine immediately.
func (c *VirtualEnvironmentClient) StopVM(nodeName string, vmID int) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/status/stop", url.PathEscape(nodeName), vmID), nil, nil)
}
// UpdateVM updates a virtual machine.
func (c *VirtualEnvironmentClient) UpdateVM(nodeName string, vmID int, d *VirtualEnvironmentVMUpdateRequestBody) error {
return c.DoRequest(hmPUT, fmt.Sprintf("nodes/%s/qemu/%d/config", url.PathEscape(nodeName), vmID), d, nil)
@ -78,3 +111,35 @@ func (c *VirtualEnvironmentClient) UpdateVM(nodeName string, vmID int, d *Virtua
func (c *VirtualEnvironmentClient) UpdateVMAsync(nodeName string, vmID int, d *VirtualEnvironmentVMUpdateRequestBody) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/config", url.PathEscape(nodeName), vmID), d, nil)
}
// WaitForState waits for a virtual machine to reach a specific state.
func (c *VirtualEnvironmentClient) WaitForState(nodeName string, vmID int, state string, timeout int, delay int) error {
state = strings.ToLower(state)
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.Status == state {
return nil
}
time.Sleep(1 * time.Second)
}
time.Sleep(200 * time.Millisecond)
timeElapsed = time.Now().Sub(timeStart)
}
return fmt.Errorf("Failed to wait for VM \"%d\" to enter the state \"%s\"", vmID, state)
}

View File

@ -225,6 +225,7 @@ type VirtualEnvironmentVMCreateRequestBody struct {
OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"`
Overwrite *CustomBool `json:"force,omitempty" url:"force,omitempty,int"`
PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"`
PoolID *string `json:"pool,omitempty" url:"pool,omitempty"`
Revert *string `json:"revert,omitempty" url:"revert,omitempty"`
SATADevices CustomStorageDevices `json:"sata,omitempty" url:"sata,omitempty"`
SCSIDevices CustomStorageDevices `json:"scsi,omitempty" url:"scsi,omitempty"`
@ -326,6 +327,28 @@ type VirtualEnvironmentVMGetResponseData struct {
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"`
}
// VirtualEnvironmentVMGetStatusResponseBody contains the body from a VM get status response.
type VirtualEnvironmentVMGetStatusResponseBody struct {
Data *VirtualEnvironmentVMGetStatusResponseData `json:"data,omitempty"`
}
// VirtualEnvironmentVMGetStatusResponseData contains the data from a VM get status response.
type VirtualEnvironmentVMGetStatusResponseData struct {
AgentEnabled *CustomBool `json:"agent,omitempty"`
CPUCount *float64 `json:"cpus,omitempty"`
Lock *string `json:"lock,omitempty"`
MemoryAllocation *int `json:"maxmem,omitempty"`
Name *string `json:"name,omitempty"`
PID *string `json:"pid,omitempty"`
QMPStatus *string `json:"qmpstatus,omitempty"`
RootDiskSize *int `json:"maxdisk,omitempty"`
SpiceSupport *CustomBool `json:"spice,omitempty"`
Status string `json:"status,omitempty"`
Tags *string `json:"tags,omitempty"`
Uptime *int `json:"uptime,omitempty"`
VMID string `json:"vmid,omitempty"`
}
// VirtualEnvironmentVMListResponseBody contains the body from an virtual machine list response.
type VirtualEnvironmentVMListResponseBody struct {
Data []*VirtualEnvironmentVMListResponseData `json:"data,omitempty"`
@ -336,6 +359,14 @@ type VirtualEnvironmentVMListResponseData struct {
ACPI *CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"`
}
// VirtualEnvironmentVMShutdownRequestBody contains the body for a VM shutdown request.
type VirtualEnvironmentVMShutdownRequestBody struct {
ForceStop *CustomBool `json:"forceStop,omitempty,int" url:"forceStop,omitempty,int"`
KeepActive *CustomBool `json:"keepActive,omitempty,int" url:"keepActive,omitempty,int"`
SkipLock *CustomBool `json:"skipLock,omitempty,int" url:"skipLock,omitempty,int"`
Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"`
}
// VirtualEnvironmentVMUpdateRequestBody contains the data for an virtual machine update request.
type VirtualEnvironmentVMUpdateRequestBody VirtualEnvironmentVMCreateRequestBody

View File

@ -24,6 +24,12 @@ import (
)
const (
dvResourceVirtualEnvironmentFileContentType = ""
dvResourceVirtualEnvironmentFileOverrideFileName = ""
dvResourceVirtualEnvironmentFileSourceChanged = false
dvResourceVirtualEnvironmentFileSourceChecksum = ""
dvResourceVirtualEnvironmentFileSourceInsecure = false
mkResourceVirtualEnvironmentFileContentType = "content_type"
mkResourceVirtualEnvironmentFileDatastoreID = "datastore_id"
mkResourceVirtualEnvironmentFileFileModificationDate = "file_modification_date"
@ -46,7 +52,7 @@ func resourceVirtualEnvironmentFile() *schema.Resource {
Description: "The content type",
Optional: true,
ForceNew: true,
Default: "",
Default: dvResourceVirtualEnvironmentFileContentType,
},
mkResourceVirtualEnvironmentFileDatastoreID: &schema.Schema{
Type: schema.TypeString,
@ -82,7 +88,7 @@ func resourceVirtualEnvironmentFile() *schema.Resource {
Description: "The file name to use instead of the source file name",
Optional: true,
ForceNew: true,
Default: "",
Default: dvResourceVirtualEnvironmentFileOverrideFileName,
},
mkResourceVirtualEnvironmentFileNodeName: &schema.Schema{
Type: schema.TypeString,
@ -101,21 +107,21 @@ func resourceVirtualEnvironmentFile() *schema.Resource {
Description: "Whether the source has changed since the last run",
Optional: true,
ForceNew: true,
Default: false,
Default: dvResourceVirtualEnvironmentFileSourceChanged,
},
mkResourceVirtualEnvironmentFileSourceChecksum: &schema.Schema{
Type: schema.TypeString,
Description: "The SHA256 checksum of the source file",
Optional: true,
ForceNew: true,
Default: "",
Default: dvResourceVirtualEnvironmentFileSourceChecksum,
},
mkResourceVirtualEnvironmentFileSourceInsecure: &schema.Schema{
Type: schema.TypeBool,
Description: "Whether to skip the TLS verification step for HTTPS sources",
Optional: true,
ForceNew: true,
Default: false,
Default: dvResourceVirtualEnvironmentFileSourceInsecure,
},
},
Create: resourceVirtualEnvironmentFileCreate,

View File

@ -12,6 +12,8 @@ import (
)
const (
dvResourceVirtualEnvironmentGroupComment = ""
mkResourceVirtualEnvironmentGroupACL = "acl"
mkResourceVirtualEnvironmentGroupACLPath = "path"
mkResourceVirtualEnvironmentGroupACLPropagate = "propagate"
@ -56,7 +58,7 @@ func resourceVirtualEnvironmentGroup() *schema.Resource {
Type: schema.TypeString,
Description: "The group comment",
Optional: true,
Default: "",
Default: dvResourceVirtualEnvironmentGroupComment,
},
mkResourceVirtualEnvironmentGroupID: &schema.Schema{
Type: schema.TypeString,

View File

@ -12,6 +12,8 @@ import (
)
const (
dvResourceVirtualEnvironmentPoolComment = ""
mkResourceVirtualEnvironmentPoolComment = "comment"
mkResourceVirtualEnvironmentPoolMembers = "members"
mkResourceVirtualEnvironmentPoolMembersDatastoreID = "datastore_id"
@ -29,7 +31,7 @@ func resourceVirtualEnvironmentPool() *schema.Resource {
Type: schema.TypeString,
Description: "The pool comment",
Optional: true,
Default: "",
Default: dvResourceVirtualEnvironmentPoolComment,
},
mkResourceVirtualEnvironmentPoolMembers: &schema.Schema{
Type: schema.TypeList,

View File

@ -14,6 +14,13 @@ import (
)
const (
dvResourceVirtualEnvironmentUserComment = ""
dvResourceVirtualEnvironmentUserEmail = ""
dvResourceVirtualEnvironmentUserEnabled = true
dvResourceVirtualEnvironmentUserFirstName = ""
dvResourceVirtualEnvironmentUserKeys = ""
dvResourceVirtualEnvironmentUserLastName = ""
mkResourceVirtualEnvironmentUserACL = "acl"
mkResourceVirtualEnvironmentUserACLPath = "path"
mkResourceVirtualEnvironmentUserACLPropagate = "propagate"
@ -65,19 +72,19 @@ func resourceVirtualEnvironmentUser() *schema.Resource {
Type: schema.TypeString,
Description: "The user comment",
Optional: true,
Default: "",
Default: dvResourceVirtualEnvironmentUserComment,
},
mkResourceVirtualEnvironmentUserEmail: &schema.Schema{
Type: schema.TypeString,
Description: "The user's email address",
Optional: true,
Default: "",
Default: dvResourceVirtualEnvironmentUserEmail,
},
mkResourceVirtualEnvironmentUserEnabled: &schema.Schema{
Type: schema.TypeBool,
Description: "Whether the user account is enabled",
Optional: true,
Default: true,
Default: dvResourceVirtualEnvironmentUserEnabled,
},
mkResourceVirtualEnvironmentUserExpirationDate: &schema.Schema{
Type: schema.TypeString,
@ -90,7 +97,7 @@ func resourceVirtualEnvironmentUser() *schema.Resource {
Type: schema.TypeString,
Description: "The user's first name",
Optional: true,
Default: "",
Default: dvResourceVirtualEnvironmentUserFirstName,
},
mkResourceVirtualEnvironmentUserGroups: &schema.Schema{
Type: schema.TypeSet,
@ -105,13 +112,13 @@ func resourceVirtualEnvironmentUser() *schema.Resource {
Type: schema.TypeString,
Description: "The user's keys",
Optional: true,
Default: "",
Default: dvResourceVirtualEnvironmentUserKeys,
},
mkResourceVirtualEnvironmentUserLastName: &schema.Schema{
Type: schema.TypeString,
Description: "The user's last name",
Optional: true,
Default: "",
Default: dvResourceVirtualEnvironmentUserLastName,
},
mkResourceVirtualEnvironmentUserPassword: &schema.Schema{
Type: schema.TypeString,

View File

@ -6,7 +6,6 @@ package proxmoxtf
import (
"fmt"
"log"
"strconv"
"strings"
@ -16,12 +15,17 @@ import (
)
const (
dvResourceVirtualEnvironmentVMAgentEnabled = false
dvResourceVirtualEnvironmentVMAgentTrim = false
dvResourceVirtualEnvironmentVMAgentType = "virtio"
dvResourceVirtualEnvironmentVMCDROMEnabled = false
dvResourceVirtualEnvironmentVMCDROMFileID = ""
dvResourceVirtualEnvironmentVMCloudInitDNSDomain = ""
dvResourceVirtualEnvironmentVMCloudInitDNSServer = ""
dvResourceVirtualEnvironmentVMCPUCores = 1
dvResourceVirtualEnvironmentVMCPUHotplugged = 0
dvResourceVirtualEnvironmentVMCPUSockets = 1
dvResourceVirtualEnvironmentVMDescription = ""
dvResourceVirtualEnvironmentVMDiskDatastoreID = "local-lvm"
dvResourceVirtualEnvironmentVMDiskEnabled = true
dvResourceVirtualEnvironmentVMDiskFileFormat = "qcow2"
@ -38,8 +42,13 @@ const (
dvResourceVirtualEnvironmentVMNetworkDeviceModel = "virtio"
dvResourceVirtualEnvironmentVMNetworkDeviceVLANID = -1
dvResourceVirtualEnvironmentVMOSType = "other"
dvResourceVirtualEnvironmentVMPoolID = ""
dvResourceVirtualEnvironmentVMVMID = -1
mkResourceVirtualEnvironmentVMAgent = "agent"
mkResourceVirtualEnvironmentVMAgentEnabled = "enabled"
mkResourceVirtualEnvironmentVMAgentTrim = "trim"
mkResourceVirtualEnvironmentVMAgentType = "type"
mkResourceVirtualEnvironmentVMCDROM = "cdrom"
mkResourceVirtualEnvironmentVMCDROMEnabled = "enabled"
mkResourceVirtualEnvironmentVMCDROMFileID = "file_id"
@ -59,7 +68,9 @@ const (
mkResourceVirtualEnvironmentVMCloudInitUserAccountUsername = "username"
mkResourceVirtualEnvironmentVMCPU = "cpu"
mkResourceVirtualEnvironmentVMCPUCores = "cores"
mkResourceVirtualEnvironmentVMCPUHotplugged = "hotplugged"
mkResourceVirtualEnvironmentVMCPUSockets = "sockets"
mkResourceVirtualEnvironmentVMDescription = "description"
mkResourceVirtualEnvironmentVMDisk = "disk"
mkResourceVirtualEnvironmentVMDiskDatastoreID = "datastore_id"
mkResourceVirtualEnvironmentVMDiskEnabled = "enabled"
@ -80,12 +91,55 @@ const (
mkResourceVirtualEnvironmentVMNetworkDeviceVLANID = "vlan_id"
mkResourceVirtualEnvironmentVMNodeName = "node_name"
mkResourceVirtualEnvironmentVMOSType = "os_type"
mkResourceVirtualEnvironmentVMPoolID = "pool_id"
mkResourceVirtualEnvironmentVMVMID = "vm_id"
)
func resourceVirtualEnvironmentVM() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
mkResourceVirtualEnvironmentVMAgent: &schema.Schema{
Type: schema.TypeList,
Description: "The QEMU agent configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
defaultList := make([]interface{}, 1)
defaultMap := make(map[string]interface{})
defaultMap[mkResourceVirtualEnvironmentVMAgentEnabled] = dvResourceVirtualEnvironmentVMAgentEnabled
defaultMap[mkResourceVirtualEnvironmentVMAgentTrim] = dvResourceVirtualEnvironmentVMAgentTrim
defaultMap[mkResourceVirtualEnvironmentVMAgentType] = dvResourceVirtualEnvironmentVMAgentType
defaultList[0] = defaultMap
return defaultList, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
mkResourceVirtualEnvironmentVMAgentEnabled: {
Type: schema.TypeBool,
Optional: true,
Description: "Whether to enable the QEMU agent",
Default: dvResourceVirtualEnvironmentVMAgentEnabled,
},
mkResourceVirtualEnvironmentVMAgentTrim: {
Type: schema.TypeBool,
Optional: true,
Description: "Whether to enable the FSTRIM feature in the QEMU agent",
Default: dvResourceVirtualEnvironmentVMAgentTrim,
},
mkResourceVirtualEnvironmentVMAgentType: {
Type: schema.TypeString,
Optional: true,
Description: "The QEMU agent interface type",
Default: dvResourceVirtualEnvironmentVMAgentType,
ValidateFunc: getQEMUAgentTypeValidator(),
},
},
},
MaxItems: 1,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMCDROM: &schema.Schema{
Type: schema.TypeList,
Description: "The CDROM drive",
@ -261,6 +315,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
defaultMap := make(map[string]interface{})
defaultMap[mkResourceVirtualEnvironmentVMCPUCores] = dvResourceVirtualEnvironmentVMCPUCores
defaultMap[mkResourceVirtualEnvironmentVMCPUHotplugged] = dvResourceVirtualEnvironmentVMCPUHotplugged
defaultMap[mkResourceVirtualEnvironmentVMCPUSockets] = dvResourceVirtualEnvironmentVMCPUSockets
defaultList[0] = defaultMap
@ -276,6 +331,13 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Default: dvResourceVirtualEnvironmentVMCPUCores,
ValidateFunc: validation.IntBetween(1, 2304),
},
mkResourceVirtualEnvironmentVMCPUHotplugged: {
Type: schema.TypeInt,
Optional: true,
Description: "The number of hotplugged vCPUs",
Default: dvResourceVirtualEnvironmentVMCPUHotplugged,
ValidateFunc: validation.IntBetween(0, 2304),
},
mkResourceVirtualEnvironmentVMCPUSockets: {
Type: schema.TypeInt,
Optional: true,
@ -288,6 +350,12 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
MaxItems: 1,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMDescription: {
Type: schema.TypeString,
Optional: true,
Description: "The description",
Default: dvResourceVirtualEnvironmentVMDescription,
},
mkResourceVirtualEnvironmentVMDisk: &schema.Schema{
Type: schema.TypeList,
Description: "The disk devices",
@ -461,6 +529,12 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Default: dvResourceVirtualEnvironmentVMOSType,
ValidateFunc: getOSTypeValidator(),
},
mkResourceVirtualEnvironmentVMPoolID: {
Type: schema.TypeString,
Optional: true,
Description: "The ID of the pool to assign the virtual machine to",
Default: dvResourceVirtualEnvironmentVMPoolID,
},
mkResourceVirtualEnvironmentVMVMID: {
Type: schema.TypeInt,
Optional: true,
@ -486,6 +560,23 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
}
schema := resourceVirtualEnvironmentVM().Schema
agent := d.Get(mkResourceVirtualEnvironmentVMAgent).([]interface{})
if len(agent) == 0 {
agentDefault, err := schema[mkResourceVirtualEnvironmentVMAgent].DefaultValue()
if err != nil {
return err
}
agent = agentDefault.([]interface{})
}
agentBlock := agent[0].(map[string]interface{})
agentEnabled := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool))
agentTrim := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool))
agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string)
cdrom := d.Get(mkResourceVirtualEnvironmentVMCDROM).([]interface{})
if len(cdrom) == 0 {
@ -609,8 +700,10 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
cpuBlock := cpu[0].(map[string]interface{})
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
description := d.Get(mkResourceVirtualEnvironmentVMDescription).(string)
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
scsiDevices := make(proxmox.CustomStorageDevices, len(disk))
@ -689,6 +782,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
osType := d.Get(mkResourceVirtualEnvironmentVMOSType).(string)
poolID := d.Get(mkResourceVirtualEnvironmentVMPoolID).(string)
vmID := d.Get(mkResourceVirtualEnvironmentVMVMID).(int)
if vmID == -1 {
@ -703,7 +797,6 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
var memorySharedObject *proxmox.CustomSharedMemory
agentEnabled := proxmox.CustomBool(true)
bootDisk := "scsi0"
bootOrder := "c"
@ -738,7 +831,11 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
tabletDeviceEnabled := proxmox.CustomBool(true)
body := &proxmox.VirtualEnvironmentVMCreateRequestBody{
Agent: &proxmox.CustomAgent{Enabled: &agentEnabled},
Agent: &proxmox.CustomAgent{
Enabled: &agentEnabled,
TrimClonedDisks: &agentTrim,
Type: &agentType,
},
BootDisk: &bootDisk,
BootOrder: &bootOrder,
CloudInitConfig: cloudInitConfig,
@ -758,10 +855,22 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
VMID: &vmID,
}
if cpuHotplugged > 0 {
body.VirtualCPUCount = &cpuHotplugged
}
if description != "" {
body.Description = &description
}
if name != "" {
body.Name = &name
}
if poolID != "" {
body.PoolID = &poolID
}
err = veClient.CreateVM(nodeName, body)
if err != nil {
@ -770,10 +879,10 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
d.SetId(strconv.Itoa(vmID))
return resourceVirtualEnvironmentVMImportDisks(d, m)
return resourceVirtualEnvironmentVMCreateImportedDisks(d, m)
}
func resourceVirtualEnvironmentVMImportDisks(d *schema.ResourceData, m interface{}) error {
func resourceVirtualEnvironmentVMCreateImportedDisks(d *schema.ResourceData, m interface{}) error {
config := m.(providerConfiguration)
veClient, err := config.GetVEClient()
@ -838,10 +947,6 @@ func resourceVirtualEnvironmentVMImportDisks(d *schema.ResourceData, m interface
// Execute the commands on the node and wait for the result.
// This is a highly experimental approach to disk imports and is not recommended by Proxmox.
if len(commands) > 0 {
for _, cmd := range commands {
log.Printf("[DEBUG] Node command: %s", cmd)
}
err = veClient.ExecuteNodeCommands(nodeName, commands)
if err != nil {
@ -849,6 +954,37 @@ func resourceVirtualEnvironmentVMImportDisks(d *schema.ResourceData, m interface
}
}
return resourceVirtualEnvironmentVMCreateStart(d, m)
}
func resourceVirtualEnvironmentVMCreateStart(d *schema.ResourceData, m interface{}) error {
config := m.(providerConfiguration)
veClient, err := config.GetVEClient()
if err != nil {
return err
}
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
vmID, err := strconv.Atoi(d.Id())
if err != nil {
return err
}
// Start the virtual machine and wait for it to reach a running state before continuing.
err = veClient.StartVM(nodeName, vmID)
if err != nil {
return err
}
err = veClient.WaitForState(nodeName, vmID, "running", 120, 5)
if err != nil {
return err
}
return resourceVirtualEnvironmentVMRead(d, m)
}
@ -893,6 +1029,25 @@ func resourceVirtualEnvironmentVMDelete(d *schema.ResourceData, m interface{}) e
return err
}
// Shut down the virtual machine before deleting it.
forceStop := proxmox.CustomBool(true)
shutdownTimeout := 300
err = veClient.ShutdownVM(nodeName, vmID, &proxmox.VirtualEnvironmentVMShutdownRequestBody{
ForceStop: &forceStop,
Timeout: &shutdownTimeout,
})
if err != nil {
return err
}
err = veClient.WaitForState(nodeName, vmID, "stopped", 30, 5)
if err != nil {
return err
}
err = veClient.DeleteVM(nodeName, vmID)
if err != nil {
@ -905,6 +1060,13 @@ func resourceVirtualEnvironmentVMDelete(d *schema.ResourceData, m interface{}) e
return err
}
// Wait for the state to become unavailable as that clearly indicates the destruction of the VM.
err = veClient.WaitForState(nodeName, vmID, "", 30, 2)
if err == nil {
return fmt.Errorf("Failed to delete VM \"%d\"", vmID)
}
d.SetId("")
return nil

View File

@ -31,12 +31,14 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMCDROM,
mkResourceVirtualEnvironmentVMCloudInit,
mkResourceVirtualEnvironmentVMCPU,
mkResourceVirtualEnvironmentVMDescription,
mkResourceVirtualEnvironmentVMDisk,
mkResourceVirtualEnvironmentVMKeyboardLayout,
mkResourceVirtualEnvironmentVMMemory,
mkResourceVirtualEnvironmentVMName,
mkResourceVirtualEnvironmentVMNetworkDevice,
mkResourceVirtualEnvironmentVMOSType,
mkResourceVirtualEnvironmentVMPoolID,
mkResourceVirtualEnvironmentVMVMID,
})
@ -44,26 +46,48 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMCDROM,
mkResourceVirtualEnvironmentVMCloudInit,
mkResourceVirtualEnvironmentVMCPU,
mkResourceVirtualEnvironmentVMDescription,
mkResourceVirtualEnvironmentVMDisk,
mkResourceVirtualEnvironmentVMKeyboardLayout,
mkResourceVirtualEnvironmentVMMemory,
mkResourceVirtualEnvironmentVMName,
mkResourceVirtualEnvironmentVMNetworkDevice,
mkResourceVirtualEnvironmentVMOSType,
mkResourceVirtualEnvironmentVMPoolID,
mkResourceVirtualEnvironmentVMVMID,
}, []schema.ValueType{
schema.TypeList,
schema.TypeList,
schema.TypeList,
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeString,
schema.TypeString,
schema.TypeInt,
})
agentSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMAgent)
testOptionalArguments(t, agentSchema, []string{
mkResourceVirtualEnvironmentVMAgentEnabled,
mkResourceVirtualEnvironmentVMAgentTrim,
mkResourceVirtualEnvironmentVMAgentType,
})
testSchemaValueTypes(t, agentSchema, []string{
mkResourceVirtualEnvironmentVMAgentEnabled,
mkResourceVirtualEnvironmentVMAgentTrim,
mkResourceVirtualEnvironmentVMAgentType,
}, []schema.ValueType{
schema.TypeBool,
schema.TypeBool,
schema.TypeString,
})
cdromSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMCDROM)
testOptionalArguments(t, cdromSchema, []string{
@ -179,15 +203,18 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
testOptionalArguments(t, cpuSchema, []string{
mkResourceVirtualEnvironmentVMCPUCores,
mkResourceVirtualEnvironmentVMCPUHotplugged,
mkResourceVirtualEnvironmentVMCPUSockets,
})
testSchemaValueTypes(t, cpuSchema, []string{
mkResourceVirtualEnvironmentVMCPUCores,
mkResourceVirtualEnvironmentVMCPUHotplugged,
mkResourceVirtualEnvironmentVMCPUSockets,
}, []schema.ValueType{
schema.TypeInt,
schema.TypeInt,
schema.TypeInt,
})
diskSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMDisk)

View File

@ -114,6 +114,10 @@ func getOSTypeValidator() schema.SchemaValidateFunc {
}, false)
}
func getQEMUAgentTypeValidator() schema.SchemaValidateFunc {
return validation.StringInSlice([]string{"isa", "virtio"}, false)
}
func getVLANIDValidator() schema.SchemaValidateFunc {
return func(i interface{}, k string) (ws []string, es []error) {
min := 1