mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 02:31:10 +00:00
move `types` back from `internal` to `proxmox` and adjust a few other types, to make sure `proxmox` package is not dependent on anything else, and therefore can be extracted to a separate repo (#423)
5839 lines
196 KiB
Go
5839 lines
196 KiB
Go
/*
|
||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||
*/
|
||
|
||
package resource
|
||
|
||
import (
|
||
"context"
|
||
"encoding/base64"
|
||
"errors"
|
||
"fmt"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
"unicode"
|
||
|
||
"github.com/google/go-cmp/cmp"
|
||
"github.com/google/uuid"
|
||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
|
||
|
||
"github.com/bpg/terraform-provider-proxmox/proxmox/cluster"
|
||
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
|
||
"github.com/bpg/terraform-provider-proxmox/proxmox/pools"
|
||
types2 "github.com/bpg/terraform-provider-proxmox/proxmox/types"
|
||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf"
|
||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator"
|
||
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure"
|
||
)
|
||
|
||
const (
|
||
dvResourceVirtualEnvironmentVMRebootAfterCreation = false
|
||
dvResourceVirtualEnvironmentVMOnBoot = true
|
||
dvResourceVirtualEnvironmentVMACPI = true
|
||
dvResourceVirtualEnvironmentVMAgentEnabled = false
|
||
dvResourceVirtualEnvironmentVMAgentTimeout = "15m"
|
||
dvResourceVirtualEnvironmentVMAgentTrim = false
|
||
dvResourceVirtualEnvironmentVMAgentType = "virtio"
|
||
dvResourceVirtualEnvironmentVMAudioDeviceDevice = "intel-hda"
|
||
dvResourceVirtualEnvironmentVMAudioDeviceDriver = "spice"
|
||
dvResourceVirtualEnvironmentVMAudioDeviceEnabled = true
|
||
dvResourceVirtualEnvironmentVMBIOS = "seabios"
|
||
dvResourceVirtualEnvironmentVMCDROMEnabled = false
|
||
dvResourceVirtualEnvironmentVMCDROMFileID = ""
|
||
dvResourceVirtualEnvironmentVMCDROMInterface = "ide3"
|
||
dvResourceVirtualEnvironmentVMCloneDatastoreID = ""
|
||
dvResourceVirtualEnvironmentVMCloneNodeName = ""
|
||
dvResourceVirtualEnvironmentVMCloneFull = true
|
||
dvResourceVirtualEnvironmentVMCloneRetries = 1
|
||
dvResourceVirtualEnvironmentVMCPUArchitecture = "x86_64"
|
||
dvResourceVirtualEnvironmentVMCPUCores = 1
|
||
dvResourceVirtualEnvironmentVMCPUHotplugged = 0
|
||
dvResourceVirtualEnvironmentVMCPUNUMA = false
|
||
dvResourceVirtualEnvironmentVMCPUSockets = 1
|
||
dvResourceVirtualEnvironmentVMCPUType = "qemu64"
|
||
dvResourceVirtualEnvironmentVMCPUUnits = 1024
|
||
dvResourceVirtualEnvironmentVMDescription = ""
|
||
dvResourceVirtualEnvironmentVMDiskInterface = "scsi0"
|
||
dvResourceVirtualEnvironmentVMDiskDatastoreID = "local-lvm"
|
||
dvResourceVirtualEnvironmentVMDiskFileFormat = "qcow2"
|
||
dvResourceVirtualEnvironmentVMDiskFileID = ""
|
||
dvResourceVirtualEnvironmentVMDiskSize = 8
|
||
dvResourceVirtualEnvironmentVMDiskIOThread = false
|
||
dvResourceVirtualEnvironmentVMDiskSSD = false
|
||
dvResourceVirtualEnvironmentVMDiskDiscard = ""
|
||
dvResourceVirtualEnvironmentVMDiskCache = "none"
|
||
dvResourceVirtualEnvironmentVMDiskSpeedRead = 0
|
||
dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable = 0
|
||
dvResourceVirtualEnvironmentVMDiskSpeedWrite = 0
|
||
dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = 0
|
||
dvResourceVirtualEnvironmentVMEFIDiskDatastoreID = "local-lvm"
|
||
dvResourceVirtualEnvironmentVMEFIDiskFileFormat = "qcow2"
|
||
dvResourceVirtualEnvironmentVMEFIDiskType = "2m"
|
||
dvResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys = false
|
||
dvResourceVirtualEnvironmentVMInitializationDatastoreID = "local-lvm"
|
||
dvResourceVirtualEnvironmentVMInitializationInterface = ""
|
||
dvResourceVirtualEnvironmentVMInitializationDNSDomain = ""
|
||
dvResourceVirtualEnvironmentVMInitializationDNSServer = ""
|
||
dvResourceVirtualEnvironmentVMInitializationIPConfigIPv4Address = ""
|
||
dvResourceVirtualEnvironmentVMInitializationIPConfigIPv4Gateway = ""
|
||
dvResourceVirtualEnvironmentVMInitializationIPConfigIPv6Address = ""
|
||
dvResourceVirtualEnvironmentVMInitializationIPConfigIPv6Gateway = ""
|
||
dvResourceVirtualEnvironmentVMInitializationUserAccountPassword = ""
|
||
dvResourceVirtualEnvironmentVMInitializationUserDataFileID = ""
|
||
dvResourceVirtualEnvironmentVMInitializationVendorDataFileID = ""
|
||
dvResourceVirtualEnvironmentVMInitializationNetworkDataFileID = ""
|
||
dvResourceVirtualEnvironmentVMInitializationMetaDataFileID = ""
|
||
dvResourceVirtualEnvironmentVMInitializationType = ""
|
||
dvResourceVirtualEnvironmentVMKeyboardLayout = "en-us"
|
||
dvResourceVirtualEnvironmentVMKVMArguments = ""
|
||
dvResourceVirtualEnvironmentVMMachineType = ""
|
||
dvResourceVirtualEnvironmentVMMemoryDedicated = 512
|
||
dvResourceVirtualEnvironmentVMMemoryFloating = 0
|
||
dvResourceVirtualEnvironmentVMMemoryShared = 0
|
||
dvResourceVirtualEnvironmentVMMigrate = false
|
||
dvResourceVirtualEnvironmentVMName = ""
|
||
dvResourceVirtualEnvironmentVMNetworkDeviceBridge = "vmbr0"
|
||
dvResourceVirtualEnvironmentVMNetworkDeviceEnabled = true
|
||
dvResourceVirtualEnvironmentVMNetworkDeviceFirewall = false
|
||
dvResourceVirtualEnvironmentVMNetworkDeviceModel = "virtio"
|
||
dvResourceVirtualEnvironmentVMNetworkDeviceRateLimit = 0
|
||
dvResourceVirtualEnvironmentVMNetworkDeviceVLANID = 0
|
||
dvResourceVirtualEnvironmentVMNetworkDeviceMTU = 0
|
||
dvResourceVirtualEnvironmentVMOperatingSystemType = "other"
|
||
dvResourceVirtualEnvironmentVMPoolID = ""
|
||
dvResourceVirtualEnvironmentVMSerialDeviceDevice = "socket"
|
||
dvResourceVirtualEnvironmentVMSMBIOSFamily = ""
|
||
dvResourceVirtualEnvironmentVMSMBIOSManufacturer = ""
|
||
dvResourceVirtualEnvironmentVMSMBIOSProduct = ""
|
||
dvResourceVirtualEnvironmentVMSMBIOSSKU = ""
|
||
dvResourceVirtualEnvironmentVMSMBIOSSerial = ""
|
||
dvResourceVirtualEnvironmentVMSMBIOSVersion = ""
|
||
dvResourceVirtualEnvironmentVMStarted = true
|
||
dvResourceVirtualEnvironmentVMStartupOrder = -1
|
||
dvResourceVirtualEnvironmentVMStartupUpDelay = -1
|
||
dvResourceVirtualEnvironmentVMStartupDownDelay = -1
|
||
dvResourceVirtualEnvironmentVMTabletDevice = true
|
||
dvResourceVirtualEnvironmentVMTemplate = false
|
||
dvResourceVirtualEnvironmentVMTimeoutClone = 1800
|
||
dvResourceVirtualEnvironmentVMTimeoutMoveDisk = 1800
|
||
dvResourceVirtualEnvironmentVMTimeoutMigrate = 1800
|
||
dvResourceVirtualEnvironmentVMTimeoutReboot = 1800
|
||
dvResourceVirtualEnvironmentVMTimeoutShutdownVM = 1800
|
||
dvResourceVirtualEnvironmentVMTimeoutStartVM = 1800
|
||
dvResourceVirtualEnvironmentVMTimeoutStopVM = 300
|
||
dvResourceVirtualEnvironmentVMVGAEnabled = true
|
||
dvResourceVirtualEnvironmentVMVGAMemory = 16
|
||
dvResourceVirtualEnvironmentVMVGAType = "std"
|
||
dvResourceVirtualEnvironmentVMSCSIHardware = "virtio-scsi-pci"
|
||
|
||
maxResourceVirtualEnvironmentVMAudioDevices = 1
|
||
maxResourceVirtualEnvironmentVMNetworkDevices = 8
|
||
maxResourceVirtualEnvironmentVMSerialDevices = 4
|
||
maxResourceVirtualEnvironmentVMHostPCIDevices = 8
|
||
|
||
mkResourceVirtualEnvironmentVMRebootAfterCreation = "reboot"
|
||
mkResourceVirtualEnvironmentVMOnBoot = "on_boot"
|
||
mkResourceVirtualEnvironmentVMBootOrder = "boot_order"
|
||
mkResourceVirtualEnvironmentVMACPI = "acpi"
|
||
mkResourceVirtualEnvironmentVMAgent = "agent"
|
||
mkResourceVirtualEnvironmentVMAgentEnabled = "enabled"
|
||
mkResourceVirtualEnvironmentVMAgentTimeout = "timeout"
|
||
mkResourceVirtualEnvironmentVMAgentTrim = "trim"
|
||
mkResourceVirtualEnvironmentVMAgentType = "type"
|
||
mkResourceVirtualEnvironmentVMAudioDevice = "audio_device"
|
||
mkResourceVirtualEnvironmentVMAudioDeviceDevice = "device"
|
||
mkResourceVirtualEnvironmentVMAudioDeviceDriver = "driver"
|
||
mkResourceVirtualEnvironmentVMAudioDeviceEnabled = "enabled"
|
||
mkResourceVirtualEnvironmentVMBIOS = "bios"
|
||
mkResourceVirtualEnvironmentVMCDROM = "cdrom"
|
||
mkResourceVirtualEnvironmentVMCDROMEnabled = "enabled"
|
||
mkResourceVirtualEnvironmentVMCDROMFileID = "file_id"
|
||
mkResourceVirtualEnvironmentVMCDROMInterface = "interface"
|
||
mkResourceVirtualEnvironmentVMClone = "clone"
|
||
mkResourceVirtualEnvironmentVMCloneRetries = "retries"
|
||
mkResourceVirtualEnvironmentVMCloneDatastoreID = "datastore_id"
|
||
mkResourceVirtualEnvironmentVMCloneNodeName = "node_name"
|
||
mkResourceVirtualEnvironmentVMCloneVMID = "vm_id"
|
||
mkResourceVirtualEnvironmentVMCloneFull = "full"
|
||
mkResourceVirtualEnvironmentVMCPU = "cpu"
|
||
mkResourceVirtualEnvironmentVMCPUArchitecture = "architecture"
|
||
mkResourceVirtualEnvironmentVMCPUCores = "cores"
|
||
mkResourceVirtualEnvironmentVMCPUFlags = "flags"
|
||
mkResourceVirtualEnvironmentVMCPUHotplugged = "hotplugged"
|
||
mkResourceVirtualEnvironmentVMCPUNUMA = "numa"
|
||
mkResourceVirtualEnvironmentVMCPUSockets = "sockets"
|
||
mkResourceVirtualEnvironmentVMCPUType = "type"
|
||
mkResourceVirtualEnvironmentVMCPUUnits = "units"
|
||
mkResourceVirtualEnvironmentVMDescription = "description"
|
||
mkResourceVirtualEnvironmentVMDisk = "disk"
|
||
mkResourceVirtualEnvironmentVMDiskInterface = "interface"
|
||
mkResourceVirtualEnvironmentVMDiskDatastoreID = "datastore_id"
|
||
mkResourceVirtualEnvironmentVMDiskFileFormat = "file_format"
|
||
mkResourceVirtualEnvironmentVMDiskFileID = "file_id"
|
||
mkResourceVirtualEnvironmentVMDiskSize = "size"
|
||
mkResourceVirtualEnvironmentVMDiskIOThread = "iothread"
|
||
mkResourceVirtualEnvironmentVMDiskSSD = "ssd"
|
||
mkResourceVirtualEnvironmentVMDiskDiscard = "discard"
|
||
mkResourceVirtualEnvironmentVMDiskCache = "cache"
|
||
mkResourceVirtualEnvironmentVMDiskSpeed = "speed"
|
||
mkResourceVirtualEnvironmentVMDiskSpeedRead = "read"
|
||
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable"
|
||
mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write"
|
||
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable"
|
||
mkResourceVirtualEnvironmentVMEFIDisk = "efi_disk"
|
||
mkResourceVirtualEnvironmentVMEFIDiskDatastoreID = "datastore_id"
|
||
mkResourceVirtualEnvironmentVMEFIDiskFileFormat = "file_format"
|
||
mkResourceVirtualEnvironmentVMEFIDiskType = "type"
|
||
mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys = "pre_enrolled_keys"
|
||
mkResourceVirtualEnvironmentVMHostPCI = "hostpci"
|
||
mkResourceVirtualEnvironmentVMHostPCIDevice = "device"
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceID = "id"
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceMapping = "mapping"
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceMDev = "mdev"
|
||
mkResourceVirtualEnvironmentVMHostPCIDevicePCIE = "pcie"
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR = "rombar"
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile = "rom_file"
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA = "xvga"
|
||
mkResourceVirtualEnvironmentVMInitialization = "initialization"
|
||
mkResourceVirtualEnvironmentVMInitializationDatastoreID = "datastore_id"
|
||
mkResourceVirtualEnvironmentVMInitializationInterface = "interface"
|
||
mkResourceVirtualEnvironmentVMInitializationDNS = "dns"
|
||
mkResourceVirtualEnvironmentVMInitializationDNSDomain = "domain"
|
||
mkResourceVirtualEnvironmentVMInitializationDNSServer = "server"
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfig = "ip_config"
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4 = "ipv4"
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Address = "address"
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Gateway = "gateway"
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6 = "ipv6"
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Address = "address"
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Gateway = "gateway"
|
||
mkResourceVirtualEnvironmentVMInitializationType = "type"
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccount = "user_account"
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccountKeys = "keys"
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccountPassword = "password"
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccountUsername = "username"
|
||
mkResourceVirtualEnvironmentVMInitializationUserDataFileID = "user_data_file_id"
|
||
mkResourceVirtualEnvironmentVMInitializationVendorDataFileID = "vendor_data_file_id"
|
||
mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID = "network_data_file_id"
|
||
mkResourceVirtualEnvironmentVMInitializationMetaDataFileID = "meta_data_file_id"
|
||
mkResourceVirtualEnvironmentVMIPv4Addresses = "ipv4_addresses"
|
||
mkResourceVirtualEnvironmentVMIPv6Addresses = "ipv6_addresses"
|
||
mkResourceVirtualEnvironmentVMKeyboardLayout = "keyboard_layout"
|
||
mkResourceVirtualEnvironmentVMKVMArguments = "kvm_arguments"
|
||
mkResourceVirtualEnvironmentVMMachine = "machine"
|
||
mkResourceVirtualEnvironmentVMMACAddresses = "mac_addresses"
|
||
mkResourceVirtualEnvironmentVMMemory = "memory"
|
||
mkResourceVirtualEnvironmentVMMemoryDedicated = "dedicated"
|
||
mkResourceVirtualEnvironmentVMMemoryFloating = "floating"
|
||
mkResourceVirtualEnvironmentVMMemoryShared = "shared"
|
||
mkResourceVirtualEnvironmentVMMigrate = "migrate"
|
||
mkResourceVirtualEnvironmentVMName = "name"
|
||
mkResourceVirtualEnvironmentVMNetworkDevice = "network_device"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceBridge = "bridge"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceEnabled = "enabled"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceFirewall = "firewall"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress = "mac_address"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceModel = "model"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit = "rate_limit"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceVLANID = "vlan_id"
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceMTU = "mtu"
|
||
mkResourceVirtualEnvironmentVMNetworkInterfaceNames = "network_interface_names"
|
||
mkResourceVirtualEnvironmentVMNodeName = "node_name"
|
||
mkResourceVirtualEnvironmentVMOperatingSystem = "operating_system"
|
||
mkResourceVirtualEnvironmentVMOperatingSystemType = "type"
|
||
mkResourceVirtualEnvironmentVMPoolID = "pool_id"
|
||
mkResourceVirtualEnvironmentVMSerialDevice = "serial_device"
|
||
mkResourceVirtualEnvironmentVMSerialDeviceDevice = "device"
|
||
mkResourceVirtualEnvironmentVMSMBIOS = "smbios"
|
||
mkResourceVirtualEnvironmentVMSMBIOSFamily = "family"
|
||
mkResourceVirtualEnvironmentVMSMBIOSManufacturer = "manufacturer"
|
||
mkResourceVirtualEnvironmentVMSMBIOSProduct = "product"
|
||
mkResourceVirtualEnvironmentVMSMBIOSSKU = "sku"
|
||
mkResourceVirtualEnvironmentVMSMBIOSSerial = "serial"
|
||
mkResourceVirtualEnvironmentVMSMBIOSUUID = "uuid"
|
||
mkResourceVirtualEnvironmentVMSMBIOSVersion = "version"
|
||
mkResourceVirtualEnvironmentVMStarted = "started"
|
||
mkResourceVirtualEnvironmentVMStartup = "startup"
|
||
mkResourceVirtualEnvironmentVMStartupOrder = "order"
|
||
mkResourceVirtualEnvironmentVMStartupUpDelay = "up_delay"
|
||
mkResourceVirtualEnvironmentVMStartupDownDelay = "down_delay"
|
||
mkResourceVirtualEnvironmentVMTabletDevice = "tablet_device"
|
||
mkResourceVirtualEnvironmentVMTags = "tags"
|
||
mkResourceVirtualEnvironmentVMTemplate = "template"
|
||
mkResourceVirtualEnvironmentVMTimeoutClone = "timeout_clone"
|
||
mkResourceVirtualEnvironmentVMTimeoutMoveDisk = "timeout_move_disk"
|
||
mkResourceVirtualEnvironmentVMTimeoutMigrate = "timeout_migrate"
|
||
mkResourceVirtualEnvironmentVMTimeoutReboot = "timeout_reboot"
|
||
mkResourceVirtualEnvironmentVMTimeoutShutdownVM = "timeout_shutdown_vm"
|
||
mkResourceVirtualEnvironmentVMTimeoutStartVM = "timeout_start_vm"
|
||
mkResourceVirtualEnvironmentVMTimeoutStopVM = "timeout_stop_vm"
|
||
mkResourceVirtualEnvironmentVMVGA = "vga"
|
||
mkResourceVirtualEnvironmentVMVGAEnabled = "enabled"
|
||
mkResourceVirtualEnvironmentVMVGAMemory = "memory"
|
||
mkResourceVirtualEnvironmentVMVGAType = "type"
|
||
mkResourceVirtualEnvironmentVMVMID = "vm_id"
|
||
mkResourceVirtualEnvironmentVMSCSIHardware = "scsi_hardware"
|
||
|
||
vmCreateTimeoutSeconds = 10
|
||
)
|
||
|
||
// VM returns a resource that manages VMs.
|
||
func VM() *schema.Resource {
|
||
return &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMRebootAfterCreation: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to reboot vm after creation",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMRebootAfterCreation,
|
||
},
|
||
mkResourceVirtualEnvironmentVMOnBoot: {
|
||
Type: schema.TypeBool,
|
||
Description: "Start VM on Node boot",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMOnBoot,
|
||
},
|
||
mkResourceVirtualEnvironmentVMBootOrder: {
|
||
Type: schema.TypeList,
|
||
Description: "The guest will attempt to boot from devices in the order they appear here",
|
||
Optional: true,
|
||
Elem: &schema.Schema{Type: schema.TypeString},
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
},
|
||
mkResourceVirtualEnvironmentVMACPI: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable ACPI",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMACPI,
|
||
},
|
||
mkResourceVirtualEnvironmentVMAgent: {
|
||
Type: schema.TypeList,
|
||
Description: "The QEMU agent configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMAgentEnabled: dvResourceVirtualEnvironmentVMAgentEnabled,
|
||
mkResourceVirtualEnvironmentVMAgentTimeout: dvResourceVirtualEnvironmentVMAgentTimeout,
|
||
mkResourceVirtualEnvironmentVMAgentTrim: dvResourceVirtualEnvironmentVMAgentTrim,
|
||
mkResourceVirtualEnvironmentVMAgentType: dvResourceVirtualEnvironmentVMAgentType,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMAgentEnabled: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable the QEMU agent",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMAgentEnabled,
|
||
},
|
||
mkResourceVirtualEnvironmentVMAgentTimeout: {
|
||
Type: schema.TypeString,
|
||
Description: "The maximum amount of time to wait for data from the QEMU agent to become available",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMAgentTimeout,
|
||
ValidateDiagFunc: validator.Timeout(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMAgentTrim: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable the FSTRIM feature in the QEMU agent",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMAgentTrim,
|
||
},
|
||
mkResourceVirtualEnvironmentVMAgentType: {
|
||
Type: schema.TypeString,
|
||
Description: "The QEMU agent interface type",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMAgentType,
|
||
ValidateDiagFunc: validator.QEMUAgentType(),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMKVMArguments: {
|
||
Type: schema.TypeString,
|
||
Description: "The args implementation",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMKVMArguments,
|
||
},
|
||
mkResourceVirtualEnvironmentVMAudioDevice: {
|
||
Type: schema.TypeList,
|
||
Description: "The audio devices",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMAudioDeviceDevice: {
|
||
Type: schema.TypeString,
|
||
Description: "The device",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMAudioDeviceDevice,
|
||
ValidateDiagFunc: vmGetAudioDeviceValidator(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMAudioDeviceDriver: {
|
||
Type: schema.TypeString,
|
||
Description: "The driver",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMAudioDeviceDriver,
|
||
ValidateDiagFunc: vmGetAudioDriverValidator(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMAudioDeviceEnabled: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable the audio device",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMAudioDeviceEnabled,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: maxResourceVirtualEnvironmentVMAudioDevices,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMBIOS: {
|
||
Type: schema.TypeString,
|
||
Description: "The BIOS implementation",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMBIOS,
|
||
ValidateDiagFunc: validator.BIOS(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCDROM: {
|
||
Type: schema.TypeList,
|
||
Description: "The CDROM drive",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMCDROMEnabled: dvResourceVirtualEnvironmentVMCDROMEnabled,
|
||
mkResourceVirtualEnvironmentVMCDROMFileID: dvResourceVirtualEnvironmentVMCDROMFileID,
|
||
mkResourceVirtualEnvironmentVMCDROMInterface: dvResourceVirtualEnvironmentVMCDROMInterface,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMCDROMEnabled: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable the CDROM drive",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCDROMEnabled,
|
||
},
|
||
mkResourceVirtualEnvironmentVMCDROMFileID: {
|
||
Type: schema.TypeString,
|
||
Description: "The file id",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCDROMFileID,
|
||
ValidateDiagFunc: validator.FileID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCDROMInterface: {
|
||
Type: schema.TypeString,
|
||
Description: "The CDROM interface",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCDROMInterface,
|
||
ValidateDiagFunc: validator.IDEInterface(),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMClone: {
|
||
Type: schema.TypeList,
|
||
Description: "The cloning configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMCloneRetries: {
|
||
Type: schema.TypeInt,
|
||
Description: "The number of Retries to create a clone",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMCloneRetries,
|
||
},
|
||
mkResourceVirtualEnvironmentVMCloneDatastoreID: {
|
||
Type: schema.TypeString,
|
||
Description: "The ID of the target datastore",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMCloneDatastoreID,
|
||
},
|
||
mkResourceVirtualEnvironmentVMCloneNodeName: {
|
||
Type: schema.TypeString,
|
||
Description: "The name of the source node",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMCloneNodeName,
|
||
},
|
||
mkResourceVirtualEnvironmentVMCloneVMID: {
|
||
Type: schema.TypeInt,
|
||
Description: "The ID of the source VM",
|
||
Required: true,
|
||
ForceNew: true,
|
||
ValidateDiagFunc: validator.VMID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCloneFull: {
|
||
Type: schema.TypeBool,
|
||
Description: "The Clone Type, create a Full Clone (true) or a linked Clone (false)",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMCloneFull,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPU: {
|
||
Type: schema.TypeList,
|
||
Description: "The CPU allocation",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMCPUArchitecture: dvResourceVirtualEnvironmentVMCPUArchitecture,
|
||
mkResourceVirtualEnvironmentVMCPUCores: dvResourceVirtualEnvironmentVMCPUCores,
|
||
mkResourceVirtualEnvironmentVMCPUFlags: []interface{}{},
|
||
mkResourceVirtualEnvironmentVMCPUNUMA: dvResourceVirtualEnvironmentVMCPUNUMA,
|
||
mkResourceVirtualEnvironmentVMCPUHotplugged: dvResourceVirtualEnvironmentVMCPUHotplugged,
|
||
mkResourceVirtualEnvironmentVMCPUSockets: dvResourceVirtualEnvironmentVMCPUSockets,
|
||
mkResourceVirtualEnvironmentVMCPUType: dvResourceVirtualEnvironmentVMCPUType,
|
||
mkResourceVirtualEnvironmentVMCPUUnits: dvResourceVirtualEnvironmentVMCPUUnits,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMCPUArchitecture: {
|
||
Type: schema.TypeString,
|
||
Description: "The CPU architecture",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCPUArchitecture,
|
||
ValidateDiagFunc: vmGetCPUArchitectureValidator(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPUCores: {
|
||
Type: schema.TypeInt,
|
||
Description: "The number of CPU cores",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCPUCores,
|
||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(1, 2304)),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPUFlags: {
|
||
Type: schema.TypeList,
|
||
Description: "The CPU flags",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Schema{Type: schema.TypeString},
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPUHotplugged: {
|
||
Type: schema.TypeInt,
|
||
Description: "The number of hotplugged vCPUs",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCPUHotplugged,
|
||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(0, 2304)),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPUNUMA: {
|
||
Type: schema.TypeBool,
|
||
Description: "Enable/disable NUMA.",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCPUNUMA,
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPUSockets: {
|
||
Type: schema.TypeInt,
|
||
Description: "The number of CPU sockets",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCPUSockets,
|
||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntBetween(1, 16)),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPUType: {
|
||
Type: schema.TypeString,
|
||
Description: "The emulated CPU type",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCPUType,
|
||
ValidateDiagFunc: validator.CPUType(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMCPUUnits: {
|
||
Type: schema.TypeInt,
|
||
Description: "The CPU units",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMCPUUnits,
|
||
ValidateDiagFunc: validation.ToDiagFunc(
|
||
validation.IntBetween(2, 262144),
|
||
),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDescription: {
|
||
Type: schema.TypeString,
|
||
Description: "The description",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDescription,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDisk: {
|
||
Type: schema.TypeList,
|
||
Description: "The disk devices",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMDiskDatastoreID: dvResourceVirtualEnvironmentVMDiskDatastoreID,
|
||
mkResourceVirtualEnvironmentVMDiskFileID: dvResourceVirtualEnvironmentVMDiskFileID,
|
||
mkResourceVirtualEnvironmentVMDiskInterface: dvResourceVirtualEnvironmentVMDiskInterface,
|
||
mkResourceVirtualEnvironmentVMDiskSize: dvResourceVirtualEnvironmentVMDiskSize,
|
||
mkResourceVirtualEnvironmentVMDiskIOThread: dvResourceVirtualEnvironmentVMDiskIOThread,
|
||
mkResourceVirtualEnvironmentVMDiskSSD: dvResourceVirtualEnvironmentVMDiskSSD,
|
||
mkResourceVirtualEnvironmentVMDiskDiscard: dvResourceVirtualEnvironmentVMDiskDiscard,
|
||
mkResourceVirtualEnvironmentVMDiskCache: dvResourceVirtualEnvironmentVMDiskCache,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMDiskInterface: {
|
||
Type: schema.TypeString,
|
||
Description: "The datastore name",
|
||
Required: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskDatastoreID: {
|
||
Type: schema.TypeString,
|
||
Description: "The datastore id",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskDatastoreID,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskFileFormat: {
|
||
Type: schema.TypeString,
|
||
Description: "The file format",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Computed: true,
|
||
ValidateDiagFunc: validator.FileFormat(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskFileID: {
|
||
Type: schema.TypeString,
|
||
Description: "The file id for a disk image",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskFileID,
|
||
ValidateDiagFunc: validator.FileID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskSize: {
|
||
Type: schema.TypeInt,
|
||
Description: "The disk size in gigabytes",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskSize,
|
||
ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)),
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskIOThread: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to use iothreads for this disk drive",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskIOThread,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskSSD: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to use ssd for this disk drive",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskSSD,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskDiscard: {
|
||
Type: schema.TypeString,
|
||
Description: "Whether to pass discard/trim requests to the underlying storage.",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskDiscard,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskCache: {
|
||
Type: schema.TypeString,
|
||
Description: "The drive’s cache mode",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskCache,
|
||
ValidateDiagFunc: validation.ToDiagFunc(
|
||
validation.StringInSlice([]string{
|
||
"none",
|
||
"writethrough",
|
||
"writeback",
|
||
"unsafe",
|
||
"directsync",
|
||
}, false),
|
||
),
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskSpeed: {
|
||
Type: schema.TypeList,
|
||
Description: "The speed limits",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMDiskSpeedRead: dvResourceVirtualEnvironmentVMDiskSpeedRead,
|
||
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable: dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable,
|
||
mkResourceVirtualEnvironmentVMDiskSpeedWrite: dvResourceVirtualEnvironmentVMDiskSpeedWrite,
|
||
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable: dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMDiskSpeedRead: {
|
||
Type: schema.TypeInt,
|
||
Description: "The maximum read speed in megabytes per second",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskSpeedRead,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable: {
|
||
Type: schema.TypeInt,
|
||
Description: "The maximum burstable read speed in megabytes per second",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskSpeedWrite: {
|
||
Type: schema.TypeInt,
|
||
Description: "The maximum write speed in megabytes per second",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskSpeedWrite,
|
||
},
|
||
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable: {
|
||
Type: schema.TypeInt,
|
||
Description: "The maximum burstable write speed in megabytes per second",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 14,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMEFIDisk: {
|
||
Type: schema.TypeList,
|
||
Description: "The efidisk device",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMEFIDiskDatastoreID: {
|
||
Type: schema.TypeString,
|
||
Description: "The datastore id",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMEFIDiskDatastoreID,
|
||
},
|
||
mkResourceVirtualEnvironmentVMEFIDiskFileFormat: {
|
||
Type: schema.TypeString,
|
||
Description: "The file format",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Computed: true,
|
||
ValidateDiagFunc: validator.FileFormat(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMEFIDiskType: {
|
||
Type: schema.TypeString,
|
||
Description: "Size and type of the OVMF EFI disk",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMEFIDiskType,
|
||
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{
|
||
"2m",
|
||
"4m",
|
||
}, true)),
|
||
},
|
||
mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys: {
|
||
Type: schema.TypeBool,
|
||
Description: "Use an EFI vars template with distribution-specific and Microsoft Standard " +
|
||
"keys enrolled, if used with efi type=`4m`.",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitialization: {
|
||
Type: schema.TypeList,
|
||
Description: "The cloud-init configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMInitializationDatastoreID: {
|
||
Type: schema.TypeString,
|
||
Description: "The datastore id",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationDatastoreID,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationInterface: {
|
||
Type: schema.TypeString,
|
||
Description: "The IDE interface on which the CloudInit drive will be added",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationInterface,
|
||
ValidateDiagFunc: validator.IDEInterface(),
|
||
DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool {
|
||
return newValue == ""
|
||
},
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationDNS: {
|
||
Type: schema.TypeList,
|
||
Description: "The DNS configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMInitializationDNSDomain: {
|
||
Type: schema.TypeString,
|
||
Description: "The DNS search domain",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationDNSDomain,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationDNSServer: {
|
||
Type: schema.TypeString,
|
||
Description: "The DNS server",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationDNSServer,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfig: {
|
||
Type: schema.TypeList,
|
||
Description: "The IP configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4: {
|
||
Type: schema.TypeList,
|
||
Description: "The IPv4 configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Address: {
|
||
Type: schema.TypeString,
|
||
Description: "The IPv4 address",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationIPConfigIPv4Address,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Gateway: {
|
||
Type: schema.TypeString,
|
||
Description: "The IPv4 gateway",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationIPConfigIPv4Gateway,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6: {
|
||
Type: schema.TypeList,
|
||
Description: "The IPv6 configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Address: {
|
||
Type: schema.TypeString,
|
||
Description: "The IPv6 address",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationIPConfigIPv6Address,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Gateway: {
|
||
Type: schema.TypeString,
|
||
Description: "The IPv6 gateway",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationIPConfigIPv6Gateway,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 8,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccount: {
|
||
Type: schema.TypeList,
|
||
Description: "The user account configuration",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccountKeys: {
|
||
Type: schema.TypeList,
|
||
Description: "The SSH keys",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Elem: &schema.Schema{Type: schema.TypeString},
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccountPassword: {
|
||
Type: schema.TypeString,
|
||
Description: "The SSH password",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Sensitive: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationUserAccountPassword,
|
||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||
return len(old) > 0 &&
|
||
strings.ReplaceAll(old, "*", "") == ""
|
||
},
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationUserAccountUsername: {
|
||
Type: schema.TypeString,
|
||
Description: "The SSH username",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationUserDataFileID: {
|
||
Type: schema.TypeString,
|
||
Description: "The ID of a file containing custom user data",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationUserDataFileID,
|
||
ValidateDiagFunc: validator.FileID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationVendorDataFileID: {
|
||
Type: schema.TypeString,
|
||
Description: "The ID of a file containing vendor data",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationVendorDataFileID,
|
||
ValidateDiagFunc: validator.FileID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID: {
|
||
Type: schema.TypeString,
|
||
Description: "The ID of a file containing network config",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationNetworkDataFileID,
|
||
ValidateDiagFunc: validator.FileID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationMetaDataFileID: {
|
||
Type: schema.TypeString,
|
||
Description: "The ID of a file containing meta data config",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationMetaDataFileID,
|
||
ValidateDiagFunc: validator.FileID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMInitializationType: {
|
||
Type: schema.TypeString,
|
||
Description: "The cloud-init configuration format",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMInitializationType,
|
||
ValidateDiagFunc: validator.CloudInitType(),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMIPv4Addresses: {
|
||
Type: schema.TypeList,
|
||
Description: "The IPv4 addresses published by the QEMU agent",
|
||
Computed: true,
|
||
Elem: &schema.Schema{
|
||
Type: schema.TypeList,
|
||
Elem: &schema.Schema{Type: schema.TypeString},
|
||
},
|
||
},
|
||
mkResourceVirtualEnvironmentVMIPv6Addresses: {
|
||
Type: schema.TypeList,
|
||
Description: "The IPv6 addresses published by the QEMU agent",
|
||
Computed: true,
|
||
Elem: &schema.Schema{
|
||
Type: schema.TypeList,
|
||
Elem: &schema.Schema{Type: schema.TypeString},
|
||
},
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCI: {
|
||
Type: schema.TypeList,
|
||
Description: "The Host PCI devices mapped to the VM",
|
||
Optional: true,
|
||
ForceNew: false,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMHostPCIDevice: {
|
||
Type: schema.TypeString,
|
||
Description: "The PCI device name for Proxmox, in form of 'hostpciX' where X is a sequential number from 0 to 3",
|
||
Required: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceID: {
|
||
Type: schema.TypeString,
|
||
Description: "The PCI ID of the device, for example 0000:00:1f.0 (or 0000:00:1f.0;0000:00:1f.1 for multiple " +
|
||
"device functions, or 0000:00:1f for all functions). Use either this or mapping.",
|
||
Optional: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceMapping: {
|
||
Type: schema.TypeString,
|
||
Description: "The resource mapping name of the device, for example gpu. Use either this or id.",
|
||
Optional: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceMDev: {
|
||
Type: schema.TypeString,
|
||
Description: "The the mediated device to use",
|
||
Optional: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCIDevicePCIE: {
|
||
Type: schema.TypeBool,
|
||
Description: "Tells Proxmox VE to use a PCIe or PCI port. Some guests/device combination require PCIe rather " +
|
||
"than PCI. PCIe is only available for q35 machine types.",
|
||
Optional: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR: {
|
||
Type: schema.TypeBool,
|
||
Description: "Makes the firmware ROM visible for the guest. Default is true",
|
||
Optional: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile: {
|
||
Type: schema.TypeString,
|
||
Description: "A path to a ROM file for the device to use. This is a relative path under /usr/share/kvm/",
|
||
Optional: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA: {
|
||
Type: schema.TypeBool,
|
||
Description: "Marks the PCI(e) device as the primary GPU of the VM. With this enabled the vga configuration argument will be ignored.",
|
||
Optional: true,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
mkResourceVirtualEnvironmentVMKeyboardLayout: {
|
||
Type: schema.TypeString,
|
||
Description: "The keyboard layout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMKeyboardLayout,
|
||
ValidateDiagFunc: validator.KeyboardLayout(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMMachine: {
|
||
Type: schema.TypeString,
|
||
Description: "The VM machine type, either default i440fx or q35",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMMachineType,
|
||
},
|
||
mkResourceVirtualEnvironmentVMMACAddresses: {
|
||
Type: schema.TypeList,
|
||
Description: "The MAC addresses for the network interfaces",
|
||
Computed: true,
|
||
Elem: &schema.Schema{Type: schema.TypeString},
|
||
},
|
||
mkResourceVirtualEnvironmentVMMemory: {
|
||
Type: schema.TypeList,
|
||
Description: "The memory allocation",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMMemoryDedicated: dvResourceVirtualEnvironmentVMMemoryDedicated,
|
||
mkResourceVirtualEnvironmentVMMemoryFloating: dvResourceVirtualEnvironmentVMMemoryFloating,
|
||
mkResourceVirtualEnvironmentVMMemoryShared: dvResourceVirtualEnvironmentVMMemoryShared,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMMemoryDedicated: {
|
||
Type: schema.TypeInt,
|
||
Description: "The dedicated memory in megabytes",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMMemoryDedicated,
|
||
ValidateDiagFunc: validation.ToDiagFunc(
|
||
validation.IntBetween(64, 268435456),
|
||
),
|
||
},
|
||
mkResourceVirtualEnvironmentVMMemoryFloating: {
|
||
Type: schema.TypeInt,
|
||
Description: "The floating memory in megabytes (balloon)",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMMemoryFloating,
|
||
ValidateDiagFunc: validation.ToDiagFunc(
|
||
validation.IntBetween(0, 268435456),
|
||
),
|
||
},
|
||
mkResourceVirtualEnvironmentVMMemoryShared: {
|
||
Type: schema.TypeInt,
|
||
Description: "The shared memory in megabytes",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMMemoryShared,
|
||
ValidateDiagFunc: validation.ToDiagFunc(
|
||
validation.IntBetween(0, 268435456),
|
||
),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMName: {
|
||
Type: schema.TypeString,
|
||
Description: "The name",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMName,
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDevice: {
|
||
Type: schema.TypeList,
|
||
Description: "The network devices",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return make([]interface{}, 1), nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceBridge: {
|
||
Type: schema.TypeString,
|
||
Description: "The bridge",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMNetworkDeviceBridge,
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceEnabled: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable the network device",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMNetworkDeviceEnabled,
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceFirewall: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether this interface's firewall rules should be used",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMNetworkDeviceFirewall,
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress: {
|
||
Type: schema.TypeString,
|
||
Description: "The MAC address",
|
||
Optional: true,
|
||
Computed: true,
|
||
ValidateDiagFunc: validator.MACAddress(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceModel: {
|
||
Type: schema.TypeString,
|
||
Description: "The model",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMNetworkDeviceModel,
|
||
ValidateDiagFunc: validator.NetworkDeviceModel(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit: {
|
||
Type: schema.TypeFloat,
|
||
Description: "The rate limit in megabytes per second",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMNetworkDeviceRateLimit,
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceVLANID: {
|
||
Type: schema.TypeInt,
|
||
Description: "The VLAN identifier",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMNetworkDeviceVLANID,
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkDeviceMTU: {
|
||
Type: schema.TypeInt,
|
||
Description: "Maximum transmission unit (MTU)",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMNetworkDeviceMTU,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: maxResourceVirtualEnvironmentVMNetworkDevices,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMNetworkInterfaceNames: {
|
||
Type: schema.TypeList,
|
||
Description: "The network interface names published by the QEMU agent",
|
||
Computed: true,
|
||
Elem: &schema.Schema{Type: schema.TypeString},
|
||
},
|
||
mkResourceVirtualEnvironmentVMNodeName: {
|
||
Type: schema.TypeString,
|
||
Description: "The node name",
|
||
Required: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMMigrate: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to migrate the VM on node change instead of re-creating it",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMMigrate,
|
||
},
|
||
mkResourceVirtualEnvironmentVMOperatingSystem: {
|
||
Type: schema.TypeList,
|
||
Description: "The operating system configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMOperatingSystemType: dvResourceVirtualEnvironmentVMOperatingSystemType,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMOperatingSystemType: {
|
||
Type: schema.TypeString,
|
||
Description: "The type",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMOperatingSystemType,
|
||
ValidateDiagFunc: vmGetOperatingSystemTypeValidator(),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMPoolID: {
|
||
Type: schema.TypeString,
|
||
Description: "The ID of the pool to assign the virtual machine to",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMPoolID,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSerialDevice: {
|
||
Type: schema.TypeList,
|
||
Description: "The serial devices",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMSerialDeviceDevice: dvResourceVirtualEnvironmentVMSerialDeviceDevice,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMSerialDeviceDevice: {
|
||
Type: schema.TypeString,
|
||
Description: "The device",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSerialDeviceDevice,
|
||
ValidateDiagFunc: vmGetSerialDeviceValidator(),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: maxResourceVirtualEnvironmentVMSerialDevices,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSMBIOS: {
|
||
Type: schema.TypeList,
|
||
Description: "Specifies SMBIOS (type1) settings for the VM",
|
||
Optional: true,
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMSMBIOSFamily: {
|
||
Type: schema.TypeString,
|
||
Description: "Sets SMBIOS family string",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSMBIOSFamily,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSMBIOSManufacturer: {
|
||
Type: schema.TypeString,
|
||
Description: "Sets SMBIOS manufacturer",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSMBIOSManufacturer,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSMBIOSProduct: {
|
||
Type: schema.TypeString,
|
||
Description: "Sets SMBIOS product ID",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSMBIOSProduct,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSMBIOSSerial: {
|
||
Type: schema.TypeString,
|
||
Description: "Sets SMBIOS serial number",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSMBIOSSerial,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSMBIOSSKU: {
|
||
Type: schema.TypeString,
|
||
Description: "Sets SMBIOS SKU",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSMBIOSSKU,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSMBIOSUUID: {
|
||
Type: schema.TypeString,
|
||
Description: "Sets SMBIOS UUID",
|
||
Optional: true,
|
||
Computed: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMSMBIOSVersion: {
|
||
Type: schema.TypeString,
|
||
Description: "Sets SMBIOS version",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSMBIOSVersion,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMStarted: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to start the virtual machine",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMStarted,
|
||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||
return d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)
|
||
},
|
||
},
|
||
mkResourceVirtualEnvironmentVMStartup: {
|
||
Type: schema.TypeList,
|
||
Description: "Defines startup and shutdown behavior of the VM",
|
||
Optional: true,
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMStartupOrder: {
|
||
Type: schema.TypeInt,
|
||
Description: "A non-negative number defining the general startup order",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMStartupOrder,
|
||
},
|
||
mkResourceVirtualEnvironmentVMStartupUpDelay: {
|
||
Type: schema.TypeInt,
|
||
Description: "A non-negative number defining the delay in seconds before the next VM is started",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMStartupUpDelay,
|
||
},
|
||
mkResourceVirtualEnvironmentVMStartupDownDelay: {
|
||
Type: schema.TypeInt,
|
||
Description: "A non-negative number defining the delay in seconds before the next VM is shut down",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMStartupDownDelay,
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTabletDevice: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable the USB tablet device",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTabletDevice,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTags: {
|
||
Type: schema.TypeList,
|
||
Description: "Tags of the virtual machine. This is only meta information.",
|
||
Optional: true,
|
||
Elem: &schema.Schema{
|
||
Type: schema.TypeString,
|
||
ValidateFunc: validation.StringIsNotEmpty,
|
||
},
|
||
DiffSuppressFunc: structure.SuppressIfListsAreEqualIgnoringOrder,
|
||
DiffSuppressOnRefresh: true,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTemplate: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to create a template",
|
||
Optional: true,
|
||
ForceNew: true,
|
||
Default: dvResourceVirtualEnvironmentVMTemplate,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTimeoutClone: {
|
||
Type: schema.TypeInt,
|
||
Description: "Clone VM timeout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTimeoutClone,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTimeoutMoveDisk: {
|
||
Type: schema.TypeInt,
|
||
Description: "MoveDisk timeout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTimeoutMoveDisk,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTimeoutMigrate: {
|
||
Type: schema.TypeInt,
|
||
Description: "Migrate VM timeout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTimeoutMigrate,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTimeoutReboot: {
|
||
Type: schema.TypeInt,
|
||
Description: "Reboot timeout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTimeoutReboot,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTimeoutShutdownVM: {
|
||
Type: schema.TypeInt,
|
||
Description: "Shutdown timeout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTimeoutShutdownVM,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTimeoutStartVM: {
|
||
Type: schema.TypeInt,
|
||
Description: "Start VM timeout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTimeoutStartVM,
|
||
},
|
||
mkResourceVirtualEnvironmentVMTimeoutStopVM: {
|
||
Type: schema.TypeInt,
|
||
Description: "Stop VM timeout",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMTimeoutStopVM,
|
||
},
|
||
mkResourceVirtualEnvironmentVMVGA: {
|
||
Type: schema.TypeList,
|
||
Description: "The VGA configuration",
|
||
Optional: true,
|
||
DefaultFunc: func() (interface{}, error) {
|
||
return []interface{}{
|
||
map[string]interface{}{
|
||
mkResourceVirtualEnvironmentVMVGAEnabled: dvResourceVirtualEnvironmentVMVGAEnabled,
|
||
mkResourceVirtualEnvironmentVMVGAMemory: dvResourceVirtualEnvironmentVMVGAMemory,
|
||
mkResourceVirtualEnvironmentVMVGAType: dvResourceVirtualEnvironmentVMVGAType,
|
||
},
|
||
}, nil
|
||
},
|
||
Elem: &schema.Resource{
|
||
Schema: map[string]*schema.Schema{
|
||
mkResourceVirtualEnvironmentVMVGAEnabled: {
|
||
Type: schema.TypeBool,
|
||
Description: "Whether to enable the VGA device",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMVGAEnabled,
|
||
},
|
||
mkResourceVirtualEnvironmentVMVGAMemory: {
|
||
Type: schema.TypeInt,
|
||
Description: "The VGA memory in megabytes (4-512 MB)",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMVGAMemory,
|
||
ValidateDiagFunc: validator.VGAMemory(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMVGAType: {
|
||
Type: schema.TypeString,
|
||
Description: "The VGA type",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMVGAType,
|
||
ValidateDiagFunc: validator.VGAType(),
|
||
},
|
||
},
|
||
},
|
||
MaxItems: 1,
|
||
MinItems: 0,
|
||
},
|
||
mkResourceVirtualEnvironmentVMVMID: {
|
||
Type: schema.TypeInt,
|
||
Description: "The VM identifier",
|
||
Optional: true,
|
||
Computed: true,
|
||
// "ForceNew: true" handled in CustomizeDiff, making sure VMs with legacy configs with vm_id = -1
|
||
// do not require re-creation.
|
||
ValidateDiagFunc: validator.VMID(),
|
||
},
|
||
mkResourceVirtualEnvironmentVMSCSIHardware: {
|
||
Type: schema.TypeString,
|
||
Description: "The SCSI hardware type",
|
||
Optional: true,
|
||
Default: dvResourceVirtualEnvironmentVMSCSIHardware,
|
||
ValidateDiagFunc: validator.SCSIHardware(),
|
||
},
|
||
},
|
||
CreateContext: vmCreate,
|
||
ReadContext: vmRead,
|
||
UpdateContext: vmUpdate,
|
||
DeleteContext: vmDelete,
|
||
CustomizeDiff: customdiff.All(
|
||
customdiff.ComputedIf(
|
||
mkResourceVirtualEnvironmentVMIPv4Addresses,
|
||
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
|
||
return d.HasChange(mkResourceVirtualEnvironmentVMStarted) ||
|
||
d.HasChange(mkResourceVirtualEnvironmentVMNetworkDevice)
|
||
},
|
||
),
|
||
customdiff.ComputedIf(
|
||
mkResourceVirtualEnvironmentVMIPv6Addresses,
|
||
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
|
||
return d.HasChange(mkResourceVirtualEnvironmentVMStarted) ||
|
||
d.HasChange(mkResourceVirtualEnvironmentVMNetworkDevice)
|
||
},
|
||
),
|
||
customdiff.ComputedIf(
|
||
mkResourceVirtualEnvironmentVMNetworkInterfaceNames,
|
||
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
|
||
return d.HasChange(mkResourceVirtualEnvironmentVMStarted) ||
|
||
d.HasChange(mkResourceVirtualEnvironmentVMNetworkDevice)
|
||
},
|
||
),
|
||
customdiff.ForceNewIf(
|
||
mkResourceVirtualEnvironmentVMVMID,
|
||
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
|
||
newValue := d.Get(mkResourceVirtualEnvironmentVMVMID)
|
||
|
||
// 'vm_id' is ForceNew, except when changing 'vm_id' to existing correct id
|
||
// (automatic fix from -1 to actual vm_id must not re-create VM)
|
||
return strconv.Itoa(newValue.(int)) != d.Id()
|
||
},
|
||
),
|
||
customdiff.ForceNewIf(
|
||
mkResourceVirtualEnvironmentVMNodeName,
|
||
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
|
||
return !d.Get(mkResourceVirtualEnvironmentVMMigrate).(bool)
|
||
},
|
||
),
|
||
),
|
||
Importer: &schema.ResourceImporter{
|
||
StateContext: func(ctx context.Context, d *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) {
|
||
node, id, err := parseImportIDWithNodeName(d.Id())
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
d.SetId(id)
|
||
err = d.Set(mkResourceVirtualEnvironmentVMNodeName, node)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed setting state during import: %w", err)
|
||
}
|
||
|
||
return []*schema.ResourceData{d}, nil
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
func vmCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
clone := d.Get(mkResourceVirtualEnvironmentVMClone).([]interface{})
|
||
|
||
if len(clone) > 0 {
|
||
return vmCreateClone(ctx, d, m)
|
||
}
|
||
|
||
return vmCreateCustom(ctx, d, m)
|
||
}
|
||
|
||
// Check for an existing CloudInit IDE drive. If no such drive is found, return the specified `defaultValue`.
|
||
func findExistingCloudInitDrive(vmConfig *vms.GetResponseData, vmID int, defaultValue string) string {
|
||
devices := []*vms.CustomStorageDevice{
|
||
vmConfig.IDEDevice0, vmConfig.IDEDevice1, vmConfig.IDEDevice2, vmConfig.IDEDevice3,
|
||
}
|
||
for i, device := range devices {
|
||
if device != nil && device.Enabled && device.Media != nil && *device.Media == "cdrom" && strings.Contains(
|
||
device.FileVolume,
|
||
fmt.Sprintf("vm-%d-cloudinit", vmID),
|
||
) {
|
||
return fmt.Sprintf("ide%d", i)
|
||
}
|
||
}
|
||
|
||
return defaultValue
|
||
}
|
||
|
||
// Return a pointer to the IDE device configuration based on its name. The device name is assumed to be a
|
||
// valid IDE interface name.
|
||
func getIdeDevice(vmConfig *vms.GetResponseData, deviceName string) *vms.CustomStorageDevice {
|
||
ideDevice := vmConfig.IDEDevice3
|
||
|
||
switch deviceName {
|
||
case "ide0":
|
||
ideDevice = vmConfig.IDEDevice0
|
||
case "ide1":
|
||
ideDevice = vmConfig.IDEDevice1
|
||
case "ide2":
|
||
ideDevice = vmConfig.IDEDevice2
|
||
}
|
||
|
||
return ideDevice
|
||
}
|
||
|
||
// Delete IDE interfaces that can then be used for CloudInit. The first interface will always
|
||
// be deleted. The second will be deleted only if it isn't empty and isn't the same as the
|
||
// first.
|
||
func deleteIdeDrives(ctx context.Context, vmAPI *vms.Client, itf1 string, itf2 string) diag.Diagnostics {
|
||
ddUpdateBody := &vms.UpdateRequestBody{}
|
||
ddUpdateBody.Delete = append(ddUpdateBody.Delete, itf1)
|
||
tflog.Debug(ctx, fmt.Sprintf("Deleting IDE interface '%s'", itf1))
|
||
|
||
if itf2 != "" && itf2 != itf1 {
|
||
ddUpdateBody.Delete = append(ddUpdateBody.Delete, itf2)
|
||
tflog.Debug(ctx, fmt.Sprintf("Deleting IDE interface '%s'", itf2))
|
||
}
|
||
|
||
e := vmAPI.UpdateVM(ctx, ddUpdateBody)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// Start the VM, then wait for it to actually start; it may not be started immediately if running in HA mode.
|
||
func vmStart(ctx context.Context, vmAPI *vms.Client, d *schema.ResourceData) diag.Diagnostics {
|
||
tflog.Debug(ctx, "Starting VM")
|
||
|
||
startVMTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutStartVM).(int)
|
||
|
||
e := vmAPI.StartVM(ctx, startVMTimeout)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
return diag.FromErr(vmAPI.WaitForVMState(ctx, "running", startVMTimeout, 1))
|
||
}
|
||
|
||
// Shutdown the VM, then wait for it to actually shut down (it may not be shut down immediately if
|
||
// running in HA mode).
|
||
func vmShutdown(ctx context.Context, vmAPI *vms.Client, d *schema.ResourceData) diag.Diagnostics {
|
||
tflog.Debug(ctx, "Shutting down VM")
|
||
|
||
forceStop := types2.CustomBool(true)
|
||
shutdownTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutShutdownVM).(int)
|
||
|
||
e := vmAPI.ShutdownVM(ctx, &vms.ShutdownRequestBody{
|
||
ForceStop: &forceStop,
|
||
Timeout: &shutdownTimeout,
|
||
}, shutdownTimeout+30)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
return diag.FromErr(vmAPI.WaitForVMState(ctx, "stopped", shutdownTimeout, 1))
|
||
}
|
||
|
||
func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
|
||
api, e := config.GetClient()
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
clone := d.Get(mkResourceVirtualEnvironmentVMClone).([]interface{})
|
||
cloneBlock := clone[0].(map[string]interface{})
|
||
cloneRetries := cloneBlock[mkResourceVirtualEnvironmentVMCloneRetries].(int)
|
||
cloneDatastoreID := cloneBlock[mkResourceVirtualEnvironmentVMCloneDatastoreID].(string)
|
||
cloneNodeName := cloneBlock[mkResourceVirtualEnvironmentVMCloneNodeName].(string)
|
||
cloneVMID := cloneBlock[mkResourceVirtualEnvironmentVMCloneVMID].(int)
|
||
cloneFull := cloneBlock[mkResourceVirtualEnvironmentVMCloneFull].(bool)
|
||
|
||
description := d.Get(mkResourceVirtualEnvironmentVMDescription).(string)
|
||
name := d.Get(mkResourceVirtualEnvironmentVMName).(string)
|
||
tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{})
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
poolID := d.Get(mkResourceVirtualEnvironmentVMPoolID).(string)
|
||
vmIDUntyped, hasVMID := d.GetOk(mkResourceVirtualEnvironmentVMVMID)
|
||
vmID := vmIDUntyped.(int)
|
||
|
||
if !hasVMID {
|
||
vmIDNew, err := api.Cluster().GetVMID(ctx)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmID = *vmIDNew
|
||
err = d.Set(mkResourceVirtualEnvironmentVMVMID, vmID)
|
||
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
|
||
fullCopy := types2.CustomBool(cloneFull)
|
||
|
||
cloneBody := &vms.CloneRequestBody{
|
||
FullCopy: &fullCopy,
|
||
VMIDNew: vmID,
|
||
}
|
||
|
||
if cloneDatastoreID != "" {
|
||
cloneBody.TargetStorage = &cloneDatastoreID
|
||
}
|
||
|
||
if description != "" {
|
||
cloneBody.Description = &description
|
||
}
|
||
|
||
if name != "" {
|
||
cloneBody.Name = &name
|
||
}
|
||
|
||
if poolID != "" {
|
||
cloneBody.PoolID = &poolID
|
||
}
|
||
|
||
cloneTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutClone).(int)
|
||
|
||
if cloneNodeName != "" && cloneNodeName != nodeName {
|
||
// Check if any used datastores of the source VM are not shared
|
||
vmConfig, err := api.Node(cloneNodeName).VM(cloneVMID).GetVM(ctx)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
datastores := getDiskDatastores(vmConfig, d)
|
||
|
||
onlySharedDatastores := true
|
||
for _, datastore := range datastores {
|
||
datastoreStatus, err2 := api.Node(cloneNodeName).GetDatastoreStatus(ctx, datastore)
|
||
if err2 != nil {
|
||
return diag.FromErr(err2)
|
||
}
|
||
|
||
if datastoreStatus.Shared != nil && !*datastoreStatus.Shared {
|
||
onlySharedDatastores = false
|
||
break
|
||
}
|
||
}
|
||
|
||
if onlySharedDatastores {
|
||
// If the source and the target node are not the same, only clone directly to the target node if
|
||
// all used datastores in the source VM are shared. Directly cloning to non-shared storage
|
||
// on a different node is currently not supported by proxmox.
|
||
cloneBody.TargetNodeName = &nodeName
|
||
err = api.Node(cloneNodeName).VM(cloneVMID).CloneVM(
|
||
ctx,
|
||
cloneRetries,
|
||
cloneBody,
|
||
cloneTimeout,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
} else {
|
||
// If the source and the target node are not the same and any used datastore in the source VM is
|
||
// not shared, clone to the source node and then migrate to the target node. This is a workaround
|
||
// for missing functionality in the proxmox api as recommended per
|
||
// https://forum.proxmox.com/threads/500-cant-clone-to-non-shared-storage-local.49078/#post-229727
|
||
|
||
// Temporarily clone to local node
|
||
err = api.Node(cloneNodeName).VM(cloneVMID).CloneVM(ctx, cloneRetries, cloneBody, cloneTimeout)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
// Wait for the virtual machine to be created and its configuration lock to be released before migrating.
|
||
|
||
err = api.Node(cloneNodeName).VM(vmID).WaitForVMConfigUnlock(ctx, 600, 5, true)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
// Migrate to target node
|
||
withLocalDisks := types2.CustomBool(true)
|
||
migrateBody := &vms.MigrateRequestBody{
|
||
TargetNode: nodeName,
|
||
WithLocalDisks: &withLocalDisks,
|
||
}
|
||
|
||
if cloneDatastoreID != "" {
|
||
migrateBody.TargetStorage = &cloneDatastoreID
|
||
}
|
||
|
||
err = api.Node(cloneNodeName).VM(vmID).MigrateVM(ctx, migrateBody, cloneTimeout)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
} else {
|
||
e = api.Node(nodeName).VM(cloneVMID).CloneVM(ctx, cloneRetries, cloneBody, cloneTimeout)
|
||
}
|
||
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
d.SetId(strconv.Itoa(vmID))
|
||
|
||
vmAPI := api.Node(nodeName).VM(vmID)
|
||
|
||
// Wait for the virtual machine to be created and its configuration lock to be released.
|
||
e = vmAPI.WaitForVMConfigUnlock(ctx, 600, 5, true)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
// Now that the virtual machine has been cloned, we need to perform some modifications.
|
||
acpi := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool))
|
||
agent := d.Get(mkResourceVirtualEnvironmentVMAgent).([]interface{})
|
||
audioDevices := vmGetAudioDeviceList(d)
|
||
|
||
bios := d.Get(mkResourceVirtualEnvironmentVMBIOS).(string)
|
||
kvmArguments := d.Get(mkResourceVirtualEnvironmentVMKVMArguments).(string)
|
||
scsiHardware := d.Get(mkResourceVirtualEnvironmentVMSCSIHardware).(string)
|
||
cdrom := d.Get(mkResourceVirtualEnvironmentVMCDROM).([]interface{})
|
||
cpu := d.Get(mkResourceVirtualEnvironmentVMCPU).([]interface{})
|
||
initialization := d.Get(mkResourceVirtualEnvironmentVMInitialization).([]interface{})
|
||
hostPCI := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
||
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||
memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
|
||
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
||
operatingSystem := d.Get(mkResourceVirtualEnvironmentVMOperatingSystem).([]interface{})
|
||
serialDevice := d.Get(mkResourceVirtualEnvironmentVMSerialDevice).([]interface{})
|
||
onBoot := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool))
|
||
tabletDevice := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool))
|
||
template := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool))
|
||
vga := d.Get(mkResourceVirtualEnvironmentVMVGA).([]interface{})
|
||
|
||
updateBody := &vms.UpdateRequestBody{
|
||
AudioDevices: audioDevices,
|
||
}
|
||
|
||
ideDevices := vms.CustomStorageDevices{}
|
||
|
||
var del []string
|
||
|
||
//nolint:gosimple
|
||
if acpi != dvResourceVirtualEnvironmentVMACPI {
|
||
updateBody.ACPI = &acpi
|
||
}
|
||
|
||
if len(agent) > 0 {
|
||
agentBlock := agent[0].(map[string]interface{})
|
||
|
||
agentEnabled := types2.CustomBool(
|
||
agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool),
|
||
)
|
||
agentTrim := types2.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool))
|
||
agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string)
|
||
|
||
updateBody.Agent = &vms.CustomAgent{
|
||
Enabled: &agentEnabled,
|
||
TrimClonedDisks: &agentTrim,
|
||
Type: &agentType,
|
||
}
|
||
}
|
||
|
||
if kvmArguments != dvResourceVirtualEnvironmentVMKVMArguments {
|
||
updateBody.KVMArguments = &kvmArguments
|
||
}
|
||
|
||
if bios != dvResourceVirtualEnvironmentVMBIOS {
|
||
updateBody.BIOS = &bios
|
||
}
|
||
|
||
if scsiHardware != dvResourceVirtualEnvironmentVMSCSIHardware {
|
||
updateBody.SCSIHardware = &scsiHardware
|
||
}
|
||
|
||
if len(cdrom) > 0 || len(initialization) > 0 {
|
||
ideDevices = vms.CustomStorageDevices{
|
||
"ide0": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
"ide1": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
"ide2": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
"ide3": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
}
|
||
}
|
||
|
||
if len(cdrom) > 0 {
|
||
cdromBlock := cdrom[0].(map[string]interface{})
|
||
|
||
cdromEnabled := cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled].(bool)
|
||
cdromFileID := cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID].(string)
|
||
cdromInterface := cdromBlock[mkResourceVirtualEnvironmentVMCDROMInterface].(string)
|
||
|
||
if cdromFileID == "" {
|
||
cdromFileID = "cdrom"
|
||
}
|
||
|
||
cdromMedia := "cdrom"
|
||
|
||
ideDevices[cdromInterface] = vms.CustomStorageDevice{
|
||
Enabled: cdromEnabled,
|
||
FileVolume: cdromFileID,
|
||
Media: &cdromMedia,
|
||
}
|
||
}
|
||
|
||
if len(cpu) > 0 {
|
||
cpuBlock := cpu[0].(map[string]interface{})
|
||
|
||
cpuArchitecture := cpuBlock[mkResourceVirtualEnvironmentVMCPUArchitecture].(string)
|
||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
||
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
||
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
||
cpuNUMA := types2.CustomBool(cpuBlock[mkResourceVirtualEnvironmentVMCPUNUMA].(bool))
|
||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
||
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
||
|
||
cpuFlagsConverted := make([]string, len(cpuFlags))
|
||
|
||
for fi, flag := range cpuFlags {
|
||
cpuFlagsConverted[fi] = flag.(string)
|
||
}
|
||
|
||
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
||
if api.API().IsRootTicket() ||
|
||
cpuArchitecture != dvResourceVirtualEnvironmentVMCPUArchitecture {
|
||
updateBody.CPUArchitecture = &cpuArchitecture
|
||
}
|
||
|
||
updateBody.CPUCores = &cpuCores
|
||
updateBody.CPUEmulation = &vms.CustomCPUEmulation{
|
||
Flags: &cpuFlagsConverted,
|
||
Type: cpuType,
|
||
}
|
||
updateBody.NUMAEnabled = &cpuNUMA
|
||
updateBody.CPUSockets = &cpuSockets
|
||
updateBody.CPUUnits = &cpuUnits
|
||
|
||
if cpuHotplugged > 0 {
|
||
updateBody.VirtualCPUCount = &cpuHotplugged
|
||
}
|
||
}
|
||
|
||
if len(initialization) > 0 {
|
||
tflog.Trace(ctx, "Preparing the CloudInit configuration")
|
||
|
||
initializationBlock := initialization[0].(map[string]interface{})
|
||
initializationDatastoreID := initializationBlock[mkResourceVirtualEnvironmentVMInitializationDatastoreID].(string)
|
||
initializationInterface := initializationBlock[mkResourceVirtualEnvironmentVMInitializationInterface].(string)
|
||
|
||
vmConfig, err := vmAPI.GetVM(ctx)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
existingInterface := findExistingCloudInitDrive(vmConfig, vmID, "ide2")
|
||
if initializationInterface == "" {
|
||
initializationInterface = existingInterface
|
||
}
|
||
|
||
tflog.Trace(ctx, fmt.Sprintf("CloudInit IDE interface is '%s'", initializationInterface))
|
||
|
||
const cdromCloudInitEnabled = true
|
||
|
||
cdromCloudInitFileID := fmt.Sprintf("%s:cloudinit", initializationDatastoreID)
|
||
cdromCloudInitMedia := "cdrom"
|
||
ideDevices[initializationInterface] = vms.CustomStorageDevice{
|
||
Enabled: cdromCloudInitEnabled,
|
||
FileVolume: cdromCloudInitFileID,
|
||
Media: &cdromCloudInitMedia,
|
||
}
|
||
|
||
if err := deleteIdeDrives(ctx, vmAPI, initializationInterface, existingInterface); err != nil {
|
||
return err
|
||
}
|
||
|
||
updateBody.CloudInitConfig = vmGetCloudInitConfig(d)
|
||
}
|
||
|
||
if len(hostPCI) > 0 {
|
||
updateBody.PCIDevices = vmGetHostPCIDeviceObjects(d)
|
||
}
|
||
|
||
if len(cdrom) > 0 || len(initialization) > 0 {
|
||
updateBody.IDEDevices = ideDevices
|
||
}
|
||
|
||
if keyboardLayout != dvResourceVirtualEnvironmentVMKeyboardLayout {
|
||
updateBody.KeyboardLayout = &keyboardLayout
|
||
}
|
||
|
||
if len(memory) > 0 {
|
||
memoryBlock := memory[0].(map[string]interface{})
|
||
|
||
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentVMMemoryDedicated].(int)
|
||
memoryFloating := memoryBlock[mkResourceVirtualEnvironmentVMMemoryFloating].(int)
|
||
memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int)
|
||
|
||
updateBody.DedicatedMemory = &memoryDedicated
|
||
updateBody.FloatingMemory = &memoryFloating
|
||
|
||
if memoryShared > 0 {
|
||
memorySharedName := fmt.Sprintf("vm-%d-ivshmem", vmID)
|
||
|
||
updateBody.SharedMemory = &vms.CustomSharedMemory{
|
||
Name: &memorySharedName,
|
||
Size: memoryShared,
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(networkDevice) > 0 {
|
||
updateBody.NetworkDevices = vmGetNetworkDeviceObjects(d)
|
||
|
||
for i := 0; i < len(updateBody.NetworkDevices); i++ {
|
||
if !updateBody.NetworkDevices[i].Enabled {
|
||
del = append(del, fmt.Sprintf("net%d", i))
|
||
}
|
||
}
|
||
|
||
for i := len(updateBody.NetworkDevices); i < maxResourceVirtualEnvironmentVMNetworkDevices; i++ {
|
||
del = append(del, fmt.Sprintf("net%d", i))
|
||
}
|
||
}
|
||
|
||
if len(operatingSystem) > 0 {
|
||
operatingSystemBlock := operatingSystem[0].(map[string]interface{})
|
||
operatingSystemType := operatingSystemBlock[mkResourceVirtualEnvironmentVMOperatingSystemType].(string)
|
||
|
||
updateBody.OSType = &operatingSystemType
|
||
}
|
||
|
||
if len(serialDevice) > 0 {
|
||
updateBody.SerialDevices = vmGetSerialDeviceList(d)
|
||
|
||
for i := len(updateBody.SerialDevices); i < maxResourceVirtualEnvironmentVMSerialDevices; i++ {
|
||
del = append(del, fmt.Sprintf("serial%d", i))
|
||
}
|
||
}
|
||
|
||
updateBody.StartOnBoot = &onBoot
|
||
|
||
updateBody.StartupOrder = vmGetStartupOrder(d)
|
||
|
||
//nolint:gosimple
|
||
if tabletDevice != dvResourceVirtualEnvironmentVMTabletDevice {
|
||
updateBody.TabletDeviceEnabled = &tabletDevice
|
||
}
|
||
|
||
if len(tags) > 0 {
|
||
tagString := vmGetTagsString(d)
|
||
updateBody.Tags = &tagString
|
||
}
|
||
|
||
//nolint:gosimple
|
||
if template != dvResourceVirtualEnvironmentVMTemplate {
|
||
updateBody.Template = &template
|
||
}
|
||
|
||
if len(vga) > 0 {
|
||
vgaDevice, err := vmGetVGADeviceObject(d)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
updateBody.VGADevice = vgaDevice
|
||
}
|
||
|
||
updateBody.Delete = del
|
||
|
||
e = vmAPI.UpdateVM(ctx, updateBody)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||
efiDisk := d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||
|
||
vmConfig, e := vmAPI.GetVM(ctx)
|
||
if e != nil {
|
||
if strings.Contains(e.Error(), "HTTP 404") ||
|
||
(strings.Contains(e.Error(), "HTTP 500") && strings.Contains(e.Error(), "does not exist")) {
|
||
d.SetId("")
|
||
|
||
return nil
|
||
}
|
||
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
allDiskInfo := getDiskInfo(vmConfig, d) // from the cloned VM
|
||
|
||
diskDeviceObjects, e := vmGetDiskDeviceObjects(d, nil) // from the resource config
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
for i := range disk {
|
||
diskBlock := disk[i].(map[string]interface{})
|
||
diskInterface := diskBlock[mkResourceVirtualEnvironmentVMDiskInterface].(string)
|
||
dataStoreID := diskBlock[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
||
diskSize := diskBlock[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
||
prefix := diskDigitPrefix(diskInterface)
|
||
|
||
currentDiskInfo := allDiskInfo[diskInterface]
|
||
configuredDiskInfo := diskDeviceObjects[prefix][diskInterface]
|
||
|
||
if currentDiskInfo == nil {
|
||
diskUpdateBody := &vms.UpdateRequestBody{}
|
||
|
||
switch prefix {
|
||
case "virtio":
|
||
if diskUpdateBody.VirtualIODevices == nil {
|
||
diskUpdateBody.VirtualIODevices = vms.CustomStorageDevices{}
|
||
}
|
||
|
||
diskUpdateBody.VirtualIODevices[diskInterface] = configuredDiskInfo
|
||
case "sata":
|
||
if diskUpdateBody.SATADevices == nil {
|
||
diskUpdateBody.SATADevices = vms.CustomStorageDevices{}
|
||
}
|
||
|
||
diskUpdateBody.SATADevices[diskInterface] = configuredDiskInfo
|
||
case "scsi":
|
||
if diskUpdateBody.SCSIDevices == nil {
|
||
diskUpdateBody.SCSIDevices = vms.CustomStorageDevices{}
|
||
}
|
||
|
||
diskUpdateBody.SCSIDevices[diskInterface] = configuredDiskInfo
|
||
}
|
||
|
||
e = vmAPI.UpdateVM(ctx, diskUpdateBody)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
continue
|
||
}
|
||
|
||
if diskSize < currentDiskInfo.Size.InGigabytes() {
|
||
return diag.Errorf(
|
||
"disk resize fails requests size (%dG) is lower than current size (%s)",
|
||
diskSize,
|
||
*currentDiskInfo.Size,
|
||
)
|
||
}
|
||
|
||
deleteOriginalDisk := types2.CustomBool(true)
|
||
|
||
diskMoveBody := &vms.MoveDiskRequestBody{
|
||
DeleteOriginalDisk: &deleteOriginalDisk,
|
||
Disk: diskInterface,
|
||
TargetStorage: dataStoreID,
|
||
}
|
||
|
||
diskResizeBody := &vms.ResizeDiskRequestBody{
|
||
Disk: diskInterface,
|
||
Size: types2.DiskSizeFromGigabytes(diskSize),
|
||
}
|
||
|
||
moveDisk := false
|
||
|
||
if dataStoreID != "" {
|
||
moveDisk = true
|
||
|
||
if allDiskInfo[diskInterface] != nil {
|
||
fileIDParts := strings.Split(allDiskInfo[diskInterface].FileVolume, ":")
|
||
moveDisk = dataStoreID != fileIDParts[0]
|
||
}
|
||
}
|
||
|
||
if moveDisk {
|
||
moveDiskTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutMoveDisk).(int)
|
||
|
||
e = vmAPI.MoveVMDisk(ctx, diskMoveBody, moveDiskTimeout)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
}
|
||
|
||
if diskSize > currentDiskInfo.Size.InGigabytes() {
|
||
e = vmAPI.ResizeVMDisk(ctx, diskResizeBody)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
}
|
||
}
|
||
|
||
efiDiskInfo := vmGetEfiDisk(d, nil) // from the resource config
|
||
|
||
for i := range efiDisk {
|
||
diskBlock := efiDisk[i].(map[string]interface{})
|
||
diskInterface := "efidisk0"
|
||
dataStoreID := diskBlock[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID].(string)
|
||
efiType := diskBlock[mkResourceVirtualEnvironmentVMEFIDiskType].(string)
|
||
|
||
currentDiskInfo := vmConfig.EFIDisk
|
||
configuredDiskInfo := efiDiskInfo
|
||
|
||
if currentDiskInfo == nil {
|
||
diskUpdateBody := &vms.UpdateRequestBody{}
|
||
|
||
diskUpdateBody.EFIDisk = configuredDiskInfo
|
||
|
||
e = vmAPI.UpdateVM(ctx, diskUpdateBody)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
continue
|
||
}
|
||
|
||
if &efiType != currentDiskInfo.Type {
|
||
return diag.Errorf(
|
||
"resizing of efidisks is not supported.",
|
||
)
|
||
}
|
||
|
||
deleteOriginalDisk := types2.CustomBool(true)
|
||
|
||
diskMoveBody := &vms.MoveDiskRequestBody{
|
||
DeleteOriginalDisk: &deleteOriginalDisk,
|
||
Disk: diskInterface,
|
||
TargetStorage: dataStoreID,
|
||
}
|
||
|
||
moveDisk := false
|
||
|
||
if dataStoreID != "" {
|
||
moveDisk = true
|
||
|
||
if allDiskInfo[diskInterface] != nil {
|
||
fileIDParts := strings.Split(allDiskInfo[diskInterface].FileVolume, ":")
|
||
moveDisk = dataStoreID != fileIDParts[0]
|
||
}
|
||
}
|
||
|
||
if moveDisk {
|
||
moveDiskTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutMoveDisk).(int)
|
||
|
||
e = vmAPI.MoveVMDisk(ctx, diskMoveBody, moveDiskTimeout)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
}
|
||
}
|
||
|
||
return vmCreateStart(ctx, d, m)
|
||
}
|
||
|
||
func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
api, err := config.GetClient()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
resource := VM()
|
||
|
||
acpi := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool))
|
||
|
||
agentBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMAgent},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
agentEnabled := types2.CustomBool(
|
||
agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool),
|
||
)
|
||
agentTrim := types2.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool))
|
||
agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string)
|
||
|
||
kvmArguments := d.Get(mkResourceVirtualEnvironmentVMKVMArguments).(string)
|
||
|
||
audioDevices := vmGetAudioDeviceList(d)
|
||
|
||
bios := d.Get(mkResourceVirtualEnvironmentVMBIOS).(string)
|
||
|
||
cdromBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMCDROM},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
cdromEnabled := cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled].(bool)
|
||
cdromFileID := cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID].(string)
|
||
cdromInterface := cdromBlock[mkResourceVirtualEnvironmentVMCDROMInterface].(string)
|
||
|
||
cdromCloudInitEnabled := false
|
||
cdromCloudInitFileID := ""
|
||
cdromCloudInitInterface := ""
|
||
|
||
if cdromFileID == "" {
|
||
cdromFileID = "cdrom"
|
||
}
|
||
|
||
cpuBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMCPU},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
cpuArchitecture := cpuBlock[mkResourceVirtualEnvironmentVMCPUArchitecture].(string)
|
||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
||
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
||
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||
cpuNUMA := types2.CustomBool(cpuBlock[mkResourceVirtualEnvironmentVMCPUNUMA].(bool))
|
||
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
||
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
||
|
||
description := d.Get(mkResourceVirtualEnvironmentVMDescription).(string)
|
||
diskDeviceObjects, err := vmGetDiskDeviceObjects(d, nil)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
var efiDisk *vms.CustomEFIDisk
|
||
|
||
efiDiskBlock := d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||
if len(efiDiskBlock) > 0 {
|
||
block := efiDiskBlock[0].(map[string]interface{})
|
||
|
||
datastoreID, _ := block[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID].(string)
|
||
fileFormat, _ := block[mkResourceVirtualEnvironmentVMEFIDiskFileFormat].(string)
|
||
efiType, _ := block[mkResourceVirtualEnvironmentVMEFIDiskType].(string)
|
||
preEnrolledKeys := types2.CustomBool(block[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys].(bool))
|
||
|
||
if fileFormat == "" {
|
||
fileFormat = dvResourceVirtualEnvironmentVMEFIDiskFileFormat
|
||
}
|
||
|
||
efiDisk = &vms.CustomEFIDisk{
|
||
Type: &efiType,
|
||
FileVolume: fmt.Sprintf("%s:1", datastoreID),
|
||
Format: &fileFormat,
|
||
PreEnrolledKeys: &preEnrolledKeys,
|
||
}
|
||
}
|
||
|
||
virtioDeviceObjects := diskDeviceObjects["virtio"]
|
||
scsiDeviceObjects := diskDeviceObjects["scsi"]
|
||
// ideDeviceObjects := getOrderedDiskDeviceList(diskDeviceObjects, "ide")
|
||
sataDeviceObjects := diskDeviceObjects["sata"]
|
||
|
||
initializationConfig := vmGetCloudInitConfig(d)
|
||
|
||
if initializationConfig != nil {
|
||
initialization := d.Get(mkResourceVirtualEnvironmentVMInitialization).([]interface{})
|
||
initializationBlock := initialization[0].(map[string]interface{})
|
||
initializationDatastoreID := initializationBlock[mkResourceVirtualEnvironmentVMInitializationDatastoreID].(string)
|
||
|
||
cdromCloudInitEnabled = true
|
||
cdromCloudInitFileID = fmt.Sprintf("%s:cloudinit", initializationDatastoreID)
|
||
|
||
cdromCloudInitInterface = initializationBlock[mkResourceVirtualEnvironmentVMInitializationInterface].(string)
|
||
if cdromCloudInitInterface == "" {
|
||
cdromCloudInitInterface = "ide2"
|
||
}
|
||
}
|
||
|
||
pciDeviceObjects := vmGetHostPCIDeviceObjects(d)
|
||
|
||
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||
memoryBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMMemory},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentVMMemoryDedicated].(int)
|
||
memoryFloating := memoryBlock[mkResourceVirtualEnvironmentVMMemoryFloating].(int)
|
||
memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int)
|
||
|
||
machine := d.Get(mkResourceVirtualEnvironmentVMMachine).(string)
|
||
name := d.Get(mkResourceVirtualEnvironmentVMName).(string)
|
||
tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{})
|
||
|
||
networkDeviceObjects := vmGetNetworkDeviceObjects(d)
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
|
||
operatingSystem, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMOperatingSystem},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
operatingSystemType := operatingSystem[mkResourceVirtualEnvironmentVMOperatingSystemType].(string)
|
||
|
||
poolID := d.Get(mkResourceVirtualEnvironmentVMPoolID).(string)
|
||
|
||
serialDevices := vmGetSerialDeviceList(d)
|
||
|
||
smbios := vmGetSMBIOS(d)
|
||
|
||
startupOrder := vmGetStartupOrder(d)
|
||
|
||
onBoot := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool))
|
||
tabletDevice := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool))
|
||
template := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool))
|
||
|
||
vgaDevice, err := vmGetVGADeviceObject(d)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmIDUntyped, hasVMID := d.GetOk(mkResourceVirtualEnvironmentVMVMID)
|
||
vmID := vmIDUntyped.(int)
|
||
|
||
if !hasVMID {
|
||
vmIDNew, e := api.Cluster().GetVMID(ctx)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
vmID = *vmIDNew
|
||
e = d.Set(mkResourceVirtualEnvironmentVMVMID, vmID)
|
||
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
}
|
||
|
||
var memorySharedObject *vms.CustomSharedMemory
|
||
|
||
var bootOrderConverted []string
|
||
if cdromEnabled {
|
||
bootOrderConverted = []string{cdromInterface}
|
||
}
|
||
|
||
bootOrder := d.Get(mkResourceVirtualEnvironmentVMBootOrder).([]interface{})
|
||
//nolint:nestif
|
||
if len(bootOrder) == 0 {
|
||
if sataDeviceObjects != nil {
|
||
bootOrderConverted = append(bootOrderConverted, "sata0")
|
||
}
|
||
|
||
if scsiDeviceObjects != nil {
|
||
bootOrderConverted = append(bootOrderConverted, "scsi0")
|
||
}
|
||
|
||
if virtioDeviceObjects != nil {
|
||
bootOrderConverted = append(bootOrderConverted, "virtio0")
|
||
}
|
||
|
||
if networkDeviceObjects != nil {
|
||
bootOrderConverted = append(bootOrderConverted, "net0")
|
||
}
|
||
} else {
|
||
bootOrderConverted = make([]string, len(bootOrder))
|
||
for i, device := range bootOrder {
|
||
bootOrderConverted[i] = device.(string)
|
||
}
|
||
}
|
||
|
||
cpuFlagsConverted := make([]string, len(cpuFlags))
|
||
for fi, flag := range cpuFlags {
|
||
cpuFlagsConverted[fi] = flag.(string)
|
||
}
|
||
|
||
ideDevice2Media := "cdrom"
|
||
ideDevices := vms.CustomStorageDevices{
|
||
cdromCloudInitInterface: vms.CustomStorageDevice{
|
||
Enabled: cdromCloudInitEnabled,
|
||
FileVolume: cdromCloudInitFileID,
|
||
Media: &ideDevice2Media,
|
||
},
|
||
cdromInterface: vms.CustomStorageDevice{
|
||
Enabled: cdromEnabled,
|
||
FileVolume: cdromFileID,
|
||
Media: &ideDevice2Media,
|
||
},
|
||
}
|
||
|
||
if memoryShared > 0 {
|
||
memorySharedName := fmt.Sprintf("vm-%d-ivshmem", vmID)
|
||
memorySharedObject = &vms.CustomSharedMemory{
|
||
Name: &memorySharedName,
|
||
Size: memoryShared,
|
||
}
|
||
}
|
||
|
||
scsiHardware := d.Get(mkResourceVirtualEnvironmentVMSCSIHardware).(string)
|
||
|
||
createBody := &vms.CreateRequestBody{
|
||
ACPI: &acpi,
|
||
Agent: &vms.CustomAgent{
|
||
Enabled: &agentEnabled,
|
||
TrimClonedDisks: &agentTrim,
|
||
Type: &agentType,
|
||
},
|
||
AudioDevices: audioDevices,
|
||
BIOS: &bios,
|
||
Boot: &vms.CustomBoot{
|
||
Order: &bootOrderConverted,
|
||
},
|
||
CloudInitConfig: initializationConfig,
|
||
CPUCores: &cpuCores,
|
||
CPUEmulation: &vms.CustomCPUEmulation{
|
||
Flags: &cpuFlagsConverted,
|
||
Type: cpuType,
|
||
},
|
||
CPUSockets: &cpuSockets,
|
||
CPUUnits: &cpuUnits,
|
||
DedicatedMemory: &memoryDedicated,
|
||
EFIDisk: efiDisk,
|
||
FloatingMemory: &memoryFloating,
|
||
IDEDevices: ideDevices,
|
||
KeyboardLayout: &keyboardLayout,
|
||
NetworkDevices: networkDeviceObjects,
|
||
NUMAEnabled: &cpuNUMA,
|
||
OSType: &operatingSystemType,
|
||
PCIDevices: pciDeviceObjects,
|
||
SCSIHardware: &scsiHardware,
|
||
SerialDevices: serialDevices,
|
||
SharedMemory: memorySharedObject,
|
||
StartOnBoot: &onBoot,
|
||
SMBIOS: smbios,
|
||
StartupOrder: startupOrder,
|
||
TabletDeviceEnabled: &tabletDevice,
|
||
Template: &template,
|
||
VGADevice: vgaDevice,
|
||
VMID: &vmID,
|
||
}
|
||
|
||
if sataDeviceObjects != nil {
|
||
createBody.SATADevices = sataDeviceObjects
|
||
}
|
||
|
||
if scsiDeviceObjects != nil {
|
||
createBody.SCSIDevices = scsiDeviceObjects
|
||
}
|
||
|
||
if virtioDeviceObjects != nil {
|
||
createBody.VirtualIODevices = virtioDeviceObjects
|
||
}
|
||
|
||
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
||
if api.API().IsRootTicket() ||
|
||
cpuArchitecture != dvResourceVirtualEnvironmentVMCPUArchitecture {
|
||
createBody.CPUArchitecture = &cpuArchitecture
|
||
}
|
||
|
||
if cpuHotplugged > 0 {
|
||
createBody.VirtualCPUCount = &cpuHotplugged
|
||
}
|
||
|
||
if description != "" {
|
||
createBody.Description = &description
|
||
}
|
||
|
||
if len(tags) > 0 {
|
||
tagsString := vmGetTagsString(d)
|
||
createBody.Tags = &tagsString
|
||
}
|
||
|
||
if kvmArguments != "" {
|
||
createBody.KVMArguments = &kvmArguments
|
||
}
|
||
|
||
if machine != "" {
|
||
createBody.Machine = &machine
|
||
}
|
||
|
||
if name != "" {
|
||
createBody.Name = &name
|
||
}
|
||
|
||
if poolID != "" {
|
||
createBody.PoolID = &poolID
|
||
}
|
||
|
||
err = api.Node(nodeName).VM(0).CreateVM(ctx, createBody, vmCreateTimeoutSeconds)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
d.SetId(strconv.Itoa(vmID))
|
||
|
||
return vmCreateCustomDisks(ctx, d, m)
|
||
}
|
||
|
||
func vmCreateCustomDisks(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
vmID, err := strconv.Atoi(d.Id())
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
var commands []string
|
||
|
||
// Determine the ID of the next disk.
|
||
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||
diskCount := 0
|
||
|
||
for _, d := range disk {
|
||
block := d.(map[string]interface{})
|
||
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||
|
||
if fileID == "" {
|
||
diskCount++
|
||
}
|
||
}
|
||
|
||
// Retrieve some information about the disk schema.
|
||
resourceSchema := VM().Schema
|
||
diskSchemaElem := resourceSchema[mkResourceVirtualEnvironmentVMDisk].Elem
|
||
diskSchemaResource := diskSchemaElem.(*schema.Resource)
|
||
diskSpeedResource := diskSchemaResource.Schema[mkResourceVirtualEnvironmentVMDiskSpeed]
|
||
|
||
// Generate the commands required to import the specified disks.
|
||
importedDiskCount := 0
|
||
|
||
for _, d := range disk {
|
||
block := d.(map[string]interface{})
|
||
|
||
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||
|
||
if fileID == "" {
|
||
continue
|
||
}
|
||
|
||
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
||
fileFormat, _ := block[mkResourceVirtualEnvironmentVMDiskFileFormat].(string)
|
||
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
||
speed := block[mkResourceVirtualEnvironmentVMDiskSpeed].([]interface{})
|
||
diskInterface, _ := block[mkResourceVirtualEnvironmentVMDiskInterface].(string)
|
||
ioThread := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskIOThread].(bool))
|
||
ssd := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool))
|
||
discard, _ := block[mkResourceVirtualEnvironmentVMDiskDiscard].(string)
|
||
cache, _ := block[mkResourceVirtualEnvironmentVMDiskCache].(string)
|
||
|
||
if fileFormat == "" {
|
||
fileFormat = dvResourceVirtualEnvironmentVMDiskFileFormat
|
||
}
|
||
|
||
if len(speed) == 0 {
|
||
diskSpeedDefault, err := diskSpeedResource.DefaultValue()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
speed = diskSpeedDefault.([]interface{})
|
||
}
|
||
|
||
speedBlock := speed[0].(map[string]interface{})
|
||
speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int)
|
||
speedLimitReadBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable].(int)
|
||
speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int)
|
||
speedLimitWriteBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable].(int)
|
||
|
||
diskOptions := ""
|
||
|
||
if ioThread {
|
||
diskOptions += ",iothread=1"
|
||
}
|
||
|
||
if ssd {
|
||
diskOptions += ",ssd=1"
|
||
}
|
||
|
||
if discard != "" {
|
||
diskOptions += fmt.Sprintf(",discard=%s", discard)
|
||
}
|
||
|
||
if cache != "" {
|
||
diskOptions += fmt.Sprintf(",cache=%s", cache)
|
||
}
|
||
|
||
if speedLimitRead > 0 {
|
||
diskOptions += fmt.Sprintf(",mbps_rd=%d", speedLimitRead)
|
||
}
|
||
|
||
if speedLimitReadBurstable > 0 {
|
||
diskOptions += fmt.Sprintf(",mbps_rd_max=%d", speedLimitReadBurstable)
|
||
}
|
||
|
||
if speedLimitWrite > 0 {
|
||
diskOptions += fmt.Sprintf(",mbps_wr=%d", speedLimitWrite)
|
||
}
|
||
|
||
if speedLimitWriteBurstable > 0 {
|
||
diskOptions += fmt.Sprintf(",mbps_wr_max=%d", speedLimitWriteBurstable)
|
||
}
|
||
|
||
filePathTmp := fmt.Sprintf(
|
||
"/tmp/vm-%d-disk-%d.%s",
|
||
vmID,
|
||
diskCount+importedDiskCount,
|
||
fileFormat,
|
||
)
|
||
|
||
//nolint:lll
|
||
commands = append(
|
||
commands,
|
||
`set -e`,
|
||
fmt.Sprintf(`file_id="%s"`, fileID),
|
||
fmt.Sprintf(`file_format="%s"`, fileFormat),
|
||
fmt.Sprintf(`datastore_id_target="%s"`, datastoreID),
|
||
fmt.Sprintf(`disk_options="%s"`, diskOptions),
|
||
fmt.Sprintf(`disk_size="%d"`, size),
|
||
fmt.Sprintf(`disk_interface="%s"`, diskInterface),
|
||
fmt.Sprintf(`file_path_tmp="%s"`, filePathTmp),
|
||
fmt.Sprintf(`vm_id="%d"`, vmID),
|
||
`source_image=$(pvesm path "$file_id")`,
|
||
`cp "$source_image" "$file_path_tmp"`,
|
||
`qemu-img resize -f "$file_format" "$file_path_tmp" "${disk_size}G"`,
|
||
`imported_disk="$(qm importdisk "$vm_id" "$file_path_tmp" "$datastore_id_target" -format $file_format | grep "unused0" | cut -d ":" -f 3 | cut -d "'" -f 1)"`,
|
||
`disk_id="${datastore_id_target}:$imported_disk${disk_options}"`,
|
||
`qm set "$vm_id" "-${disk_interface}" "$disk_id"`,
|
||
`rm -f "$file_path_tmp"`,
|
||
)
|
||
|
||
importedDiskCount++
|
||
}
|
||
|
||
// 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 {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
|
||
api, err := config.GetClient()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
|
||
err = api.SSH().ExecuteNodeCommands(ctx, nodeName, commands)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
|
||
return vmCreateStart(ctx, d, m)
|
||
}
|
||
|
||
func vmCreateStart(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
started := d.Get(mkResourceVirtualEnvironmentVMStarted).(bool)
|
||
template := d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)
|
||
reboot := d.Get(mkResourceVirtualEnvironmentVMRebootAfterCreation).(bool)
|
||
|
||
if !started || template {
|
||
return vmRead(ctx, d, m)
|
||
}
|
||
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
api, err := config.GetClient()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
vmID, err := strconv.Atoi(d.Id())
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmAPI := api.Node(nodeName).VM(vmID)
|
||
|
||
// Start the virtual machine and wait for it to reach a running state before continuing.
|
||
if diags := vmStart(ctx, vmAPI, d); diags != nil {
|
||
return diags
|
||
}
|
||
|
||
if reboot {
|
||
rebootTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutReboot).(int)
|
||
|
||
err := vmAPI.RebootVM(
|
||
ctx,
|
||
&vms.RebootRequestBody{
|
||
Timeout: &rebootTimeout,
|
||
},
|
||
rebootTimeout+30,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
|
||
return vmRead(ctx, d, m)
|
||
}
|
||
|
||
func vmGetAudioDeviceList(d *schema.ResourceData) vms.CustomAudioDevices {
|
||
devices := d.Get(mkResourceVirtualEnvironmentVMAudioDevice).([]interface{})
|
||
list := make(vms.CustomAudioDevices, len(devices))
|
||
|
||
for i, v := range devices {
|
||
block := v.(map[string]interface{})
|
||
|
||
device, _ := block[mkResourceVirtualEnvironmentVMAudioDeviceDevice].(string)
|
||
driver, _ := block[mkResourceVirtualEnvironmentVMAudioDeviceDriver].(string)
|
||
enabled, _ := block[mkResourceVirtualEnvironmentVMAudioDeviceEnabled].(bool)
|
||
|
||
list[i].Device = device
|
||
list[i].Driver = &driver
|
||
list[i].Enabled = enabled
|
||
}
|
||
|
||
return list
|
||
}
|
||
|
||
func vmGetAudioDeviceValidator() schema.SchemaValidateDiagFunc {
|
||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||
"AC97",
|
||
"ich9-intel-hda",
|
||
"intel-hda",
|
||
}, false))
|
||
}
|
||
|
||
func vmGetAudioDriverValidator() schema.SchemaValidateDiagFunc {
|
||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||
"spice",
|
||
}, false))
|
||
}
|
||
|
||
func vmGetCloudInitConfig(d *schema.ResourceData) *vms.CustomCloudInitConfig {
|
||
var initializationConfig *vms.CustomCloudInitConfig
|
||
|
||
initialization := d.Get(mkResourceVirtualEnvironmentVMInitialization).([]interface{})
|
||
|
||
if len(initialization) > 0 {
|
||
initializationBlock := initialization[0].(map[string]interface{})
|
||
initializationConfig = &vms.CustomCloudInitConfig{}
|
||
initializationDNS := initializationBlock[mkResourceVirtualEnvironmentVMInitializationDNS].([]interface{})
|
||
|
||
if len(initializationDNS) > 0 {
|
||
initializationDNSBlock := initializationDNS[0].(map[string]interface{})
|
||
domain := initializationDNSBlock[mkResourceVirtualEnvironmentVMInitializationDNSDomain].(string)
|
||
|
||
if domain != "" {
|
||
initializationConfig.SearchDomain = &domain
|
||
}
|
||
|
||
server := initializationDNSBlock[mkResourceVirtualEnvironmentVMInitializationDNSServer].(string)
|
||
|
||
if server != "" {
|
||
initializationConfig.Nameserver = &server
|
||
}
|
||
}
|
||
|
||
initializationIPConfig := initializationBlock[mkResourceVirtualEnvironmentVMInitializationIPConfig].([]interface{})
|
||
initializationConfig.IPConfig = make(
|
||
[]vms.CustomCloudInitIPConfig,
|
||
len(initializationIPConfig),
|
||
)
|
||
|
||
for i, c := range initializationIPConfig {
|
||
configBlock := c.(map[string]interface{})
|
||
ipv4 := configBlock[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4].([]interface{})
|
||
|
||
if len(ipv4) > 0 {
|
||
ipv4Block := ipv4[0].(map[string]interface{})
|
||
ipv4Address := ipv4Block[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Address].(string)
|
||
|
||
if ipv4Address != "" {
|
||
initializationConfig.IPConfig[i].IPv4 = &ipv4Address
|
||
}
|
||
|
||
ipv4Gateway := ipv4Block[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Gateway].(string)
|
||
|
||
if ipv4Gateway != "" {
|
||
initializationConfig.IPConfig[i].GatewayIPv4 = &ipv4Gateway
|
||
}
|
||
}
|
||
|
||
ipv6 := configBlock[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6].([]interface{})
|
||
|
||
if len(ipv6) > 0 {
|
||
ipv6Block := ipv6[0].(map[string]interface{})
|
||
ipv6Address := ipv6Block[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Address].(string)
|
||
|
||
if ipv6Address != "" {
|
||
initializationConfig.IPConfig[i].IPv6 = &ipv6Address
|
||
}
|
||
|
||
ipv6Gateway := ipv6Block[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Gateway].(string)
|
||
|
||
if ipv6Gateway != "" {
|
||
initializationConfig.IPConfig[i].GatewayIPv6 = &ipv6Gateway
|
||
}
|
||
}
|
||
}
|
||
|
||
initializationUserAccount := initializationBlock[mkResourceVirtualEnvironmentVMInitializationUserAccount].([]interface{})
|
||
|
||
if len(initializationUserAccount) > 0 {
|
||
initializationUserAccountBlock := initializationUserAccount[0].(map[string]interface{})
|
||
keys := initializationUserAccountBlock[mkResourceVirtualEnvironmentVMInitializationUserAccountKeys].([]interface{})
|
||
|
||
if len(keys) > 0 {
|
||
sshKeys := make(vms.CustomCloudInitSSHKeys, len(keys))
|
||
|
||
for i, k := range keys {
|
||
sshKeys[i] = k.(string)
|
||
}
|
||
|
||
initializationConfig.SSHKeys = &sshKeys
|
||
}
|
||
|
||
password := initializationUserAccountBlock[mkResourceVirtualEnvironmentVMInitializationUserAccountPassword].(string)
|
||
|
||
if password != "" {
|
||
initializationConfig.Password = &password
|
||
}
|
||
|
||
username := initializationUserAccountBlock[mkResourceVirtualEnvironmentVMInitializationUserAccountUsername].(string)
|
||
|
||
initializationConfig.Username = &username
|
||
}
|
||
|
||
initializationUserDataFileID := initializationBlock[mkResourceVirtualEnvironmentVMInitializationUserDataFileID].(string)
|
||
|
||
if initializationUserDataFileID != "" {
|
||
initializationConfig.Files = &vms.CustomCloudInitFiles{
|
||
UserVolume: &initializationUserDataFileID,
|
||
}
|
||
}
|
||
|
||
initializationVendorDataFileID := initializationBlock[mkResourceVirtualEnvironmentVMInitializationVendorDataFileID].(string)
|
||
|
||
if initializationVendorDataFileID != "" {
|
||
if initializationConfig.Files == nil {
|
||
initializationConfig.Files = &vms.CustomCloudInitFiles{}
|
||
}
|
||
|
||
initializationConfig.Files.VendorVolume = &initializationVendorDataFileID
|
||
}
|
||
|
||
initializationNetworkDataFileID := initializationBlock[mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID].(string)
|
||
|
||
if initializationNetworkDataFileID != "" {
|
||
if initializationConfig.Files == nil {
|
||
initializationConfig.Files = &vms.CustomCloudInitFiles{}
|
||
}
|
||
|
||
initializationConfig.Files.NetworkVolume = &initializationNetworkDataFileID
|
||
}
|
||
|
||
//nolint:lll
|
||
initializationMetaDataFileID := initializationBlock[mkResourceVirtualEnvironmentVMInitializationMetaDataFileID].(string)
|
||
|
||
if initializationMetaDataFileID != "" {
|
||
if initializationConfig.Files == nil {
|
||
initializationConfig.Files = &vms.CustomCloudInitFiles{}
|
||
}
|
||
|
||
initializationConfig.Files.MetaVolume = &initializationMetaDataFileID
|
||
}
|
||
|
||
initializationType := initializationBlock[mkResourceVirtualEnvironmentVMInitializationType].(string)
|
||
|
||
if initializationType != "" {
|
||
initializationConfig.Type = &initializationType
|
||
}
|
||
}
|
||
|
||
return initializationConfig
|
||
}
|
||
|
||
func vmGetCPUArchitectureValidator() schema.SchemaValidateDiagFunc {
|
||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||
"aarch64",
|
||
"x86_64",
|
||
}, false))
|
||
}
|
||
|
||
func vmGetDiskDeviceObjects(
|
||
d *schema.ResourceData,
|
||
disks []interface{},
|
||
) (map[string]map[string]vms.CustomStorageDevice, error) {
|
||
var diskDevice []interface{}
|
||
|
||
if disks != nil {
|
||
diskDevice = disks
|
||
} else {
|
||
diskDevice = d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||
}
|
||
|
||
diskDeviceObjects := map[string]map[string]vms.CustomStorageDevice{}
|
||
resource := VM()
|
||
|
||
for _, diskEntry := range diskDevice {
|
||
diskDevice := vms.CustomStorageDevice{
|
||
Enabled: true,
|
||
}
|
||
|
||
block := diskEntry.(map[string]interface{})
|
||
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
||
fileFormat, _ := block[mkResourceVirtualEnvironmentVMDiskFileFormat].(string)
|
||
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
||
diskInterface, _ := block[mkResourceVirtualEnvironmentVMDiskInterface].(string)
|
||
ioThread := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskIOThread].(bool))
|
||
ssd := types2.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool))
|
||
discard := block[mkResourceVirtualEnvironmentVMDiskDiscard].(string)
|
||
cache := block[mkResourceVirtualEnvironmentVMDiskCache].(string)
|
||
|
||
speedBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMDisk, mkResourceVirtualEnvironmentVMDiskSpeed},
|
||
0,
|
||
false,
|
||
)
|
||
if err != nil {
|
||
return diskDeviceObjects, err
|
||
}
|
||
|
||
if fileFormat == "" {
|
||
fileFormat = dvResourceVirtualEnvironmentVMDiskFileFormat
|
||
}
|
||
if fileID != "" {
|
||
diskDevice.Enabled = false
|
||
} else {
|
||
diskDevice.FileVolume = fmt.Sprintf("%s:%d", datastoreID, size)
|
||
}
|
||
|
||
diskDevice.ID = &datastoreID
|
||
diskDevice.Interface = &diskInterface
|
||
diskDevice.Format = &fileFormat
|
||
diskDevice.FileID = &fileID
|
||
diskSize := types2.DiskSizeFromGigabytes(size)
|
||
diskDevice.Size = &diskSize
|
||
diskDevice.SizeInt = &size
|
||
diskDevice.IOThread = &ioThread
|
||
diskDevice.Discard = &discard
|
||
diskDevice.Cache = &cache
|
||
|
||
if !strings.HasPrefix(diskInterface, "virtio") {
|
||
diskDevice.SSD = &ssd
|
||
}
|
||
|
||
if len(speedBlock) > 0 {
|
||
speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int)
|
||
speedLimitReadBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable].(int)
|
||
speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int)
|
||
speedLimitWriteBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable].(int)
|
||
|
||
if speedLimitRead > 0 {
|
||
diskDevice.MaxReadSpeedMbps = &speedLimitRead
|
||
}
|
||
|
||
if speedLimitReadBurstable > 0 {
|
||
diskDevice.BurstableReadSpeedMbps = &speedLimitReadBurstable
|
||
}
|
||
|
||
if speedLimitWrite > 0 {
|
||
diskDevice.MaxWriteSpeedMbps = &speedLimitWrite
|
||
}
|
||
|
||
if speedLimitWriteBurstable > 0 {
|
||
diskDevice.BurstableWriteSpeedMbps = &speedLimitWriteBurstable
|
||
}
|
||
}
|
||
|
||
baseDiskInterface := diskDigitPrefix(diskInterface)
|
||
|
||
if baseDiskInterface != "virtio" && baseDiskInterface != "scsi" &&
|
||
baseDiskInterface != "sata" {
|
||
errorMsg := fmt.Sprintf(
|
||
"Defined disk interface not supported. Interface was %s, but only virtio, sata and scsi are supported",
|
||
diskInterface,
|
||
)
|
||
return diskDeviceObjects, errors.New(errorMsg)
|
||
}
|
||
|
||
if _, present := diskDeviceObjects[baseDiskInterface]; !present {
|
||
diskDeviceObjects[baseDiskInterface] = map[string]vms.CustomStorageDevice{}
|
||
}
|
||
|
||
diskDeviceObjects[baseDiskInterface][diskInterface] = diskDevice
|
||
}
|
||
|
||
return diskDeviceObjects, nil
|
||
}
|
||
|
||
func vmGetEfiDisk(d *schema.ResourceData, disk []interface{}) *vms.CustomEFIDisk {
|
||
var efiDisk []interface{}
|
||
|
||
if disk != nil {
|
||
efiDisk = disk
|
||
} else {
|
||
efiDisk = d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||
}
|
||
|
||
var efiDiskConfig *vms.CustomEFIDisk
|
||
|
||
if len(efiDisk) > 0 {
|
||
efiDiskConfig = &vms.CustomEFIDisk{}
|
||
|
||
block := efiDisk[0].(map[string]interface{})
|
||
datastoreID, _ := block[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID].(string)
|
||
fileFormat, _ := block[mkResourceVirtualEnvironmentVMEFIDiskFileFormat].(string)
|
||
efiType, _ := block[mkResourceVirtualEnvironmentVMEFIDiskType].(string)
|
||
preEnrolledKeys := types2.CustomBool(block[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys].(bool))
|
||
|
||
// special case for efi disk, the size is ignored, see docs for more info
|
||
efiDiskConfig.FileVolume = fmt.Sprintf("%s:1", datastoreID)
|
||
efiDiskConfig.Format = &fileFormat
|
||
efiDiskConfig.Type = &efiType
|
||
efiDiskConfig.PreEnrolledKeys = &preEnrolledKeys
|
||
}
|
||
|
||
return efiDiskConfig
|
||
}
|
||
|
||
func vmGetEfiDiskAsStorageDevice(d *schema.ResourceData, disk []interface{}) (*vms.CustomStorageDevice, error) {
|
||
efiDisk := vmGetEfiDisk(d, disk)
|
||
|
||
var storageDevice *vms.CustomStorageDevice
|
||
|
||
if efiDisk != nil {
|
||
id := "0"
|
||
baseDiskInterface := "efidisk"
|
||
diskInterface := fmt.Sprint(baseDiskInterface, id)
|
||
|
||
storageDevice = &vms.CustomStorageDevice{
|
||
Enabled: true,
|
||
FileVolume: efiDisk.FileVolume,
|
||
Format: efiDisk.Format,
|
||
Interface: &diskInterface,
|
||
ID: &id,
|
||
}
|
||
|
||
if efiDisk.Type != nil {
|
||
ds, err := types2.ParseDiskSize(*efiDisk.Type)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("invalid efi disk type: %s", err.Error())
|
||
}
|
||
|
||
sizeInt := ds.InMegabytes()
|
||
storageDevice.Size = &ds
|
||
storageDevice.SizeInt = &sizeInt
|
||
}
|
||
}
|
||
|
||
return storageDevice, nil
|
||
}
|
||
|
||
func vmGetHostPCIDeviceObjects(d *schema.ResourceData) vms.CustomPCIDevices {
|
||
pciDevice := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
||
pciDeviceObjects := make(vms.CustomPCIDevices, len(pciDevice))
|
||
|
||
for i, pciDeviceEntry := range pciDevice {
|
||
block := pciDeviceEntry.(map[string]interface{})
|
||
|
||
ids, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceID].(string)
|
||
mdev, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceMDev].(string)
|
||
pcie := types2.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDevicePCIE].(bool))
|
||
rombar := types2.CustomBool(
|
||
block[mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR].(bool),
|
||
)
|
||
romfile, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile].(string)
|
||
xvga := types2.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA].(bool))
|
||
mapping, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceMapping].(string)
|
||
|
||
device := vms.CustomPCIDevice{
|
||
PCIExpress: &pcie,
|
||
ROMBAR: &rombar,
|
||
XVGA: &xvga,
|
||
}
|
||
if ids != "" {
|
||
dIds := strings.Split(ids, ";")
|
||
device.DeviceIDs = &dIds
|
||
}
|
||
|
||
if mdev != "" {
|
||
device.MDev = &mdev
|
||
}
|
||
|
||
if romfile != "" {
|
||
device.ROMFile = &romfile
|
||
}
|
||
|
||
if mapping != "" {
|
||
device.Mapping = &mapping
|
||
}
|
||
|
||
pciDeviceObjects[i] = device
|
||
}
|
||
|
||
return pciDeviceObjects
|
||
}
|
||
|
||
func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices {
|
||
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
||
networkDeviceObjects := make(vms.CustomNetworkDevices, len(networkDevice))
|
||
|
||
for i, networkDeviceEntry := range networkDevice {
|
||
block := networkDeviceEntry.(map[string]interface{})
|
||
|
||
bridge := block[mkResourceVirtualEnvironmentVMNetworkDeviceBridge].(string)
|
||
enabled := block[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled].(bool)
|
||
firewall := types2.CustomBool(block[mkResourceVirtualEnvironmentVMNetworkDeviceFirewall].(bool))
|
||
macAddress := block[mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress].(string)
|
||
model := block[mkResourceVirtualEnvironmentVMNetworkDeviceModel].(string)
|
||
rateLimit := block[mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit].(float64)
|
||
vlanID := block[mkResourceVirtualEnvironmentVMNetworkDeviceVLANID].(int)
|
||
mtu := block[mkResourceVirtualEnvironmentVMNetworkDeviceMTU].(int)
|
||
|
||
device := vms.CustomNetworkDevice{
|
||
Enabled: enabled,
|
||
Firewall: &firewall,
|
||
Model: model,
|
||
}
|
||
|
||
if bridge != "" {
|
||
device.Bridge = &bridge
|
||
}
|
||
|
||
if macAddress != "" {
|
||
device.MACAddress = &macAddress
|
||
}
|
||
|
||
if rateLimit != 0 {
|
||
device.RateLimit = &rateLimit
|
||
}
|
||
|
||
if vlanID != 0 {
|
||
device.Tag = &vlanID
|
||
}
|
||
|
||
if mtu != 0 {
|
||
device.MTU = &mtu
|
||
}
|
||
|
||
networkDeviceObjects[i] = device
|
||
}
|
||
|
||
return networkDeviceObjects
|
||
}
|
||
|
||
func vmGetOperatingSystemTypeValidator() schema.SchemaValidateDiagFunc {
|
||
return validation.ToDiagFunc(validation.StringInSlice([]string{
|
||
"l24",
|
||
"l26",
|
||
"other",
|
||
"solaris",
|
||
"w2k",
|
||
"w2k3",
|
||
"w2k8",
|
||
"win7",
|
||
"win8",
|
||
"win10",
|
||
"wvista",
|
||
"wxp",
|
||
}, false))
|
||
}
|
||
|
||
func vmGetSerialDeviceList(d *schema.ResourceData) vms.CustomSerialDevices {
|
||
device := d.Get(mkResourceVirtualEnvironmentVMSerialDevice).([]interface{})
|
||
list := make(vms.CustomSerialDevices, len(device))
|
||
|
||
for i, v := range device {
|
||
block := v.(map[string]interface{})
|
||
|
||
device, _ := block[mkResourceVirtualEnvironmentVMSerialDeviceDevice].(string)
|
||
|
||
list[i] = device
|
||
}
|
||
|
||
return list
|
||
}
|
||
|
||
func vmGetSMBIOS(d *schema.ResourceData) *vms.CustomSMBIOS {
|
||
smbiosSections := d.Get(mkResourceVirtualEnvironmentVMSMBIOS).([]interface{})
|
||
//nolint:nestif
|
||
if len(smbiosSections) > 0 {
|
||
smbiosBlock := smbiosSections[0].(map[string]interface{})
|
||
b64 := types2.CustomBool(true)
|
||
family, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSFamily].(string)
|
||
manufacturer, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSManufacturer].(string)
|
||
product, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSProduct].(string)
|
||
serial, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSSerial].(string)
|
||
sku, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSSKU].(string)
|
||
version, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSVersion].(string)
|
||
uid, _ := smbiosBlock[mkResourceVirtualEnvironmentVMSMBIOSUUID].(string)
|
||
|
||
smbios := vms.CustomSMBIOS{
|
||
Base64: &b64,
|
||
}
|
||
|
||
if family != "" {
|
||
v := base64.StdEncoding.EncodeToString([]byte(family))
|
||
smbios.Family = &v
|
||
}
|
||
|
||
if manufacturer != "" {
|
||
v := base64.StdEncoding.EncodeToString([]byte(manufacturer))
|
||
smbios.Manufacturer = &v
|
||
}
|
||
|
||
if product != "" {
|
||
v := base64.StdEncoding.EncodeToString([]byte(product))
|
||
smbios.Product = &v
|
||
}
|
||
|
||
if serial != "" {
|
||
v := base64.StdEncoding.EncodeToString([]byte(serial))
|
||
smbios.Serial = &v
|
||
}
|
||
|
||
if sku != "" {
|
||
v := base64.StdEncoding.EncodeToString([]byte(sku))
|
||
smbios.SKU = &v
|
||
}
|
||
|
||
if version != "" {
|
||
v := base64.StdEncoding.EncodeToString([]byte(version))
|
||
smbios.Version = &v
|
||
}
|
||
|
||
if uid != "" {
|
||
smbios.UUID = &uid
|
||
}
|
||
|
||
if smbios.UUID == nil || *smbios.UUID == "" {
|
||
smbios.UUID = types2.StrPtr(uuid.New().String())
|
||
}
|
||
|
||
return &smbios
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func vmGetStartupOrder(d *schema.ResourceData) *vms.CustomStartupOrder {
|
||
startup := d.Get(mkResourceVirtualEnvironmentVMStartup).([]interface{})
|
||
if len(startup) > 0 {
|
||
startupBlock := startup[0].(map[string]interface{})
|
||
startupOrder := startupBlock[mkResourceVirtualEnvironmentVMStartupOrder].(int)
|
||
startupUpDelay := startupBlock[mkResourceVirtualEnvironmentVMStartupUpDelay].(int)
|
||
startupDownDelay := startupBlock[mkResourceVirtualEnvironmentVMStartupDownDelay].(int)
|
||
|
||
order := vms.CustomStartupOrder{}
|
||
|
||
if startupUpDelay >= 0 {
|
||
order.Up = &startupUpDelay
|
||
}
|
||
|
||
if startupDownDelay >= 0 {
|
||
order.Down = &startupDownDelay
|
||
}
|
||
|
||
if startupOrder >= 0 {
|
||
order.Order = &startupOrder
|
||
}
|
||
|
||
return &order
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func vmGetTagsString(d *schema.ResourceData) string {
|
||
tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{})
|
||
var sanitizedTags []string
|
||
for i := 0; i < len(tags); i++ {
|
||
tag := strings.TrimSpace(tags[i].(string))
|
||
if len(tag) > 0 {
|
||
sanitizedTags = append(sanitizedTags, tag)
|
||
}
|
||
}
|
||
sort.Strings(sanitizedTags)
|
||
return strings.Join(sanitizedTags, ";")
|
||
}
|
||
|
||
func vmGetSerialDeviceValidator() schema.SchemaValidateDiagFunc {
|
||
return validation.ToDiagFunc(func(i interface{}, k string) (s []string, es []error) {
|
||
v, ok := i.(string)
|
||
|
||
if !ok {
|
||
es = append(es, fmt.Errorf("expected type of %s to be string", k))
|
||
return
|
||
}
|
||
|
||
if !strings.HasPrefix(v, "/dev/") && v != "socket" {
|
||
es = append(es, fmt.Errorf("expected %s to be '/dev/*' or 'socket'", k))
|
||
return
|
||
}
|
||
|
||
return
|
||
})
|
||
}
|
||
|
||
func vmGetVGADeviceObject(d *schema.ResourceData) (*vms.CustomVGADevice, error) {
|
||
resource := VM()
|
||
|
||
vgaBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMVGA},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
vgaEnabled := types2.CustomBool(vgaBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool))
|
||
vgaMemory := vgaBlock[mkResourceVirtualEnvironmentVMVGAMemory].(int)
|
||
vgaType := vgaBlock[mkResourceVirtualEnvironmentVMVGAType].(string)
|
||
|
||
vgaDevice := &vms.CustomVGADevice{}
|
||
|
||
if vgaEnabled {
|
||
if vgaMemory > 0 {
|
||
vgaDevice.Memory = &vgaMemory
|
||
}
|
||
|
||
vgaDevice.Type = &vgaType
|
||
} else {
|
||
vgaType = "none"
|
||
|
||
vgaDevice = &vms.CustomVGADevice{
|
||
Type: &vgaType,
|
||
}
|
||
}
|
||
|
||
return vgaDevice, nil
|
||
}
|
||
|
||
func vmRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
api, err := config.GetClient()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmID, err := strconv.Atoi(d.Id())
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmNodeName, err := api.Cluster().GetVMNodeName(ctx, vmID)
|
||
if err != nil {
|
||
if errors.Is(err, cluster.ErrVMDoesNotExist) {
|
||
d.SetId("")
|
||
|
||
return nil
|
||
}
|
||
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
if vmNodeName != d.Get(mkResourceVirtualEnvironmentVMNodeName) {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMNodeName, vmNodeName)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
|
||
vmAPI := api.Node(nodeName).VM(vmID)
|
||
|
||
// Retrieve the entire configuration in order to compare it to the state.
|
||
vmConfig, err := vmAPI.GetVM(ctx)
|
||
if err != nil {
|
||
if strings.Contains(err.Error(), "HTTP 404") ||
|
||
(strings.Contains(err.Error(), "HTTP 500") && strings.Contains(err.Error(), "does not exist")) {
|
||
d.SetId("")
|
||
|
||
return nil
|
||
}
|
||
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmStatus, err := vmAPI.GetVMStatus(ctx)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
return vmReadCustom(ctx, d, m, vmID, vmConfig, vmStatus)
|
||
}
|
||
|
||
// orderedListFromMap generates a list from a map's values. The values are sorted based on the map's keys.
|
||
func orderedListFromMap(inputMap map[string]interface{}) []interface{} {
|
||
itemCount := len(inputMap)
|
||
keyList := make([]string, itemCount)
|
||
i := 0
|
||
|
||
for key := range inputMap {
|
||
keyList[i] = key
|
||
i++
|
||
}
|
||
|
||
sort.Strings(keyList)
|
||
|
||
orderedList := make([]interface{}, itemCount)
|
||
for i, k := range keyList {
|
||
orderedList[i] = inputMap[k]
|
||
}
|
||
|
||
return orderedList
|
||
}
|
||
|
||
func vmReadCustom(
|
||
ctx context.Context,
|
||
d *schema.ResourceData,
|
||
m interface{},
|
||
vmID int,
|
||
vmConfig *vms.GetResponseData,
|
||
vmStatus *vms.GetStatusResponseData,
|
||
) diag.Diagnostics {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
|
||
api, err := config.GetClient()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
diags := vmReadPrimitiveValues(d, vmConfig, vmStatus)
|
||
if diags.HasError() {
|
||
return diags
|
||
}
|
||
|
||
// Fix terraform.tfstate, by replacing '-1' (the old default value) with actual vm_id value
|
||
if storedVMID := d.Get(mkResourceVirtualEnvironmentVMVMID).(int); storedVMID == -1 {
|
||
diags = append(diags, diag.Diagnostic{
|
||
Severity: diag.Warning,
|
||
Summary: fmt.Sprintf("VM %s has stored legacy vm_id %d, setting vm_id to its correct value %d.",
|
||
d.Id(), storedVMID, vmID),
|
||
})
|
||
|
||
err = d.Set(mkResourceVirtualEnvironmentVMVMID, vmID)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
clone := d.Get(mkResourceVirtualEnvironmentVMClone).([]interface{})
|
||
|
||
// Compare the agent configuration to the one stored in the state.
|
||
currentAgent := d.Get(mkResourceVirtualEnvironmentVMAgent).([]interface{})
|
||
|
||
if len(clone) == 0 || len(currentAgent) > 0 {
|
||
if vmConfig.Agent != nil {
|
||
agent := map[string]interface{}{}
|
||
|
||
if vmConfig.Agent.Enabled != nil {
|
||
agent[mkResourceVirtualEnvironmentVMAgentEnabled] = bool(*vmConfig.Agent.Enabled)
|
||
} else {
|
||
agent[mkResourceVirtualEnvironmentVMAgentEnabled] = false
|
||
}
|
||
|
||
if vmConfig.Agent.TrimClonedDisks != nil {
|
||
agent[mkResourceVirtualEnvironmentVMAgentTrim] = bool(
|
||
*vmConfig.Agent.TrimClonedDisks,
|
||
)
|
||
} else {
|
||
agent[mkResourceVirtualEnvironmentVMAgentTrim] = false
|
||
}
|
||
|
||
if len(currentAgent) > 0 {
|
||
currentAgentBlock := currentAgent[0].(map[string]interface{})
|
||
currentAgentTimeout := currentAgentBlock[mkResourceVirtualEnvironmentVMAgentTimeout].(string)
|
||
|
||
if currentAgentTimeout != "" {
|
||
agent[mkResourceVirtualEnvironmentVMAgentTimeout] = currentAgentTimeout
|
||
} else {
|
||
agent[mkResourceVirtualEnvironmentVMAgentTimeout] = dvResourceVirtualEnvironmentVMAgentTimeout
|
||
}
|
||
} else {
|
||
agent[mkResourceVirtualEnvironmentVMAgentTimeout] = dvResourceVirtualEnvironmentVMAgentTimeout
|
||
}
|
||
|
||
if vmConfig.Agent.Type != nil {
|
||
agent[mkResourceVirtualEnvironmentVMAgentType] = *vmConfig.Agent.Type
|
||
} else {
|
||
agent[mkResourceVirtualEnvironmentVMAgentType] = ""
|
||
}
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentAgent) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMAgent, []interface{}{agent})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(currentAgent) > 0 ||
|
||
agent[mkResourceVirtualEnvironmentVMAgentEnabled] != dvResourceVirtualEnvironmentVMAgentEnabled ||
|
||
agent[mkResourceVirtualEnvironmentVMAgentTimeout] != dvResourceVirtualEnvironmentVMAgentTimeout ||
|
||
agent[mkResourceVirtualEnvironmentVMAgentTrim] != dvResourceVirtualEnvironmentVMAgentTrim ||
|
||
agent[mkResourceVirtualEnvironmentVMAgentType] != dvResourceVirtualEnvironmentVMAgentType {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMAgent, []interface{}{agent})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(clone) > 0 {
|
||
if len(currentAgent) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMAgent, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMAgent, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
}
|
||
|
||
// Compare the audio devices to those stored in the state.
|
||
currentAudioDevice := d.Get(mkResourceVirtualEnvironmentVMAudioDevice).([]interface{})
|
||
|
||
audioDevices := make([]interface{}, 1)
|
||
audioDevicesArray := []*vms.CustomAudioDevice{
|
||
vmConfig.AudioDevice,
|
||
}
|
||
audioDevicesCount := 0
|
||
|
||
for adi, ad := range audioDevicesArray {
|
||
m := map[string]interface{}{}
|
||
|
||
if ad != nil {
|
||
m[mkResourceVirtualEnvironmentVMAudioDeviceDevice] = ad.Device
|
||
|
||
if ad.Driver != nil {
|
||
m[mkResourceVirtualEnvironmentVMAudioDeviceDriver] = *ad.Driver
|
||
} else {
|
||
m[mkResourceVirtualEnvironmentVMAudioDeviceDriver] = ""
|
||
}
|
||
|
||
m[mkResourceVirtualEnvironmentVMAudioDeviceEnabled] = true
|
||
|
||
audioDevicesCount = adi + 1
|
||
} else {
|
||
m[mkResourceVirtualEnvironmentVMAudioDeviceDevice] = ""
|
||
m[mkResourceVirtualEnvironmentVMAudioDeviceDriver] = ""
|
||
m[mkResourceVirtualEnvironmentVMAudioDeviceEnabled] = false
|
||
}
|
||
|
||
audioDevices[adi] = m
|
||
}
|
||
|
||
if len(clone) == 0 || len(currentAudioDevice) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMAudioDevice, audioDevices[:audioDevicesCount])
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the IDE devices to the CDROM configurations stored in the state.
|
||
currentInterface := dvResourceVirtualEnvironmentVMCDROMInterface
|
||
|
||
currentCDROM := d.Get(mkResourceVirtualEnvironmentVMCDROM).([]interface{})
|
||
if len(currentCDROM) > 0 {
|
||
currentBlock := currentCDROM[0].(map[string]interface{})
|
||
currentInterface = currentBlock[mkResourceVirtualEnvironmentVMCDROMInterface].(string)
|
||
}
|
||
|
||
cdromIDEDevice := getIdeDevice(vmConfig, currentInterface)
|
||
|
||
//nolint:nestif
|
||
if cdromIDEDevice != nil {
|
||
cdrom := make([]interface{}, 1)
|
||
cdromBlock := map[string]interface{}{}
|
||
|
||
if len(clone) == 0 || len(currentCDROM) > 0 {
|
||
cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled] = cdromIDEDevice.Enabled
|
||
cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID] = cdromIDEDevice.FileVolume
|
||
cdromBlock[mkResourceVirtualEnvironmentVMCDROMInterface] = currentInterface
|
||
|
||
if len(currentCDROM) > 0 {
|
||
currentBlock := currentCDROM[0].(map[string]interface{})
|
||
|
||
if currentBlock[mkResourceVirtualEnvironmentVMCDROMFileID] == "" {
|
||
cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID] = ""
|
||
}
|
||
|
||
if currentBlock[mkResourceVirtualEnvironmentVMCDROMEnabled] == false {
|
||
cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled] = false
|
||
}
|
||
}
|
||
|
||
cdrom[0] = cdromBlock
|
||
|
||
err := d.Set(mkResourceVirtualEnvironmentVMCDROM, cdrom)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
} else {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMCDROM, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the CPU configuration to the one stored in the state.
|
||
cpu := map[string]interface{}{}
|
||
|
||
if vmConfig.CPUArchitecture != nil {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUArchitecture] = *vmConfig.CPUArchitecture
|
||
} else {
|
||
// Default value of "arch" is "" according to the API documentation.
|
||
// However, assume the provider's default value as a workaround when the root account is not being used.
|
||
if !api.API().IsRootTicket() {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUArchitecture] = dvResourceVirtualEnvironmentVMCPUArchitecture
|
||
} else {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUArchitecture] = ""
|
||
}
|
||
}
|
||
|
||
if vmConfig.CPUCores != nil {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUCores] = *vmConfig.CPUCores
|
||
} else {
|
||
// Default value of "cores" is "1" according to the API documentation.
|
||
cpu[mkResourceVirtualEnvironmentVMCPUCores] = 1
|
||
}
|
||
|
||
if vmConfig.VirtualCPUCount != nil {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUHotplugged] = *vmConfig.VirtualCPUCount
|
||
} else {
|
||
// Default value of "vcpus" is "1" according to the API documentation.
|
||
cpu[mkResourceVirtualEnvironmentVMCPUHotplugged] = 0
|
||
}
|
||
|
||
if vmConfig.NUMAEnabled != nil {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUNUMA] = *vmConfig.NUMAEnabled
|
||
} else {
|
||
// Default value of "numa" is "false" according to the API documentation.
|
||
cpu[mkResourceVirtualEnvironmentVMCPUNUMA] = false
|
||
}
|
||
|
||
if vmConfig.CPUSockets != nil {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUSockets] = *vmConfig.CPUSockets
|
||
} else {
|
||
// Default value of "sockets" is "1" according to the API documentation.
|
||
cpu[mkResourceVirtualEnvironmentVMCPUSockets] = 1
|
||
}
|
||
|
||
if vmConfig.CPUEmulation != nil {
|
||
if vmConfig.CPUEmulation.Flags != nil {
|
||
convertedFlags := make([]interface{}, len(*vmConfig.CPUEmulation.Flags))
|
||
|
||
for fi, fv := range *vmConfig.CPUEmulation.Flags {
|
||
convertedFlags[fi] = fv
|
||
}
|
||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUFlags] = convertedFlags
|
||
} else {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUFlags] = []interface{}{}
|
||
}
|
||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUType] = vmConfig.CPUEmulation.Type
|
||
} else {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUFlags] = []interface{}{}
|
||
// Default value of "cputype" is "qemu64" according to the QEMU documentation.
|
||
cpu[mkResourceVirtualEnvironmentVMCPUType] = "qemu64"
|
||
}
|
||
|
||
if vmConfig.CPUUnits != nil {
|
||
cpu[mkResourceVirtualEnvironmentVMCPUUnits] = *vmConfig.CPUUnits
|
||
} else {
|
||
// Default value of "cpuunits" is "1024" according to the API documentation.
|
||
cpu[mkResourceVirtualEnvironmentVMCPUUnits] = 1024
|
||
}
|
||
|
||
currentCPU := d.Get(mkResourceVirtualEnvironmentVMCPU).([]interface{})
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentCPU) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMCPU, []interface{}{cpu})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(currentCPU) > 0 ||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUArchitecture] != dvResourceVirtualEnvironmentVMCPUArchitecture ||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUCores] != dvResourceVirtualEnvironmentVMCPUCores ||
|
||
len(cpu[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})) > 0 ||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUHotplugged] != dvResourceVirtualEnvironmentVMCPUHotplugged ||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUSockets] != dvResourceVirtualEnvironmentVMCPUSockets ||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUType] != dvResourceVirtualEnvironmentVMCPUType ||
|
||
cpu[mkResourceVirtualEnvironmentVMCPUUnits] != dvResourceVirtualEnvironmentVMCPUUnits {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMCPU, []interface{}{cpu})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentDiskList := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||
diskMap := map[string]interface{}{}
|
||
diskObjects := getDiskInfo(vmConfig, d)
|
||
|
||
for di, dd := range diskObjects {
|
||
disk := map[string]interface{}{}
|
||
|
||
if dd == nil || dd.FileVolume == "none" || strings.HasPrefix(di, "ide") {
|
||
continue
|
||
}
|
||
|
||
fileIDParts := strings.Split(dd.FileVolume, ":")
|
||
|
||
disk[mkResourceVirtualEnvironmentVMDiskDatastoreID] = fileIDParts[0]
|
||
|
||
if dd.Format == nil {
|
||
disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = dvResourceVirtualEnvironmentVMDiskFileFormat
|
||
// disk format may not be returned by config API if it is default for the storage, and that may be different
|
||
// from the default qcow2, so we need to read it from the storage API to make sure we have the correct value
|
||
files, err := api.Node(nodeName).ListDatastoreFiles(ctx, fileIDParts[0])
|
||
if err != nil {
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
continue
|
||
}
|
||
|
||
for _, v := range files {
|
||
if v.VolumeID == dd.FileVolume {
|
||
disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = v.FileFormat
|
||
break
|
||
}
|
||
}
|
||
} else {
|
||
disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = dd.Format
|
||
}
|
||
|
||
if dd.FileID != nil {
|
||
disk[mkResourceVirtualEnvironmentVMDiskFileID] = dd.FileID
|
||
}
|
||
|
||
disk[mkResourceVirtualEnvironmentVMDiskInterface] = di
|
||
disk[mkResourceVirtualEnvironmentVMDiskSize] = dd.Size.InGigabytes()
|
||
|
||
if dd.BurstableReadSpeedMbps != nil ||
|
||
dd.BurstableWriteSpeedMbps != nil ||
|
||
dd.MaxReadSpeedMbps != nil ||
|
||
dd.MaxWriteSpeedMbps != nil {
|
||
speed := map[string]interface{}{}
|
||
|
||
if dd.MaxReadSpeedMbps != nil {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedRead] = *dd.MaxReadSpeedMbps
|
||
} else {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedRead] = 0
|
||
}
|
||
|
||
if dd.BurstableReadSpeedMbps != nil {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable] = *dd.BurstableReadSpeedMbps
|
||
} else {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable] = 0
|
||
}
|
||
|
||
if dd.MaxWriteSpeedMbps != nil {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedWrite] = *dd.MaxWriteSpeedMbps
|
||
} else {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedWrite] = 0
|
||
}
|
||
|
||
if dd.BurstableWriteSpeedMbps != nil {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable] = *dd.BurstableWriteSpeedMbps
|
||
} else {
|
||
speed[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable] = 0
|
||
}
|
||
|
||
disk[mkResourceVirtualEnvironmentVMDiskSpeed] = []interface{}{speed}
|
||
} else {
|
||
disk[mkResourceVirtualEnvironmentVMDiskSpeed] = []interface{}{}
|
||
}
|
||
|
||
if dd.IOThread != nil {
|
||
disk[mkResourceVirtualEnvironmentVMDiskIOThread] = *dd.IOThread
|
||
} else {
|
||
disk[mkResourceVirtualEnvironmentVMDiskIOThread] = false
|
||
}
|
||
|
||
if dd.SSD != nil {
|
||
disk[mkResourceVirtualEnvironmentVMDiskSSD] = *dd.SSD
|
||
} else {
|
||
disk[mkResourceVirtualEnvironmentVMDiskSSD] = false
|
||
}
|
||
|
||
if dd.Discard != nil {
|
||
disk[mkResourceVirtualEnvironmentVMDiskDiscard] = *dd.Discard
|
||
} else {
|
||
disk[mkResourceVirtualEnvironmentVMDiskDiscard] = dvResourceVirtualEnvironmentVMDiskDiscard
|
||
}
|
||
|
||
if dd.Cache != nil {
|
||
disk[mkResourceVirtualEnvironmentVMDiskCache] = *dd.Cache
|
||
} else {
|
||
disk[mkResourceVirtualEnvironmentVMDiskCache] = dvResourceVirtualEnvironmentVMDiskCache
|
||
}
|
||
|
||
diskMap[di] = disk
|
||
}
|
||
|
||
if len(currentDiskList) > 0 {
|
||
orderedDiskList := orderedListFromMap(diskMap)
|
||
err := d.Set(mkResourceVirtualEnvironmentVMDisk, orderedDiskList)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
//nolint:nestif
|
||
if vmConfig.EFIDisk != nil {
|
||
efiDisk := map[string]interface{}{}
|
||
|
||
fileIDParts := strings.Split(vmConfig.EFIDisk.FileVolume, ":")
|
||
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID] = fileIDParts[0]
|
||
|
||
if vmConfig.EFIDisk.Format != nil {
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] = *vmConfig.EFIDisk.Format
|
||
} else {
|
||
// disk format may not be returned by config API if it is default for the storage, and that may be different
|
||
// from the default qcow2, so we need to read it from the storage API to make sure we have the correct value
|
||
files, err := api.Node(nodeName).ListDatastoreFiles(ctx, fileIDParts[0])
|
||
if err != nil {
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
} else {
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] = ""
|
||
for _, v := range files {
|
||
if v.VolumeID == vmConfig.EFIDisk.FileVolume {
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] = v.FileFormat
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if vmConfig.EFIDisk.Type != nil {
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskType] = *vmConfig.EFIDisk.Type
|
||
} else {
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskType] = dvResourceVirtualEnvironmentVMEFIDiskType
|
||
}
|
||
|
||
if vmConfig.EFIDisk.PreEnrolledKeys != nil {
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys] = *vmConfig.EFIDisk.PreEnrolledKeys
|
||
} else {
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys] = false
|
||
}
|
||
|
||
currentEfiDisk := d.Get(mkResourceVirtualEnvironmentVMEFIDisk).([]interface{})
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentEfiDisk) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMEFIDisk, []interface{}{efiDisk})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(currentEfiDisk) > 0 ||
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskDatastoreID] != dvResourceVirtualEnvironmentVMEFIDiskDatastoreID ||
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskType] != dvResourceVirtualEnvironmentVMEFIDiskType ||
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys] != dvResourceVirtualEnvironmentVMEFIDiskPreEnrolledKeys || //nolint:lll
|
||
efiDisk[mkResourceVirtualEnvironmentVMEFIDiskFileFormat] != dvResourceVirtualEnvironmentVMEFIDiskFileFormat {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMEFIDisk, []interface{}{efiDisk})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
}
|
||
|
||
currentPCIList := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
||
pciMap := map[string]interface{}{}
|
||
|
||
pciDevices := getPCIInfo(vmConfig, d)
|
||
for pi, pp := range pciDevices {
|
||
if (pp == nil) || (pp.DeviceIDs == nil && pp.Mapping == nil) {
|
||
continue
|
||
}
|
||
|
||
pci := map[string]interface{}{}
|
||
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDevice] = pi
|
||
if pp.DeviceIDs != nil {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceID] = strings.Join(*pp.DeviceIDs, ";")
|
||
} else {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceID] = ""
|
||
}
|
||
|
||
if pp.MDev != nil {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceMDev] = *pp.MDev
|
||
} else {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceMDev] = ""
|
||
}
|
||
|
||
if pp.PCIExpress != nil {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDevicePCIE] = *pp.PCIExpress
|
||
} else {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDevicePCIE] = false
|
||
}
|
||
|
||
if pp.ROMBAR != nil {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR] = *pp.ROMBAR
|
||
} else {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR] = false
|
||
}
|
||
|
||
if pp.ROMFile != nil {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile] = *pp.ROMFile
|
||
} else {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile] = ""
|
||
}
|
||
|
||
if pp.XVGA != nil {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA] = *pp.XVGA
|
||
} else {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA] = false
|
||
}
|
||
|
||
if pp.Mapping != nil {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceMapping] = *pp.Mapping
|
||
} else {
|
||
pci[mkResourceVirtualEnvironmentVMHostPCIDeviceMapping] = ""
|
||
}
|
||
|
||
pciMap[pi] = pci
|
||
}
|
||
|
||
if len(currentPCIList) > 0 {
|
||
// todo: reordering of devices by PVE may cause an issue here
|
||
orderedPCIList := orderedListFromMap(pciMap)
|
||
err := d.Set(mkResourceVirtualEnvironmentVMHostPCI, orderedPCIList)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the initialization configuration to the one stored in the state.
|
||
initialization := map[string]interface{}{}
|
||
|
||
initializationInterface := findExistingCloudInitDrive(vmConfig, vmID, "")
|
||
if initializationInterface != "" {
|
||
initializationDevice := getIdeDevice(vmConfig, initializationInterface)
|
||
fileVolumeParts := strings.Split(initializationDevice.FileVolume, ":")
|
||
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationInterface] = initializationInterface
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationDatastoreID] = fileVolumeParts[0]
|
||
}
|
||
|
||
if vmConfig.CloudInitDNSDomain != nil || vmConfig.CloudInitDNSServer != nil {
|
||
initializationDNS := map[string]interface{}{}
|
||
|
||
if vmConfig.CloudInitDNSDomain != nil {
|
||
initializationDNS[mkResourceVirtualEnvironmentVMInitializationDNSDomain] = *vmConfig.CloudInitDNSDomain
|
||
} else {
|
||
initializationDNS[mkResourceVirtualEnvironmentVMInitializationDNSDomain] = ""
|
||
}
|
||
|
||
if vmConfig.CloudInitDNSServer != nil {
|
||
initializationDNS[mkResourceVirtualEnvironmentVMInitializationDNSServer] = *vmConfig.CloudInitDNSServer
|
||
} else {
|
||
initializationDNS[mkResourceVirtualEnvironmentVMInitializationDNSServer] = ""
|
||
}
|
||
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationDNS] = []interface{}{
|
||
initializationDNS,
|
||
}
|
||
}
|
||
|
||
ipConfigLast := -1
|
||
ipConfigObjects := []*vms.CustomCloudInitIPConfig{
|
||
vmConfig.IPConfig0,
|
||
vmConfig.IPConfig1,
|
||
vmConfig.IPConfig2,
|
||
vmConfig.IPConfig3,
|
||
vmConfig.IPConfig4,
|
||
vmConfig.IPConfig5,
|
||
vmConfig.IPConfig6,
|
||
vmConfig.IPConfig7,
|
||
}
|
||
ipConfigList := make([]interface{}, len(ipConfigObjects))
|
||
|
||
for ipConfigIndex, ipConfig := range ipConfigObjects {
|
||
ipConfigItem := map[string]interface{}{}
|
||
|
||
if ipConfig != nil {
|
||
ipConfigLast = ipConfigIndex
|
||
|
||
if ipConfig.GatewayIPv4 != nil || ipConfig.IPv4 != nil {
|
||
ipv4 := map[string]interface{}{}
|
||
|
||
if ipConfig.IPv4 != nil {
|
||
ipv4[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Address] = *ipConfig.IPv4
|
||
} else {
|
||
ipv4[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Address] = ""
|
||
}
|
||
|
||
if ipConfig.GatewayIPv4 != nil {
|
||
ipv4[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Gateway] = *ipConfig.GatewayIPv4
|
||
} else {
|
||
ipv4[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4Gateway] = ""
|
||
}
|
||
|
||
ipConfigItem[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4] = []interface{}{
|
||
ipv4,
|
||
}
|
||
} else {
|
||
ipConfigItem[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4] = []interface{}{}
|
||
}
|
||
|
||
if ipConfig.GatewayIPv6 != nil || ipConfig.IPv6 != nil {
|
||
ipv6 := map[string]interface{}{}
|
||
|
||
if ipConfig.IPv6 != nil {
|
||
ipv6[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Address] = *ipConfig.IPv6
|
||
} else {
|
||
ipv6[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Address] = ""
|
||
}
|
||
|
||
if ipConfig.GatewayIPv6 != nil {
|
||
ipv6[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Gateway] = *ipConfig.GatewayIPv6
|
||
} else {
|
||
ipv6[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6Gateway] = ""
|
||
}
|
||
|
||
ipConfigItem[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6] = []interface{}{
|
||
ipv6,
|
||
}
|
||
} else {
|
||
ipConfigItem[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6] = []interface{}{}
|
||
}
|
||
} else {
|
||
ipConfigItem[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv4] = []interface{}{}
|
||
ipConfigItem[mkResourceVirtualEnvironmentVMInitializationIPConfigIPv6] = []interface{}{}
|
||
}
|
||
|
||
ipConfigList[ipConfigIndex] = ipConfigItem
|
||
}
|
||
|
||
if ipConfigLast >= 0 {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationIPConfig] = ipConfigList[:ipConfigLast+1]
|
||
}
|
||
|
||
//nolint:nestif
|
||
if vmConfig.CloudInitPassword != nil || vmConfig.CloudInitSSHKeys != nil ||
|
||
vmConfig.CloudInitUsername != nil {
|
||
initializationUserAccount := map[string]interface{}{}
|
||
|
||
if vmConfig.CloudInitSSHKeys != nil {
|
||
initializationUserAccount[mkResourceVirtualEnvironmentVMInitializationUserAccountKeys] = []string(
|
||
*vmConfig.CloudInitSSHKeys,
|
||
)
|
||
} else {
|
||
initializationUserAccount[mkResourceVirtualEnvironmentVMInitializationUserAccountKeys] = []string{}
|
||
}
|
||
|
||
if vmConfig.CloudInitPassword != nil {
|
||
initializationUserAccount[mkResourceVirtualEnvironmentVMInitializationUserAccountPassword] = *vmConfig.CloudInitPassword
|
||
} else {
|
||
initializationUserAccount[mkResourceVirtualEnvironmentVMInitializationUserAccountPassword] = ""
|
||
}
|
||
|
||
if vmConfig.CloudInitUsername != nil {
|
||
initializationUserAccount[mkResourceVirtualEnvironmentVMInitializationUserAccountUsername] = *vmConfig.CloudInitUsername
|
||
} else {
|
||
initializationUserAccount[mkResourceVirtualEnvironmentVMInitializationUserAccountUsername] = ""
|
||
}
|
||
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationUserAccount] = []interface{}{
|
||
initializationUserAccount,
|
||
}
|
||
}
|
||
|
||
if vmConfig.CloudInitFiles != nil {
|
||
if vmConfig.CloudInitFiles.UserVolume != nil {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationUserDataFileID] = *vmConfig.CloudInitFiles.UserVolume
|
||
} else {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationUserDataFileID] = ""
|
||
}
|
||
|
||
if vmConfig.CloudInitFiles.VendorVolume != nil {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationVendorDataFileID] = *vmConfig.CloudInitFiles.VendorVolume
|
||
} else {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationVendorDataFileID] = ""
|
||
}
|
||
|
||
if vmConfig.CloudInitFiles.NetworkVolume != nil {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID] = *vmConfig.CloudInitFiles.NetworkVolume
|
||
} else {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID] = ""
|
||
}
|
||
|
||
if vmConfig.CloudInitFiles.MetaVolume != nil {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationMetaDataFileID] = *vmConfig.CloudInitFiles.MetaVolume
|
||
} else {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationMetaDataFileID] = ""
|
||
}
|
||
} else if len(initialization) > 0 {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationUserDataFileID] = ""
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationVendorDataFileID] = ""
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID] = ""
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationMetaDataFileID] = ""
|
||
}
|
||
|
||
if vmConfig.CloudInitType != nil {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationType] = *vmConfig.CloudInitType
|
||
} else if len(initialization) > 0 {
|
||
initialization[mkResourceVirtualEnvironmentVMInitializationType] = ""
|
||
}
|
||
|
||
currentInitialization := d.Get(mkResourceVirtualEnvironmentVMInitialization).([]interface{})
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentInitialization) > 0 {
|
||
if len(initialization) > 0 {
|
||
err := d.Set(
|
||
mkResourceVirtualEnvironmentVMInitialization,
|
||
[]interface{}{initialization},
|
||
)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
} else {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMInitialization, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
}
|
||
} else if len(initialization) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMInitialization, []interface{}{initialization})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
} else {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMInitialization, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the operating system configuration to the one stored in the state.
|
||
kvmArguments := map[string]interface{}{}
|
||
|
||
if vmConfig.KVMArguments != nil {
|
||
kvmArguments[mkResourceVirtualEnvironmentVMKVMArguments] = *vmConfig.KVMArguments
|
||
} else {
|
||
kvmArguments[mkResourceVirtualEnvironmentVMKVMArguments] = ""
|
||
}
|
||
|
||
// Compare the memory configuration to the one stored in the state.
|
||
memory := map[string]interface{}{}
|
||
|
||
if vmConfig.DedicatedMemory != nil {
|
||
memory[mkResourceVirtualEnvironmentVMMemoryDedicated] = *vmConfig.DedicatedMemory
|
||
} else {
|
||
memory[mkResourceVirtualEnvironmentVMMemoryDedicated] = 0
|
||
}
|
||
|
||
if vmConfig.FloatingMemory != nil {
|
||
memory[mkResourceVirtualEnvironmentVMMemoryFloating] = *vmConfig.FloatingMemory
|
||
} else {
|
||
memory[mkResourceVirtualEnvironmentVMMemoryFloating] = 0
|
||
}
|
||
|
||
if vmConfig.SharedMemory != nil {
|
||
memory[mkResourceVirtualEnvironmentVMMemoryShared] = vmConfig.SharedMemory.Size
|
||
} else {
|
||
memory[mkResourceVirtualEnvironmentVMMemoryShared] = 0
|
||
}
|
||
|
||
currentMemory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentMemory) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMMemory, []interface{}{memory})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(currentMemory) > 0 ||
|
||
memory[mkResourceVirtualEnvironmentVMMemoryDedicated] != dvResourceVirtualEnvironmentVMMemoryDedicated ||
|
||
memory[mkResourceVirtualEnvironmentVMMemoryFloating] != dvResourceVirtualEnvironmentVMMemoryFloating ||
|
||
memory[mkResourceVirtualEnvironmentVMMemoryShared] != dvResourceVirtualEnvironmentVMMemoryShared {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMMemory, []interface{}{memory})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the network devices to those stored in the state.
|
||
currentNetworkDeviceList := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
||
|
||
macAddresses := make([]interface{}, 8)
|
||
networkDeviceLast := -1
|
||
networkDeviceList := make([]interface{}, 8)
|
||
networkDeviceObjects := []*vms.CustomNetworkDevice{
|
||
vmConfig.NetworkDevice0,
|
||
vmConfig.NetworkDevice1,
|
||
vmConfig.NetworkDevice2,
|
||
vmConfig.NetworkDevice3,
|
||
vmConfig.NetworkDevice4,
|
||
vmConfig.NetworkDevice5,
|
||
vmConfig.NetworkDevice6,
|
||
vmConfig.NetworkDevice7,
|
||
}
|
||
|
||
for ni, nd := range networkDeviceObjects {
|
||
networkDevice := map[string]interface{}{}
|
||
|
||
if nd != nil {
|
||
networkDeviceLast = ni
|
||
|
||
if nd.Bridge != nil {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceBridge] = *nd.Bridge
|
||
} else {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceBridge] = ""
|
||
}
|
||
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled] = nd.Enabled
|
||
|
||
if nd.Firewall != nil {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceFirewall] = *nd.Firewall
|
||
} else {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceFirewall] = false
|
||
}
|
||
|
||
if nd.MACAddress != nil {
|
||
macAddresses[ni] = *nd.MACAddress
|
||
} else {
|
||
macAddresses[ni] = ""
|
||
}
|
||
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress] = macAddresses[ni]
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceModel] = nd.Model
|
||
|
||
if nd.RateLimit != nil {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit] = *nd.RateLimit
|
||
} else {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit] = 0
|
||
}
|
||
|
||
if nd.Tag != nil {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceVLANID] = nd.Tag
|
||
} else {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceVLANID] = 0
|
||
}
|
||
if nd.MTU != nil {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceMTU] = nd.MTU
|
||
} else {
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceMTU] = 0
|
||
}
|
||
} else {
|
||
macAddresses[ni] = ""
|
||
networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled] = false
|
||
}
|
||
|
||
networkDeviceList[ni] = networkDevice
|
||
}
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentNetworkDeviceList) > 0 {
|
||
err := d.Set(
|
||
mkResourceVirtualEnvironmentVMMACAddresses,
|
||
macAddresses[0:len(currentNetworkDeviceList)],
|
||
)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
err = d.Set(
|
||
mkResourceVirtualEnvironmentVMNetworkDevice,
|
||
networkDeviceList[:networkDeviceLast+1],
|
||
)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMMACAddresses, macAddresses[0:len(currentNetworkDeviceList)])
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
|
||
if len(currentNetworkDeviceList) > 0 || networkDeviceLast > -1 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMNetworkDevice, networkDeviceList[:networkDeviceLast+1])
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
}
|
||
|
||
// Compare the operating system configuration to the one stored in the state.
|
||
operatingSystem := map[string]interface{}{}
|
||
|
||
if vmConfig.OSType != nil {
|
||
operatingSystem[mkResourceVirtualEnvironmentVMOperatingSystemType] = *vmConfig.OSType
|
||
} else {
|
||
operatingSystem[mkResourceVirtualEnvironmentVMOperatingSystemType] = ""
|
||
}
|
||
|
||
currentOperatingSystem := d.Get(mkResourceVirtualEnvironmentVMOperatingSystem).([]interface{})
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentOperatingSystem) > 0 {
|
||
err := d.Set(
|
||
mkResourceVirtualEnvironmentVMOperatingSystem,
|
||
[]interface{}{operatingSystem},
|
||
)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(currentOperatingSystem) > 0 ||
|
||
operatingSystem[mkResourceVirtualEnvironmentVMOperatingSystemType] != dvResourceVirtualEnvironmentVMOperatingSystemType {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMOperatingSystem, []interface{}{operatingSystem})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
} else {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMOperatingSystem, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the pool ID to the value stored in the state.
|
||
currentPoolID := d.Get(mkResourceVirtualEnvironmentVMPoolID).(string)
|
||
|
||
if len(clone) == 0 || currentPoolID != dvResourceVirtualEnvironmentVMPoolID {
|
||
if vmConfig.PoolID != nil {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMPoolID, *vmConfig.PoolID)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
}
|
||
|
||
// Compare the serial devices to those stored in the state.
|
||
serialDevices := make([]interface{}, 4)
|
||
serialDevicesArray := []*string{
|
||
vmConfig.SerialDevice0,
|
||
vmConfig.SerialDevice1,
|
||
vmConfig.SerialDevice2,
|
||
vmConfig.SerialDevice3,
|
||
}
|
||
serialDevicesCount := 0
|
||
|
||
for sdi, sd := range serialDevicesArray {
|
||
m := map[string]interface{}{}
|
||
|
||
if sd != nil {
|
||
m[mkResourceVirtualEnvironmentVMSerialDeviceDevice] = *sd
|
||
serialDevicesCount = sdi + 1
|
||
} else {
|
||
m[mkResourceVirtualEnvironmentVMSerialDeviceDevice] = ""
|
||
}
|
||
|
||
serialDevices[sdi] = m
|
||
}
|
||
|
||
currentSerialDevice := d.Get(mkResourceVirtualEnvironmentVMSerialDevice).([]interface{})
|
||
|
||
if len(clone) == 0 || len(currentSerialDevice) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMSerialDevice, serialDevices[:serialDevicesCount])
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the SMBIOS to the one stored in the state.
|
||
var smbios map[string]interface{}
|
||
|
||
//nolint:nestif
|
||
if vmConfig.SMBIOS != nil {
|
||
smbios = map[string]interface{}{}
|
||
|
||
if vmConfig.SMBIOS.Family != nil {
|
||
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Family)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSFamily] = string(b)
|
||
} else {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSFamily] = dvResourceVirtualEnvironmentVMSMBIOSFamily
|
||
}
|
||
|
||
if vmConfig.SMBIOS.Manufacturer != nil {
|
||
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Manufacturer)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSManufacturer] = string(b)
|
||
} else {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSManufacturer] = dvResourceVirtualEnvironmentVMSMBIOSManufacturer
|
||
}
|
||
|
||
if vmConfig.SMBIOS.Product != nil {
|
||
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Product)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSProduct] = string(b)
|
||
} else {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSProduct] = dvResourceVirtualEnvironmentVMSMBIOSProduct
|
||
}
|
||
|
||
if vmConfig.SMBIOS.Serial != nil {
|
||
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Serial)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSerial] = string(b)
|
||
} else {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSerial] = dvResourceVirtualEnvironmentVMSMBIOSSerial
|
||
}
|
||
|
||
if vmConfig.SMBIOS.SKU != nil {
|
||
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.SKU)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSKU] = string(b)
|
||
} else {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSKU] = dvResourceVirtualEnvironmentVMSMBIOSSKU
|
||
}
|
||
|
||
if vmConfig.SMBIOS.Version != nil {
|
||
b, err := base64.StdEncoding.DecodeString(*vmConfig.SMBIOS.Version)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSVersion] = string(b)
|
||
} else {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSVersion] = dvResourceVirtualEnvironmentVMSMBIOSVersion
|
||
}
|
||
|
||
if vmConfig.SMBIOS.UUID != nil {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSUUID] = *vmConfig.SMBIOS.UUID
|
||
} else {
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSUUID] = nil
|
||
}
|
||
}
|
||
|
||
currentSMBIOS := d.Get(mkResourceVirtualEnvironmentVMSMBIOS).([]interface{})
|
||
|
||
//nolint:gocritic
|
||
if len(clone) > 0 {
|
||
if len(currentSMBIOS) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMSMBIOS, currentSMBIOS)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(smbios) == 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMSMBIOS, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
} else if len(currentSMBIOS) > 0 ||
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSFamily] != dvResourceVirtualEnvironmentVMSMBIOSFamily ||
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSManufacturer] != dvResourceVirtualEnvironmentVMSMBIOSManufacturer ||
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSProduct] != dvResourceVirtualEnvironmentVMSMBIOSProduct ||
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSerial] != dvResourceVirtualEnvironmentVMSMBIOSSerial ||
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSSKU] != dvResourceVirtualEnvironmentVMSMBIOSSKU ||
|
||
smbios[mkResourceVirtualEnvironmentVMSMBIOSVersion] != dvResourceVirtualEnvironmentVMSMBIOSVersion {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMSMBIOS, []interface{}{smbios})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the startup order to the one stored in the state.
|
||
var startup map[string]interface{}
|
||
|
||
//nolint:nestif
|
||
if vmConfig.StartupOrder != nil {
|
||
startup = map[string]interface{}{}
|
||
|
||
if vmConfig.StartupOrder.Order != nil {
|
||
startup[mkResourceVirtualEnvironmentVMStartupOrder] = *vmConfig.StartupOrder.Order
|
||
} else {
|
||
startup[mkResourceVirtualEnvironmentVMStartupOrder] = dvResourceVirtualEnvironmentVMStartupOrder
|
||
}
|
||
|
||
if vmConfig.StartupOrder.Up != nil {
|
||
startup[mkResourceVirtualEnvironmentVMStartupUpDelay] = *vmConfig.StartupOrder.Up
|
||
} else {
|
||
startup[mkResourceVirtualEnvironmentVMStartupUpDelay] = dvResourceVirtualEnvironmentVMStartupUpDelay
|
||
}
|
||
|
||
if vmConfig.StartupOrder.Down != nil {
|
||
startup[mkResourceVirtualEnvironmentVMStartupDownDelay] = *vmConfig.StartupOrder.Down
|
||
} else {
|
||
startup[mkResourceVirtualEnvironmentVMStartupDownDelay] = dvResourceVirtualEnvironmentVMStartupDownDelay
|
||
}
|
||
}
|
||
|
||
currentStartup := d.Get(mkResourceVirtualEnvironmentVMStartup).([]interface{})
|
||
|
||
//nolint:gocritic
|
||
if len(clone) > 0 {
|
||
if len(currentStartup) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMStartup, []interface{}{startup})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(startup) == 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMStartup, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
} else if len(currentStartup) > 0 ||
|
||
startup[mkResourceVirtualEnvironmentVMStartupOrder] != mkResourceVirtualEnvironmentVMStartupOrder ||
|
||
startup[mkResourceVirtualEnvironmentVMStartupUpDelay] != dvResourceVirtualEnvironmentVMStartupUpDelay ||
|
||
startup[mkResourceVirtualEnvironmentVMStartupDownDelay] != dvResourceVirtualEnvironmentVMStartupDownDelay {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMStartup, []interface{}{startup})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare the VGA configuration to the one stored in the state.
|
||
vga := map[string]interface{}{}
|
||
|
||
if vmConfig.VGADevice != nil {
|
||
vgaEnabled := true
|
||
|
||
if vmConfig.VGADevice.Type != nil {
|
||
vgaEnabled = *vmConfig.VGADevice.Type != "none"
|
||
}
|
||
|
||
vga[mkResourceVirtualEnvironmentVMVGAEnabled] = vgaEnabled
|
||
|
||
if vmConfig.VGADevice.Memory != nil {
|
||
vga[mkResourceVirtualEnvironmentVMVGAMemory] = *vmConfig.VGADevice.Memory
|
||
} else {
|
||
vga[mkResourceVirtualEnvironmentVMVGAMemory] = 0
|
||
}
|
||
|
||
if vgaEnabled {
|
||
if vmConfig.VGADevice.Type != nil {
|
||
vga[mkResourceVirtualEnvironmentVMVGAType] = *vmConfig.VGADevice.Type
|
||
} else {
|
||
vga[mkResourceVirtualEnvironmentVMVGAType] = ""
|
||
}
|
||
}
|
||
} else {
|
||
vga[mkResourceVirtualEnvironmentVMVGAEnabled] = true
|
||
vga[mkResourceVirtualEnvironmentVMVGAMemory] = 0
|
||
vga[mkResourceVirtualEnvironmentVMVGAType] = ""
|
||
}
|
||
|
||
currentVGA := d.Get(mkResourceVirtualEnvironmentVMVGA).([]interface{})
|
||
|
||
if len(clone) > 0 {
|
||
if len(currentVGA) > 0 {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMVGA, []interface{}{vga})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
} else if len(currentVGA) > 0 ||
|
||
vga[mkResourceVirtualEnvironmentVMVGAEnabled] != dvResourceVirtualEnvironmentVMVGAEnabled ||
|
||
vga[mkResourceVirtualEnvironmentVMVGAMemory] != dvResourceVirtualEnvironmentVMVGAMemory ||
|
||
vga[mkResourceVirtualEnvironmentVMVGAType] != dvResourceVirtualEnvironmentVMVGAType {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMVGA, []interface{}{vga})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
} else {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMVGA, []interface{}{})
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
// Compare SCSI hardware type
|
||
scsiHardware := d.Get(mkResourceVirtualEnvironmentVMSCSIHardware).(string)
|
||
|
||
if len(clone) == 0 || scsiHardware != dvResourceVirtualEnvironmentVMSCSIHardware {
|
||
if vmConfig.SCSIHardware != nil {
|
||
err := d.Set(mkResourceVirtualEnvironmentVMSCSIHardware, *vmConfig.SCSIHardware)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
}
|
||
|
||
diags = append(
|
||
diags,
|
||
vmReadNetworkValues(ctx, d, m, vmID, vmConfig)...)
|
||
|
||
return diags
|
||
}
|
||
|
||
func vmReadNetworkValues(
|
||
ctx context.Context,
|
||
d *schema.ResourceData,
|
||
m interface{},
|
||
vmID int,
|
||
vmConfig *vms.GetResponseData,
|
||
) diag.Diagnostics {
|
||
var diags diag.Diagnostics
|
||
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
|
||
api, e := config.GetClient()
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
|
||
vmAPI := api.Node(nodeName).VM(vmID)
|
||
|
||
started := d.Get(mkResourceVirtualEnvironmentVMStarted).(bool)
|
||
|
||
var ipv4Addresses []interface{}
|
||
var ipv6Addresses []interface{}
|
||
var networkInterfaceNames []interface{}
|
||
|
||
if started {
|
||
if vmConfig.Agent != nil && vmConfig.Agent.Enabled != nil && *vmConfig.Agent.Enabled {
|
||
resource := VM()
|
||
agentBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMAgent},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
agentTimeout, err := time.ParseDuration(
|
||
agentBlock[mkResourceVirtualEnvironmentVMAgentTimeout].(string),
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
var macAddresses []interface{}
|
||
|
||
networkInterfaces, err := vmAPI.WaitForNetworkInterfacesFromVMAgent(ctx, int(agentTimeout.Seconds()), 5, true)
|
||
if err == nil && networkInterfaces.Result != nil {
|
||
ipv4Addresses = make([]interface{}, len(*networkInterfaces.Result))
|
||
ipv6Addresses = make([]interface{}, len(*networkInterfaces.Result))
|
||
macAddresses = make([]interface{}, len(*networkInterfaces.Result))
|
||
networkInterfaceNames = make([]interface{}, len(*networkInterfaces.Result))
|
||
|
||
for ri, rv := range *networkInterfaces.Result {
|
||
var rvIPv4Addresses []interface{}
|
||
var rvIPv6Addresses []interface{}
|
||
|
||
if rv.IPAddresses != nil {
|
||
for _, ip := range *rv.IPAddresses {
|
||
switch ip.Type {
|
||
case "ipv4":
|
||
rvIPv4Addresses = append(rvIPv4Addresses, ip.Address)
|
||
case "ipv6":
|
||
rvIPv6Addresses = append(rvIPv6Addresses, ip.Address)
|
||
}
|
||
}
|
||
}
|
||
|
||
ipv4Addresses[ri] = rvIPv4Addresses
|
||
ipv6Addresses[ri] = rvIPv6Addresses
|
||
macAddresses[ri] = strings.ToUpper(rv.MACAddress)
|
||
networkInterfaceNames[ri] = rv.Name
|
||
}
|
||
}
|
||
|
||
err = d.Set(mkResourceVirtualEnvironmentVMMACAddresses, macAddresses)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
}
|
||
|
||
e = d.Set(mkResourceVirtualEnvironmentVMIPv4Addresses, ipv4Addresses)
|
||
diags = append(diags, diag.FromErr(e)...)
|
||
e = d.Set(mkResourceVirtualEnvironmentVMIPv6Addresses, ipv6Addresses)
|
||
diags = append(diags, diag.FromErr(e)...)
|
||
e = d.Set(mkResourceVirtualEnvironmentVMNetworkInterfaceNames, networkInterfaceNames)
|
||
diags = append(diags, diag.FromErr(e)...)
|
||
|
||
return diags
|
||
}
|
||
|
||
func vmReadPrimitiveValues(
|
||
d *schema.ResourceData,
|
||
vmConfig *vms.GetResponseData,
|
||
vmStatus *vms.GetStatusResponseData,
|
||
) diag.Diagnostics {
|
||
var diags diag.Diagnostics
|
||
var err error
|
||
|
||
clone := d.Get(mkResourceVirtualEnvironmentVMClone).([]interface{})
|
||
currentACPI := d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)
|
||
|
||
//nolint:gosimple
|
||
if len(clone) == 0 || currentACPI != dvResourceVirtualEnvironmentVMACPI {
|
||
if vmConfig.ACPI != nil {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMACPI, bool(*vmConfig.ACPI))
|
||
} else {
|
||
// Default value of "acpi" is "1" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMACPI, true)
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentkvmArguments := d.Get(mkResourceVirtualEnvironmentVMKVMArguments).(string)
|
||
|
||
if len(clone) == 0 || currentkvmArguments != dvResourceVirtualEnvironmentVMKVMArguments {
|
||
// PVE API returns "args" as " " if it is set to empty.
|
||
if vmConfig.KVMArguments != nil && len(strings.TrimSpace(*vmConfig.KVMArguments)) > 0 {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMKVMArguments, *vmConfig.KVMArguments)
|
||
} else {
|
||
// Default value of "args" is "" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMKVMArguments, "")
|
||
}
|
||
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentBIOS := d.Get(mkResourceVirtualEnvironmentVMBIOS).(string)
|
||
|
||
if len(clone) == 0 || currentBIOS != dvResourceVirtualEnvironmentVMBIOS {
|
||
if vmConfig.BIOS != nil {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMBIOS, *vmConfig.BIOS)
|
||
} else {
|
||
// Default value of "bios" is "seabios" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMBIOS, "seabios")
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentDescription := d.Get(mkResourceVirtualEnvironmentVMDescription).(string)
|
||
|
||
if len(clone) == 0 || currentDescription != dvResourceVirtualEnvironmentVMDescription {
|
||
if vmConfig.Description != nil {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMDescription, *vmConfig.Description)
|
||
} else {
|
||
// Default value of "description" is "" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMDescription, "")
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentTags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{})
|
||
|
||
if len(clone) == 0 || len(currentTags) > 0 {
|
||
var tags []string
|
||
if vmConfig.Tags != nil {
|
||
for _, tag := range strings.Split(*vmConfig.Tags, ";") {
|
||
t := strings.TrimSpace(tag)
|
||
if len(t) > 0 {
|
||
tags = append(tags, t)
|
||
}
|
||
}
|
||
sort.Strings(tags)
|
||
}
|
||
err = d.Set(mkResourceVirtualEnvironmentVMTags, tags)
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentKeyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||
|
||
if len(clone) == 0 || currentKeyboardLayout != dvResourceVirtualEnvironmentVMKeyboardLayout {
|
||
if vmConfig.KeyboardLayout != nil {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMKeyboardLayout, *vmConfig.KeyboardLayout)
|
||
} else {
|
||
// Default value of "keyboard" is "" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMKeyboardLayout, "")
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentMachine := d.Get(mkResourceVirtualEnvironmentVMMachine).(string)
|
||
|
||
if len(clone) == 0 || currentMachine != dvResourceVirtualEnvironmentVMMachineType {
|
||
if vmConfig.Machine != nil {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMMachine, *vmConfig.Machine)
|
||
} else {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMMachine, "")
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentName := d.Get(mkResourceVirtualEnvironmentVMName).(string)
|
||
|
||
if len(clone) == 0 || currentName != dvResourceVirtualEnvironmentVMName {
|
||
if vmConfig.Name != nil {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMName, *vmConfig.Name)
|
||
} else {
|
||
// Default value of "name" is "" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMName, "")
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
if !d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool) {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMStarted, vmStatus.Status == "running")
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentTabletDevice := d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool)
|
||
|
||
//nolint:gosimple
|
||
if len(clone) == 0 || currentTabletDevice != dvResourceVirtualEnvironmentVMTabletDevice {
|
||
if vmConfig.TabletDeviceEnabled != nil {
|
||
err = d.Set(
|
||
mkResourceVirtualEnvironmentVMTabletDevice,
|
||
bool(*vmConfig.TabletDeviceEnabled),
|
||
)
|
||
} else {
|
||
// Default value of "tablet" is "1" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMTabletDevice, true)
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
currentTemplate := d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)
|
||
|
||
//nolint:gosimple
|
||
if len(clone) == 0 || currentTemplate != dvResourceVirtualEnvironmentVMTemplate {
|
||
if vmConfig.Template != nil {
|
||
err = d.Set(mkResourceVirtualEnvironmentVMTemplate, bool(*vmConfig.Template))
|
||
} else {
|
||
// Default value of "template" is "0" according to the API documentation.
|
||
err = d.Set(mkResourceVirtualEnvironmentVMTemplate, false)
|
||
}
|
||
diags = append(diags, diag.FromErr(err)...)
|
||
}
|
||
|
||
return diags
|
||
}
|
||
|
||
// vmUpdatePool moves the VM to the pool it is supposed to be in if the pool ID changed.
|
||
func vmUpdatePool(
|
||
ctx context.Context,
|
||
d *schema.ResourceData,
|
||
api *pools.Client,
|
||
vmID int,
|
||
) error {
|
||
oldPoolValue, newPoolValue := d.GetChange(mkResourceVirtualEnvironmentVMPoolID)
|
||
if cmp.Equal(newPoolValue, oldPoolValue) {
|
||
return nil
|
||
}
|
||
|
||
oldPool := oldPoolValue.(string)
|
||
newPool := newPoolValue.(string)
|
||
vmList := (types2.CustomCommaSeparatedList)([]string{strconv.Itoa(vmID)})
|
||
|
||
tflog.Debug(ctx, fmt.Sprintf("Moving VM %d from pool '%s' to pool '%s'", vmID, oldPool, newPool))
|
||
|
||
if oldPool != "" {
|
||
trueValue := types2.CustomBool(true)
|
||
poolUpdate := &pools.PoolUpdateRequestBody{
|
||
VMs: &vmList,
|
||
Delete: &trueValue,
|
||
}
|
||
|
||
err := api.UpdatePool(ctx, oldPool, poolUpdate)
|
||
if err != nil {
|
||
return fmt.Errorf("while removing VM %d from pool %s: %w", vmID, oldPool, err)
|
||
}
|
||
}
|
||
|
||
if newPool != "" {
|
||
poolUpdate := &pools.PoolUpdateRequestBody{VMs: &vmList}
|
||
|
||
err := api.UpdatePool(ctx, newPool, poolUpdate)
|
||
if err != nil {
|
||
return fmt.Errorf("while adding VM %d to pool %s: %w", vmID, newPool, err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
|
||
api, e := config.GetClient()
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
rebootRequired := false
|
||
|
||
vmID, e := strconv.Atoi(d.Id())
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
e = vmUpdatePool(ctx, d, api.Pool(), vmID)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
// If the node name has changed we need to migrate the VM to the new node before we do anything else.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMNodeName) {
|
||
oldNodeNameValue, _ := d.GetChange(mkResourceVirtualEnvironmentVMNodeName)
|
||
oldNodeName := oldNodeNameValue.(string)
|
||
vmAPI := api.Node(oldNodeName).VM(vmID)
|
||
|
||
migrateTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutMigrate).(int)
|
||
trueValue := types2.CustomBool(true)
|
||
migrateBody := &vms.MigrateRequestBody{
|
||
TargetNode: nodeName,
|
||
WithLocalDisks: &trueValue,
|
||
OnlineMigration: &trueValue,
|
||
}
|
||
|
||
err := vmAPI.MigrateVM(ctx, migrateBody, migrateTimeout)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
|
||
vmAPI := api.Node(nodeName).VM(vmID)
|
||
|
||
updateBody := &vms.UpdateRequestBody{
|
||
IDEDevices: vms.CustomStorageDevices{
|
||
"ide0": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
"ide1": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
"ide2": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
"ide3": vms.CustomStorageDevice{
|
||
Enabled: false,
|
||
},
|
||
},
|
||
}
|
||
|
||
var del []string
|
||
|
||
resource := VM()
|
||
|
||
// Retrieve the entire configuration as we need to process certain values.
|
||
vmConfig, e := vmAPI.GetVM(ctx)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
// Prepare the new primitive configuration values.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMACPI) {
|
||
acpi := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool))
|
||
updateBody.ACPI = &acpi
|
||
rebootRequired = true
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMKVMArguments) {
|
||
kvmArguments := d.Get(mkResourceVirtualEnvironmentVMKVMArguments).(string)
|
||
updateBody.KVMArguments = &kvmArguments
|
||
rebootRequired = true
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMBIOS) {
|
||
bios := d.Get(mkResourceVirtualEnvironmentVMBIOS).(string)
|
||
updateBody.BIOS = &bios
|
||
rebootRequired = true
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMDescription) {
|
||
description := d.Get(mkResourceVirtualEnvironmentVMDescription).(string)
|
||
updateBody.Description = &description
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMOnBoot) {
|
||
startOnBoot := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMOnBoot).(bool))
|
||
updateBody.StartOnBoot = &startOnBoot
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMTags) {
|
||
tagString := vmGetTagsString(d)
|
||
updateBody.Tags = &tagString
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMKeyboardLayout) {
|
||
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||
updateBody.KeyboardLayout = &keyboardLayout
|
||
rebootRequired = true
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMMachine) {
|
||
machine := d.Get(mkResourceVirtualEnvironmentVMMachine).(string)
|
||
updateBody.Machine = &machine
|
||
rebootRequired = true
|
||
}
|
||
|
||
name := d.Get(mkResourceVirtualEnvironmentVMName).(string)
|
||
|
||
if name == "" {
|
||
del = append(del, "name")
|
||
} else {
|
||
updateBody.Name = &name
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMTabletDevice) {
|
||
tabletDevice := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTabletDevice).(bool))
|
||
updateBody.TabletDeviceEnabled = &tabletDevice
|
||
rebootRequired = true
|
||
}
|
||
|
||
template := types2.CustomBool(d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool))
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMTemplate) {
|
||
updateBody.Template = &template
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new agent configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMAgent) {
|
||
agentBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMAgent},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
agentEnabled := types2.CustomBool(
|
||
agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool),
|
||
)
|
||
agentTrim := types2.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool))
|
||
agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string)
|
||
|
||
updateBody.Agent = &vms.CustomAgent{
|
||
Enabled: &agentEnabled,
|
||
TrimClonedDisks: &agentTrim,
|
||
Type: &agentType,
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new audio devices.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMAudioDevice) {
|
||
updateBody.AudioDevices = vmGetAudioDeviceList(d)
|
||
|
||
for i := 0; i < len(updateBody.AudioDevices); i++ {
|
||
if !updateBody.AudioDevices[i].Enabled {
|
||
del = append(del, fmt.Sprintf("audio%d", i))
|
||
}
|
||
}
|
||
|
||
for i := len(updateBody.AudioDevices); i < maxResourceVirtualEnvironmentVMAudioDevices; i++ {
|
||
del = append(del, fmt.Sprintf("audio%d", i))
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new boot configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMBootOrder) {
|
||
bootOrder := d.Get(mkResourceVirtualEnvironmentVMBootOrder).([]interface{})
|
||
bootOrderConverted := make([]string, len(bootOrder))
|
||
|
||
for i, device := range bootOrder {
|
||
bootOrderConverted[i] = device.(string)
|
||
}
|
||
|
||
updateBody.Boot = &vms.CustomBoot{
|
||
Order: &bootOrderConverted,
|
||
}
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new CD-ROM configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMCDROM) {
|
||
cdromBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMCDROM},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
cdromEnabled := cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled].(bool)
|
||
cdromFileID := cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID].(string)
|
||
cdromInterface := cdromBlock[mkResourceVirtualEnvironmentVMCDROMInterface].(string)
|
||
|
||
old, _ := d.GetChange(mkResourceVirtualEnvironmentVMCDROM)
|
||
|
||
if len(old.([]interface{})) > 0 {
|
||
oldList := old.([]interface{})[0]
|
||
oldBlock := oldList.(map[string]interface{})
|
||
|
||
// If the interface is not set, use the default, for backward compatibility.
|
||
oldInterface, ok := oldBlock[mkResourceVirtualEnvironmentVMCDROMInterface].(string)
|
||
if !ok || oldInterface == "" {
|
||
oldInterface = dvResourceVirtualEnvironmentVMCDROMInterface
|
||
}
|
||
|
||
if oldInterface != cdromInterface {
|
||
del = append(del, oldInterface)
|
||
}
|
||
}
|
||
|
||
if !cdromEnabled && cdromFileID == "" {
|
||
del = append(del, cdromInterface)
|
||
}
|
||
|
||
if cdromFileID == "" {
|
||
cdromFileID = "cdrom"
|
||
}
|
||
|
||
cdromMedia := "cdrom"
|
||
|
||
updateBody.IDEDevices[cdromInterface] = vms.CustomStorageDevice{
|
||
Enabled: cdromEnabled,
|
||
FileVolume: cdromFileID,
|
||
Media: &cdromMedia,
|
||
}
|
||
}
|
||
|
||
// Prepare the new CPU configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMCPU) {
|
||
cpuBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMCPU},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
cpuArchitecture := cpuBlock[mkResourceVirtualEnvironmentVMCPUArchitecture].(string)
|
||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
||
cpuFlags := cpuBlock[mkResourceVirtualEnvironmentVMCPUFlags].([]interface{})
|
||
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
||
cpuNUMA := types2.CustomBool(cpuBlock[mkResourceVirtualEnvironmentVMCPUNUMA].(bool))
|
||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||
cpuType := cpuBlock[mkResourceVirtualEnvironmentVMCPUType].(string)
|
||
cpuUnits := cpuBlock[mkResourceVirtualEnvironmentVMCPUUnits].(int)
|
||
|
||
// Only the root account is allowed to change the CPU architecture, which makes this check necessary.
|
||
if api.API().IsRootTicket() ||
|
||
cpuArchitecture != dvResourceVirtualEnvironmentVMCPUArchitecture {
|
||
updateBody.CPUArchitecture = &cpuArchitecture
|
||
}
|
||
|
||
updateBody.CPUCores = &cpuCores
|
||
updateBody.CPUSockets = &cpuSockets
|
||
updateBody.CPUUnits = &cpuUnits
|
||
updateBody.NUMAEnabled = &cpuNUMA
|
||
|
||
if cpuHotplugged > 0 {
|
||
updateBody.VirtualCPUCount = &cpuHotplugged
|
||
} else {
|
||
del = append(del, "vcpus")
|
||
}
|
||
|
||
cpuFlagsConverted := make([]string, len(cpuFlags))
|
||
|
||
for fi, flag := range cpuFlags {
|
||
cpuFlagsConverted[fi] = flag.(string)
|
||
}
|
||
|
||
updateBody.CPUEmulation = &vms.CustomCPUEmulation{
|
||
Flags: &cpuFlagsConverted,
|
||
Type: cpuType,
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new disk device configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMDisk) {
|
||
diskDeviceObjects, err := vmGetDiskDeviceObjects(d, nil)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
diskDeviceInfo := getDiskInfo(vmConfig, d)
|
||
|
||
for prefix, diskMap := range diskDeviceObjects {
|
||
if diskMap == nil {
|
||
continue
|
||
}
|
||
|
||
for key, value := range diskMap {
|
||
if diskDeviceInfo[key] == nil {
|
||
return diag.Errorf("missing %s device %s", prefix, key)
|
||
}
|
||
|
||
tmp := *diskDeviceInfo[key]
|
||
tmp.BurstableReadSpeedMbps = value.BurstableReadSpeedMbps
|
||
tmp.BurstableWriteSpeedMbps = value.BurstableWriteSpeedMbps
|
||
tmp.MaxReadSpeedMbps = value.MaxReadSpeedMbps
|
||
tmp.MaxWriteSpeedMbps = value.MaxWriteSpeedMbps
|
||
tmp.Cache = value.Cache
|
||
|
||
switch prefix {
|
||
case "virtio":
|
||
{
|
||
if updateBody.VirtualIODevices == nil {
|
||
updateBody.VirtualIODevices = vms.CustomStorageDevices{}
|
||
}
|
||
|
||
updateBody.VirtualIODevices[key] = tmp
|
||
}
|
||
case "sata":
|
||
{
|
||
if updateBody.SATADevices == nil {
|
||
updateBody.SATADevices = vms.CustomStorageDevices{}
|
||
}
|
||
|
||
updateBody.SATADevices[key] = tmp
|
||
}
|
||
case "scsi":
|
||
{
|
||
if updateBody.SCSIDevices == nil {
|
||
updateBody.SCSIDevices = vms.CustomStorageDevices{}
|
||
}
|
||
|
||
updateBody.SCSIDevices[key] = tmp
|
||
}
|
||
case "ide":
|
||
{
|
||
// Investigate whether to support IDE mapping.
|
||
}
|
||
default:
|
||
return diag.Errorf("device prefix %s not supported", prefix)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Prepare the new efi disk configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMEFIDisk) {
|
||
efiDisk := vmGetEfiDisk(d, nil)
|
||
|
||
updateBody.EFIDisk = efiDisk
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new cloud-init configuration.
|
||
stoppedBeforeUpdate := false
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMInitialization) {
|
||
initializationConfig := vmGetCloudInitConfig(d)
|
||
|
||
updateBody.CloudInitConfig = initializationConfig
|
||
|
||
if updateBody.CloudInitConfig != nil {
|
||
var fileVolume string
|
||
initialization := d.Get(mkResourceVirtualEnvironmentVMInitialization).([]interface{})
|
||
initializationBlock := initialization[0].(map[string]interface{})
|
||
initializationDatastoreID := initializationBlock[mkResourceVirtualEnvironmentVMInitializationDatastoreID].(string)
|
||
initializationInterface := initializationBlock[mkResourceVirtualEnvironmentVMInitializationInterface].(string)
|
||
cdromMedia := "cdrom"
|
||
|
||
existingInterface := findExistingCloudInitDrive(vmConfig, vmID, "")
|
||
if initializationInterface == "" && existingInterface == "" {
|
||
initializationInterface = "ide2"
|
||
} else if initializationInterface == "" {
|
||
initializationInterface = existingInterface
|
||
}
|
||
|
||
mustMove := existingInterface != "" && initializationInterface != existingInterface
|
||
if mustMove {
|
||
tflog.Debug(ctx, fmt.Sprintf("CloudInit must be moved from %s to %s", existingInterface, initializationInterface))
|
||
}
|
||
|
||
oldInit, _ := d.GetChange(mkResourceVirtualEnvironmentVMInitialization)
|
||
oldInitBlock := oldInit.([]interface{})[0].(map[string]interface{})
|
||
prevDatastoreID := oldInitBlock[mkResourceVirtualEnvironmentVMInitializationDatastoreID].(string)
|
||
|
||
mustChangeDatastore := prevDatastoreID != initializationDatastoreID
|
||
if mustChangeDatastore {
|
||
tflog.Debug(ctx, fmt.Sprintf("CloudInit must be moved from datastore %s to datastore %s",
|
||
prevDatastoreID, initializationDatastoreID))
|
||
}
|
||
|
||
if mustMove || mustChangeDatastore || existingInterface == "" {
|
||
// CloudInit must be moved, either from a device to another or from a datastore
|
||
// to another (or both). This requires the VM to be stopped.
|
||
if err := vmShutdown(ctx, vmAPI, d); err != nil {
|
||
return err
|
||
}
|
||
|
||
if err := deleteIdeDrives(ctx, vmAPI, initializationInterface, existingInterface); err != nil {
|
||
return err
|
||
}
|
||
|
||
stoppedBeforeUpdate = true
|
||
fileVolume = fmt.Sprintf("%s:cloudinit", initializationDatastoreID)
|
||
} else {
|
||
ideDevice := getIdeDevice(vmConfig, existingInterface)
|
||
fileVolume = ideDevice.FileVolume
|
||
}
|
||
|
||
updateBody.IDEDevices[initializationInterface] = vms.CustomStorageDevice{
|
||
Enabled: true,
|
||
FileVolume: fileVolume,
|
||
Media: &cdromMedia,
|
||
}
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new hostpci devices configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMHostPCI) {
|
||
updateBody.PCIDevices = vmGetHostPCIDeviceObjects(d)
|
||
|
||
for i := len(updateBody.PCIDevices); i < maxResourceVirtualEnvironmentVMHostPCIDevices; i++ {
|
||
del = append(del, fmt.Sprintf("hostpci%d", i))
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new memory configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMMemory) {
|
||
memoryBlock, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMMemory},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentVMMemoryDedicated].(int)
|
||
memoryFloating := memoryBlock[mkResourceVirtualEnvironmentVMMemoryFloating].(int)
|
||
memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int)
|
||
|
||
updateBody.DedicatedMemory = &memoryDedicated
|
||
updateBody.FloatingMemory = &memoryFloating
|
||
|
||
if memoryShared > 0 {
|
||
memorySharedName := fmt.Sprintf("vm-%d-ivshmem", vmID)
|
||
|
||
updateBody.SharedMemory = &vms.CustomSharedMemory{
|
||
Name: &memorySharedName,
|
||
Size: memoryShared,
|
||
}
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new network device configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMNetworkDevice) {
|
||
updateBody.NetworkDevices = vmGetNetworkDeviceObjects(d)
|
||
|
||
for i := 0; i < len(updateBody.NetworkDevices); i++ {
|
||
if !updateBody.NetworkDevices[i].Enabled {
|
||
del = append(del, fmt.Sprintf("net%d", i))
|
||
}
|
||
}
|
||
|
||
for i := len(updateBody.NetworkDevices); i < maxResourceVirtualEnvironmentVMNetworkDevices; i++ {
|
||
del = append(del, fmt.Sprintf("net%d", i))
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new operating system configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMOperatingSystem) {
|
||
operatingSystem, err := structure.GetSchemaBlock(
|
||
resource,
|
||
d,
|
||
[]string{mkResourceVirtualEnvironmentVMOperatingSystem},
|
||
0,
|
||
true,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
operatingSystemType := operatingSystem[mkResourceVirtualEnvironmentVMOperatingSystemType].(string)
|
||
|
||
updateBody.OSType = &operatingSystemType
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new serial devices.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMSerialDevice) {
|
||
updateBody.SerialDevices = vmGetSerialDeviceList(d)
|
||
|
||
for i := len(updateBody.SerialDevices); i < maxResourceVirtualEnvironmentVMSerialDevices; i++ {
|
||
del = append(del, fmt.Sprintf("serial%d", i))
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMSMBIOS) {
|
||
updateBody.SMBIOS = vmGetSMBIOS(d)
|
||
if updateBody.SMBIOS == nil {
|
||
del = append(del, "smbios1")
|
||
}
|
||
}
|
||
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMStartup) {
|
||
updateBody.StartupOrder = vmGetStartupOrder(d)
|
||
if updateBody.StartupOrder == nil {
|
||
del = append(del, "startup")
|
||
}
|
||
}
|
||
|
||
// Prepare the new VGA configuration.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMVGA) {
|
||
updateBody.VGADevice, e = vmGetVGADeviceObject(d)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Prepare the new SCSI hardware type
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMSCSIHardware) {
|
||
scsiHardware := d.Get(mkResourceVirtualEnvironmentVMSCSIHardware).(string)
|
||
updateBody.SCSIHardware = &scsiHardware
|
||
|
||
rebootRequired = true
|
||
}
|
||
|
||
// Update the configuration now that everything has been prepared.
|
||
updateBody.Delete = del
|
||
|
||
e = vmAPI.UpdateVM(ctx, updateBody)
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
// Determine if the state of the virtual machine state needs to be changed.
|
||
//nolint: nestif
|
||
if (d.HasChange(mkResourceVirtualEnvironmentVMStarted) || stoppedBeforeUpdate) && !bool(template) {
|
||
started := d.Get(mkResourceVirtualEnvironmentVMStarted).(bool)
|
||
if started {
|
||
if diags := vmStart(ctx, vmAPI, d); diags != nil {
|
||
return diags
|
||
}
|
||
} else {
|
||
if e := vmShutdown(ctx, vmAPI, d); e != nil {
|
||
return e
|
||
}
|
||
rebootRequired = false
|
||
}
|
||
}
|
||
|
||
// Change the disk locations and/or sizes, if necessary.
|
||
return vmUpdateDiskLocationAndSize(
|
||
ctx,
|
||
d,
|
||
m,
|
||
!bool(template) && rebootRequired,
|
||
)
|
||
}
|
||
|
||
func vmUpdateDiskLocationAndSize(
|
||
ctx context.Context,
|
||
d *schema.ResourceData,
|
||
m interface{},
|
||
reboot bool,
|
||
) diag.Diagnostics {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
api, err := config.GetClient()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
started := d.Get(mkResourceVirtualEnvironmentVMStarted).(bool)
|
||
template := d.Get(mkResourceVirtualEnvironmentVMTemplate).(bool)
|
||
vmID, err := strconv.Atoi(d.Id())
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmAPI := api.Node(nodeName).VM(vmID)
|
||
|
||
// Determine if any of the disks are changing location and/or size, and initiate the necessary actions.
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMDisk) {
|
||
diskOld, diskNew := d.GetChange(mkResourceVirtualEnvironmentVMDisk)
|
||
|
||
diskOldEntries, err := vmGetDiskDeviceObjects(
|
||
d,
|
||
diskOld.([]interface{}),
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
diskNewEntries, err := vmGetDiskDeviceObjects(
|
||
d,
|
||
diskNew.([]interface{}),
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
// Add efidisk if it has changes
|
||
if d.HasChange(mkResourceVirtualEnvironmentVMEFIDisk) {
|
||
diskOld, diskNew := d.GetChange(mkResourceVirtualEnvironmentVMEFIDisk)
|
||
|
||
oldEfiDisk, e := vmGetEfiDiskAsStorageDevice(d, diskOld.([]interface{}))
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
newEfiDisk, e := vmGetEfiDiskAsStorageDevice(d, diskNew.([]interface{}))
|
||
if e != nil {
|
||
return diag.FromErr(e)
|
||
}
|
||
|
||
if oldEfiDisk != nil {
|
||
baseDiskInterface := diskDigitPrefix(*oldEfiDisk.Interface)
|
||
diskOldEntries[baseDiskInterface][*oldEfiDisk.Interface] = *oldEfiDisk
|
||
}
|
||
|
||
if newEfiDisk != nil {
|
||
baseDiskInterface := diskDigitPrefix(*newEfiDisk.Interface)
|
||
diskNewEntries[baseDiskInterface][*newEfiDisk.Interface] = *newEfiDisk
|
||
}
|
||
|
||
if oldEfiDisk != nil && newEfiDisk != nil && oldEfiDisk.Size != newEfiDisk.Size {
|
||
return diag.Errorf(
|
||
"resizing of efidisks is not supported.",
|
||
)
|
||
}
|
||
}
|
||
|
||
var diskMoveBodies []*vms.MoveDiskRequestBody
|
||
|
||
var diskResizeBodies []*vms.ResizeDiskRequestBody
|
||
|
||
shutdownForDisksRequired := false
|
||
|
||
for prefix, diskMap := range diskOldEntries {
|
||
for oldKey, oldDisk := range diskMap {
|
||
if _, present := diskNewEntries[prefix][oldKey]; !present {
|
||
return diag.Errorf(
|
||
"deletion of disks not supported. Please delete disk by hand. Old Interface was %s",
|
||
*oldDisk.Interface,
|
||
)
|
||
}
|
||
|
||
if *oldDisk.ID != *diskNewEntries[prefix][oldKey].ID {
|
||
deleteOriginalDisk := types2.CustomBool(true)
|
||
|
||
diskMoveBodies = append(
|
||
diskMoveBodies,
|
||
&vms.MoveDiskRequestBody{
|
||
DeleteOriginalDisk: &deleteOriginalDisk,
|
||
Disk: *oldDisk.Interface,
|
||
TargetStorage: *diskNewEntries[prefix][oldKey].ID,
|
||
},
|
||
)
|
||
|
||
// Cannot be done while VM is running.
|
||
shutdownForDisksRequired = true
|
||
}
|
||
|
||
if *oldDisk.SizeInt < *diskNewEntries[prefix][oldKey].SizeInt {
|
||
diskResizeBodies = append(
|
||
diskResizeBodies,
|
||
&vms.ResizeDiskRequestBody{
|
||
Disk: *oldDisk.Interface,
|
||
Size: *diskNewEntries[prefix][oldKey].Size,
|
||
},
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
if shutdownForDisksRequired && !template {
|
||
if e := vmShutdown(ctx, vmAPI, d); e != nil {
|
||
return e
|
||
}
|
||
}
|
||
|
||
for _, reqBody := range diskMoveBodies {
|
||
moveDiskTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutMoveDisk).(int)
|
||
err = vmAPI.MoveVMDisk(ctx, reqBody, moveDiskTimeout)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
|
||
for _, reqBody := range diskResizeBodies {
|
||
err = vmAPI.ResizeVMDisk(ctx, reqBody)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
|
||
if shutdownForDisksRequired && started && !template {
|
||
if diags := vmStart(ctx, vmAPI, d); diags != nil {
|
||
return diags
|
||
}
|
||
|
||
// This concludes an equivalent of a reboot, avoid doing another.
|
||
reboot = false
|
||
}
|
||
}
|
||
|
||
// Perform a regular reboot in case it's necessary and haven't already been done.
|
||
if reboot {
|
||
vmStatus, err := vmAPI.GetVMStatus(ctx)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
if vmStatus.Status != "stopped" {
|
||
rebootTimeout := d.Get(mkResourceVirtualEnvironmentVMTimeoutReboot).(int)
|
||
|
||
err := vmAPI.RebootVM(
|
||
ctx,
|
||
&vms.RebootRequestBody{
|
||
Timeout: &rebootTimeout,
|
||
},
|
||
rebootTimeout+30,
|
||
)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
}
|
||
}
|
||
|
||
return vmRead(ctx, d, m)
|
||
}
|
||
|
||
func vmDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
|
||
config := m.(proxmoxtf.ProviderConfiguration)
|
||
api, err := config.GetClient()
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||
vmID, err := strconv.Atoi(d.Id())
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
vmAPI := api.Node(nodeName).VM(vmID)
|
||
|
||
// Shut down the virtual machine before deleting it.
|
||
status, err := vmAPI.GetVMStatus(ctx)
|
||
if err != nil {
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
if status.Status != "stopped" {
|
||
if e := vmShutdown(ctx, vmAPI, d); e != nil {
|
||
return e
|
||
}
|
||
}
|
||
|
||
err = vmAPI.DeleteVM(ctx)
|
||
|
||
if err != nil {
|
||
if strings.Contains(err.Error(), "HTTP 404") ||
|
||
(strings.Contains(err.Error(), "HTTP 500") && strings.Contains(err.Error(), "does not exist")) {
|
||
d.SetId("")
|
||
|
||
return nil
|
||
}
|
||
return diag.FromErr(err)
|
||
}
|
||
|
||
// Wait for the state to become unavailable as that clearly indicates the destruction of the VM.
|
||
err = vmAPI.WaitForVMState(ctx, "", 60, 2)
|
||
if err == nil {
|
||
return diag.Errorf("failed to delete VM \"%d\"", vmID)
|
||
}
|
||
|
||
d.SetId("")
|
||
|
||
return nil
|
||
}
|
||
|
||
func diskDigitPrefix(s string) string {
|
||
for i, r := range s {
|
||
if unicode.IsDigit(r) {
|
||
return s[:i]
|
||
}
|
||
}
|
||
|
||
return s
|
||
}
|
||
|
||
func getDiskInfo(resp *vms.GetResponseData, d *schema.ResourceData) map[string]*vms.CustomStorageDevice {
|
||
currentDisk := d.Get(mkResourceVirtualEnvironmentVMDisk)
|
||
|
||
currentDiskList := currentDisk.([]interface{})
|
||
currentDiskMap := map[string]map[string]interface{}{}
|
||
|
||
for _, v := range currentDiskList {
|
||
diskMap := v.(map[string]interface{})
|
||
diskInterface := diskMap[mkResourceVirtualEnvironmentVMDiskInterface].(string)
|
||
|
||
currentDiskMap[diskInterface] = diskMap
|
||
}
|
||
|
||
storageDevices := map[string]*vms.CustomStorageDevice{}
|
||
|
||
storageDevices["ide0"] = resp.IDEDevice0
|
||
storageDevices["ide1"] = resp.IDEDevice1
|
||
storageDevices["ide2"] = resp.IDEDevice2
|
||
storageDevices["ide3"] = resp.IDEDevice3
|
||
|
||
storageDevices["sata0"] = resp.SATADevice0
|
||
storageDevices["sata1"] = resp.SATADevice1
|
||
storageDevices["sata2"] = resp.SATADevice2
|
||
storageDevices["sata3"] = resp.SATADevice3
|
||
storageDevices["sata4"] = resp.SATADevice4
|
||
storageDevices["sata5"] = resp.SATADevice5
|
||
|
||
storageDevices["scsi0"] = resp.SCSIDevice0
|
||
storageDevices["scsi1"] = resp.SCSIDevice1
|
||
storageDevices["scsi2"] = resp.SCSIDevice2
|
||
storageDevices["scsi3"] = resp.SCSIDevice3
|
||
storageDevices["scsi4"] = resp.SCSIDevice4
|
||
storageDevices["scsi5"] = resp.SCSIDevice5
|
||
storageDevices["scsi6"] = resp.SCSIDevice6
|
||
storageDevices["scsi7"] = resp.SCSIDevice7
|
||
storageDevices["scsi8"] = resp.SCSIDevice8
|
||
storageDevices["scsi9"] = resp.SCSIDevice9
|
||
storageDevices["scsi10"] = resp.SCSIDevice10
|
||
storageDevices["scsi11"] = resp.SCSIDevice11
|
||
storageDevices["scsi12"] = resp.SCSIDevice12
|
||
storageDevices["scsi13"] = resp.SCSIDevice13
|
||
|
||
storageDevices["virtio0"] = resp.VirtualIODevice0
|
||
storageDevices["virtio1"] = resp.VirtualIODevice1
|
||
storageDevices["virtio2"] = resp.VirtualIODevice2
|
||
storageDevices["virtio3"] = resp.VirtualIODevice3
|
||
storageDevices["virtio4"] = resp.VirtualIODevice4
|
||
storageDevices["virtio5"] = resp.VirtualIODevice5
|
||
storageDevices["virtio6"] = resp.VirtualIODevice6
|
||
storageDevices["virtio7"] = resp.VirtualIODevice7
|
||
storageDevices["virtio8"] = resp.VirtualIODevice8
|
||
storageDevices["virtio9"] = resp.VirtualIODevice9
|
||
storageDevices["virtio10"] = resp.VirtualIODevice10
|
||
storageDevices["virtio11"] = resp.VirtualIODevice11
|
||
storageDevices["virtio12"] = resp.VirtualIODevice12
|
||
storageDevices["virtio13"] = resp.VirtualIODevice13
|
||
storageDevices["virtio14"] = resp.VirtualIODevice14
|
||
storageDevices["virtio15"] = resp.VirtualIODevice15
|
||
|
||
for k, v := range storageDevices {
|
||
if v != nil {
|
||
if currentDiskMap[k] != nil {
|
||
if currentDiskMap[k][mkResourceVirtualEnvironmentVMDiskFileID] != nil {
|
||
fileID := currentDiskMap[k][mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||
v.FileID = &fileID
|
||
}
|
||
}
|
||
// defensive copy of the loop variable
|
||
iface := k
|
||
v.Interface = &iface
|
||
}
|
||
}
|
||
|
||
return storageDevices
|
||
}
|
||
|
||
// getDiskDatastores returns a list of the used datastores in a VM.
|
||
func getDiskDatastores(vm *vms.GetResponseData, d *schema.ResourceData) []string {
|
||
storageDevices := getDiskInfo(vm, d)
|
||
datastoresSet := map[string]int{}
|
||
|
||
for _, diskInfo := range storageDevices {
|
||
// Ignore empty storage devices and storage devices (like ide) which may not have any media mounted
|
||
if diskInfo == nil || diskInfo.FileVolume == "none" {
|
||
continue
|
||
}
|
||
|
||
fileIDParts := strings.Split(diskInfo.FileVolume, ":")
|
||
datastoresSet[fileIDParts[0]] = 1
|
||
}
|
||
|
||
if vm.EFIDisk != nil {
|
||
fileIDParts := strings.Split(vm.EFIDisk.FileVolume, ":")
|
||
datastoresSet[fileIDParts[0]] = 1
|
||
}
|
||
|
||
datastores := []string{}
|
||
for datastore := range datastoresSet {
|
||
datastores = append(datastores, datastore)
|
||
}
|
||
|
||
return datastores
|
||
}
|
||
|
||
func getPCIInfo(resp *vms.GetResponseData, _ *schema.ResourceData) map[string]*vms.CustomPCIDevice {
|
||
pciDevices := map[string]*vms.CustomPCIDevice{}
|
||
|
||
pciDevices["hostpci0"] = resp.PCIDevice0
|
||
pciDevices["hostpci1"] = resp.PCIDevice1
|
||
pciDevices["hostpci2"] = resp.PCIDevice2
|
||
pciDevices["hostpci3"] = resp.PCIDevice3
|
||
|
||
return pciDevices
|
||
}
|
||
|
||
func parseImportIDWithNodeName(id string) (string, string, error) {
|
||
nodeName, id, found := strings.Cut(id, "/")
|
||
|
||
if !found {
|
||
return "", "", fmt.Errorf("unexpected format of ID (%s), expected node/id", id)
|
||
}
|
||
|
||
return nodeName, id, nil
|
||
}
|