From 993df2ae6ca40de5fb1fa87725c98271b5144fb1 Mon Sep 17 00:00:00 2001 From: Henri Williams Date: Tue, 24 Jan 2023 06:09:46 +0000 Subject: [PATCH 1/3] feat(vm): add support for 'boot_order and boot_disk flags for VM --- docs/resources/virtual_environment_vm.md | 2 ++ proxmoxtf/resource/vm.go | 22 +++++++++++++++++++--- proxmoxtf/resource/vm_test.go | 4 ++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/resources/virtual_environment_vm.md b/docs/resources/virtual_environment_vm.md index 5272d07b3..f9d3e483b 100644 --- a/docs/resources/virtual_environment_vm.md +++ b/docs/resources/virtual_environment_vm.md @@ -120,6 +120,8 @@ output "ubuntu_vm_public_key" { - `bios` - (Optional) The BIOS implementation (defaults to `seabios`). - `ovmf` - OVMF (UEFI). - `seabios` - SeaBIOS. +- `boot_order` - (Optional) Specify the guest boot order (defaults to `c`). +- `boot_disk` - (Optional) Enable booting from specified disk (deprecated). - `cdrom` - (Optional) The CDROM configuration. - `enabled` - (Optional) Whether to enable the CDROM drive (defaults to `false`). diff --git a/proxmoxtf/resource/vm.go b/proxmoxtf/resource/vm.go index a51179baf..ca19f9666 100644 --- a/proxmoxtf/resource/vm.go +++ b/proxmoxtf/resource/vm.go @@ -37,6 +37,8 @@ const ( dvResourceVirtualEnvironmentVMAudioDeviceDriver = "spice" dvResourceVirtualEnvironmentVMAudioDeviceEnabled = true dvResourceVirtualEnvironmentVMBIOS = "seabios" + dvResourceVirtualEnvironmentVMBootOrder = "c" + dvResourceVirtualEnvironmentVMBootDisk = "scsi0" dvResourceVirtualEnvironmentVMCDROMEnabled = false dvResourceVirtualEnvironmentVMCDROMFileID = "" dvResourceVirtualEnvironmentVMCloneDatastoreID = "" @@ -112,6 +114,8 @@ const ( mkResourceVirtualEnvironmentVMRebootAfterCreation = "reboot" mkResourceVirtualEnvironmentVMOnBoot = "on_boot" + mkResourceVirtualEnvironmentVMBootOrder = "boot_order" + mkResourceVirtualEnvironmentVMBootDisk = "boot_disk" mkResourceVirtualEnvironmentVMACPI = "acpi" mkResourceVirtualEnvironmentVMAgent = "agent" mkResourceVirtualEnvironmentVMAgentEnabled = "enabled" @@ -242,6 +246,18 @@ func VM() *schema.Resource { Optional: true, Default: dvResourceVirtualEnvironmentVMOnBoot, }, + mkResourceVirtualEnvironmentVMBootOrder: { + Type: schema.TypeString, + Description: "Specify the guest boot order", + Optional: true, + Default: dvResourceVirtualEnvironmentVMBootOrder, + }, + mkResourceVirtualEnvironmentVMBootDisk: { + Type: schema.TypeString, + Description: "Enable booting from specified disk", + Optional: true, + Default: dvResourceVirtualEnvironmentVMBootDisk, + }, mkResourceVirtualEnvironmentVMACPI: { Type: schema.TypeBool, Description: "Whether to enable ACPI", @@ -1915,10 +1931,10 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) var memorySharedObject *proxmox.CustomSharedMemory - bootDisk := "scsi0" - bootOrder := "c" + bootDisk := d.Get(mkResourceVirtualEnvironmentVMBootDisk).(string) + bootOrder := d.Get(mkResourceVirtualEnvironmentVMBootOrder).(string) - if cdromEnabled { + if cdromEnabled && bootOrder == dvResourceVirtualEnvironmentVMBootOrder { bootOrder = "cd" } diff --git a/proxmoxtf/resource/vm_test.go b/proxmoxtf/resource/vm_test.go index 12561f67c..889a86b10 100644 --- a/proxmoxtf/resource/vm_test.go +++ b/proxmoxtf/resource/vm_test.go @@ -38,6 +38,8 @@ func TestVMSchema(t *testing.T) { mkResourceVirtualEnvironmentVMAgent, mkResourceVirtualEnvironmentVMAudioDevice, mkResourceVirtualEnvironmentVMBIOS, + mkResourceVirtualEnvironmentVMBootOrder, + mkResourceVirtualEnvironmentVMBootDisk, mkResourceVirtualEnvironmentVMCDROM, mkResourceVirtualEnvironmentVMClone, mkResourceVirtualEnvironmentVMCPU, @@ -73,6 +75,8 @@ func TestVMSchema(t *testing.T) { mkResourceVirtualEnvironmentVMAgent: schema.TypeList, mkResourceVirtualEnvironmentVMAudioDevice: schema.TypeList, mkResourceVirtualEnvironmentVMBIOS: schema.TypeString, + mkResourceVirtualEnvironmentVMBootOrder: schema.TypeString, + mkResourceVirtualEnvironmentVMBootDisk: schema.TypeString, mkResourceVirtualEnvironmentVMCDROM: schema.TypeList, mkResourceVirtualEnvironmentVMCPU: schema.TypeList, mkResourceVirtualEnvironmentVMDescription: schema.TypeString, From 688dc0e6dab0dd57bc838ae1ae90876cbcfb0fee Mon Sep 17 00:00:00 2001 From: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Date: Sat, 18 Feb 2023 20:40:44 -0500 Subject: [PATCH 2/3] refactoring (1) --- proxmox/virtual_environment_vm_types.go | 35 ++++++++++++++++--------- proxmoxtf/resource/vm.go | 29 ++++++++------------ 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/proxmox/virtual_environment_vm_types.go b/proxmox/virtual_environment_vm_types.go index 1575a3c9c..09801aed1 100644 --- a/proxmox/virtual_environment_vm_types.go +++ b/proxmox/virtual_environment_vm_types.go @@ -33,6 +33,10 @@ type CustomAudioDevice struct { // CustomAudioDevices handles QEMU audio device parameters. type CustomAudioDevices []CustomAudioDevice +type CustomBoot struct { + Order *[]string `json:"order,omitempty" url:"order,omitempty,semicolon"` +} + // CustomCloudInitConfig handles QEMU cloud-init parameters. type CustomCloudInitConfig struct { Files *CustomCloudInitFiles `json:"cicustom,omitempty" url:"cicustom,omitempty"` @@ -236,18 +240,17 @@ type VirtualEnvironmentVMCreateRequestBody struct { BackupFile *string `json:"archive,omitempty" url:"archive,omitempty"` BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` BIOS *string `json:"bios,omitempty" url:"bios,omitempty"` - BootDisk *string `json:"bootdisk,omitempty" url:"bootdisk,omitempty"` - BootOrder *string `json:"boot,omitempty" url:"boot,omitempty"` - CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"` - CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"` - CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` - CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"` - CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` - CPUSockets *int `json:"sockets,omitempty" url:"sockets,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` - DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` - Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"` + Boot *CustomBoot `json:"boot,omitempty" url:"boot,omitempty"` + CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"` + CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"` + CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` + CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` + CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"` + CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` + CPUSockets *int `json:"sockets,omitempty" url:"sockets,omitempty"` + CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` + DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` + Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"` DeletionProtection *types.CustomBool `json:"protection,omitempty" url:"force,omitempty,int"` Description *string `json:"description,omitempty" url:"description,omitempty"` EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty" url:"efidisk0,omitempty"` @@ -646,6 +649,14 @@ func (r CustomAudioDevices) EncodeValues(key string, v *url.Values) error { return nil } +func (r CustomBoot) EncodeValues(key string, v *url.Values) error { + if r.Order != nil && len(*r.Order) > 0 { + v.Add(key, fmt.Sprintf("order=%s", strings.Join(*r.Order, ";"))) + } + + return nil +} + // EncodeValues converts a CustomCloudInitConfig struct to multiple URL values. func (r CustomCloudInitConfig) EncodeValues(_ string, v *url.Values) error { if r.Files != nil { diff --git a/proxmoxtf/resource/vm.go b/proxmoxtf/resource/vm.go index ca19f9666..cc8fdf3ef 100644 --- a/proxmoxtf/resource/vm.go +++ b/proxmoxtf/resource/vm.go @@ -37,8 +37,7 @@ const ( dvResourceVirtualEnvironmentVMAudioDeviceDriver = "spice" dvResourceVirtualEnvironmentVMAudioDeviceEnabled = true dvResourceVirtualEnvironmentVMBIOS = "seabios" - dvResourceVirtualEnvironmentVMBootOrder = "c" - dvResourceVirtualEnvironmentVMBootDisk = "scsi0" + dvResourceVirtualEnvironmentVMBootDevice = "scsi0" dvResourceVirtualEnvironmentVMCDROMEnabled = false dvResourceVirtualEnvironmentVMCDROMFileID = "" dvResourceVirtualEnvironmentVMCloneDatastoreID = "" @@ -115,7 +114,6 @@ const ( mkResourceVirtualEnvironmentVMRebootAfterCreation = "reboot" mkResourceVirtualEnvironmentVMOnBoot = "on_boot" mkResourceVirtualEnvironmentVMBootOrder = "boot_order" - mkResourceVirtualEnvironmentVMBootDisk = "boot_disk" mkResourceVirtualEnvironmentVMACPI = "acpi" mkResourceVirtualEnvironmentVMAgent = "agent" mkResourceVirtualEnvironmentVMAgentEnabled = "enabled" @@ -247,16 +245,13 @@ func VM() *schema.Resource { Default: dvResourceVirtualEnvironmentVMOnBoot, }, mkResourceVirtualEnvironmentVMBootOrder: { - Type: schema.TypeString, - Description: "Specify the guest boot order", - Optional: true, - Default: dvResourceVirtualEnvironmentVMBootOrder, - }, - mkResourceVirtualEnvironmentVMBootDisk: { - Type: schema.TypeString, - Description: "Enable booting from specified disk", + Type: schema.TypeList, + Description: "The guest will attempt to boot from devices in the order they appear here", Optional: true, - Default: dvResourceVirtualEnvironmentVMBootDisk, + Elem: &schema.Schema{Type: schema.TypeString}, + DefaultFunc: func() (interface{}, error) { + return []interface{}{}, nil + }, }, mkResourceVirtualEnvironmentVMACPI: { Type: schema.TypeBool, @@ -1931,15 +1926,13 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) var memorySharedObject *proxmox.CustomSharedMemory - bootDisk := d.Get(mkResourceVirtualEnvironmentVMBootDisk).(string) - bootOrder := d.Get(mkResourceVirtualEnvironmentVMBootOrder).(string) - - if cdromEnabled && bootOrder == dvResourceVirtualEnvironmentVMBootOrder { - bootOrder = "cd" + bootOrder := d.Get(mkResourceVirtualEnvironmentVMBootOrder).([]interface{}) + 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) } From 1bb68bb31e2a7b6a45ea579f15f3b47c799cd580 Mon Sep 17 00:00:00 2001 From: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:30:20 -0400 Subject: [PATCH 3/3] refactor to use only boot_order argument --- docs/resources/virtual_environment_vm.md | 7 ++-- proxmox/virtual_environment_vm_types.go | 47 +++++++++++++++++------ proxmoxtf/resource/vm.go | 49 ++++++++++++++++++++---- proxmoxtf/resource/vm_test.go | 4 +- 4 files changed, 82 insertions(+), 25 deletions(-) diff --git a/docs/resources/virtual_environment_vm.md b/docs/resources/virtual_environment_vm.md index f9d3e483b..c2e91d62e 100644 --- a/docs/resources/virtual_environment_vm.md +++ b/docs/resources/virtual_environment_vm.md @@ -120,8 +120,8 @@ output "ubuntu_vm_public_key" { - `bios` - (Optional) The BIOS implementation (defaults to `seabios`). - `ovmf` - OVMF (UEFI). - `seabios` - SeaBIOS. -- `boot_order` - (Optional) Specify the guest boot order (defaults to `c`). -- `boot_disk` - (Optional) Enable booting from specified disk (deprecated). +- `boot_order` - (Optional) Specify a list of devices to boot from in the order + they appear in the list (defaults to `[]`). - `cdrom` - (Optional) The CDROM configuration. - `enabled` - (Optional) Whether to enable the CDROM drive (defaults to `false`). @@ -363,7 +363,8 @@ output "ubuntu_vm_public_key" { - `device` - (Optional) The device (defaults to `socket`). - `/dev/*` - A host serial device. - `socket` - A unix socket. -- `scsi_hardware` - (Optional) The SCSI hardware type (defaults to `virtio-scsi-pci`). +- `scsi_hardware` - (Optional) The SCSI hardware type (defaults + to `virtio-scsi-pci`). - `lsi` - LSI Logic SAS1068E. - `lsi53c810` - LSI Logic 53C810. - `virtio-scsi-pci` - VirtIO SCSI. diff --git a/proxmox/virtual_environment_vm_types.go b/proxmox/virtual_environment_vm_types.go index 09801aed1..24825906f 100644 --- a/proxmox/virtual_environment_vm_types.go +++ b/proxmox/virtual_environment_vm_types.go @@ -240,17 +240,17 @@ type VirtualEnvironmentVMCreateRequestBody struct { BackupFile *string `json:"archive,omitempty" url:"archive,omitempty"` BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` BIOS *string `json:"bios,omitempty" url:"bios,omitempty"` - Boot *CustomBoot `json:"boot,omitempty" url:"boot,omitempty"` - CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"` - CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"` - CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` - CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"` - CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` - CPUSockets *int `json:"sockets,omitempty" url:"sockets,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` - DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` - Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"` + Boot *CustomBoot `json:"boot,omitempty" url:"boot,omitempty"` + CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"` + CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"` + CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` + CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` + CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"` + CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` + CPUSockets *int `json:"sockets,omitempty" url:"sockets,omitempty"` + CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` + DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` + Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"` DeletionProtection *types.CustomBool `json:"protection,omitempty" url:"force,omitempty,int"` Description *string `json:"description,omitempty" url:"description,omitempty"` EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty" url:"efidisk0,omitempty"` @@ -1310,6 +1310,31 @@ func (r *CustomAudioDevice) UnmarshalJSON(b []byte) error { return nil } +// UnmarshalJSON converts a CustomBoot string to an object. +func (r *CustomBoot) UnmarshalJSON(b []byte) error { + var s string + + err := json.Unmarshal(b, &s) + if err != nil { + return fmt.Errorf("error unmarshalling CustomBoot: %w", err) + } + + pairs := strings.Split(s, ",") + + for _, p := range pairs { + v := strings.Split(strings.TrimSpace(p), "=") + + if len(v) == 2 { + if v[0] == "order" { + v := strings.Split(strings.TrimSpace(v[1]), ";") + r.Order = &v + } + } + } + + return nil +} + // UnmarshalJSON converts a CustomCloudInitFiles string to an object. func (r *CustomCloudInitFiles) UnmarshalJSON(b []byte) error { var s string diff --git a/proxmoxtf/resource/vm.go b/proxmoxtf/resource/vm.go index cc8fdf3ef..ef0aad25c 100644 --- a/proxmoxtf/resource/vm.go +++ b/proxmoxtf/resource/vm.go @@ -37,7 +37,6 @@ const ( dvResourceVirtualEnvironmentVMAudioDeviceDriver = "spice" dvResourceVirtualEnvironmentVMAudioDeviceEnabled = true dvResourceVirtualEnvironmentVMBIOS = "seabios" - dvResourceVirtualEnvironmentVMBootDevice = "scsi0" dvResourceVirtualEnvironmentVMCDROMEnabled = false dvResourceVirtualEnvironmentVMCDROMFileID = "" dvResourceVirtualEnvironmentVMCloneDatastoreID = "" @@ -1926,10 +1925,30 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) var memorySharedObject *proxmox.CustomSharedMemory + var bootOrderConverted []string + if cdromEnabled { + bootOrderConverted = []string{"ide3"} + } bootOrder := d.Get(mkResourceVirtualEnvironmentVMBootOrder).([]interface{}) - bootOrderConverted := make([]string, len(bootOrder)) - for i, device := range bootOrder { - bootOrderConverted[i] = device.(string) + //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)) @@ -1968,10 +1987,11 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) TrimClonedDisks: &agentTrim, Type: &agentType, }, - AudioDevices: audioDevices, - BIOS: &bios, - BootDisk: &bootDisk, - BootOrder: &bootOrder, + AudioDevices: audioDevices, + BIOS: &bios, + Boot: &proxmox.CustomBoot{ + Order: &bootOrderConverted, + }, CloudInitConfig: initializationConfig, CPUCores: &cpuCores, CPUEmulation: &proxmox.CustomCPUEmulation{ @@ -4045,6 +4065,19 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D 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 = &proxmox.CustomBoot{ + Order: &bootOrderConverted, + } + rebootRequired = true + } + // Prepare the new CDROM configuration. if d.HasChange(mkResourceVirtualEnvironmentVMCDROM) { cdromBlock, err := getSchemaBlock( diff --git a/proxmoxtf/resource/vm_test.go b/proxmoxtf/resource/vm_test.go index 889a86b10..649c54938 100644 --- a/proxmoxtf/resource/vm_test.go +++ b/proxmoxtf/resource/vm_test.go @@ -39,7 +39,6 @@ func TestVMSchema(t *testing.T) { mkResourceVirtualEnvironmentVMAudioDevice, mkResourceVirtualEnvironmentVMBIOS, mkResourceVirtualEnvironmentVMBootOrder, - mkResourceVirtualEnvironmentVMBootDisk, mkResourceVirtualEnvironmentVMCDROM, mkResourceVirtualEnvironmentVMClone, mkResourceVirtualEnvironmentVMCPU, @@ -75,8 +74,7 @@ func TestVMSchema(t *testing.T) { mkResourceVirtualEnvironmentVMAgent: schema.TypeList, mkResourceVirtualEnvironmentVMAudioDevice: schema.TypeList, mkResourceVirtualEnvironmentVMBIOS: schema.TypeString, - mkResourceVirtualEnvironmentVMBootOrder: schema.TypeString, - mkResourceVirtualEnvironmentVMBootDisk: schema.TypeString, + mkResourceVirtualEnvironmentVMBootOrder: schema.TypeList, mkResourceVirtualEnvironmentVMCDROM: schema.TypeList, mkResourceVirtualEnvironmentVMCPU: schema.TypeList, mkResourceVirtualEnvironmentVMDescription: schema.TypeString,