diff --git a/docs/resources/virtual_environment_vm.md b/docs/resources/virtual_environment_vm.md index 16c4d868..ce3ece48 100644 --- a/docs/resources/virtual_environment_vm.md +++ b/docs/resources/virtual_environment_vm.md @@ -198,7 +198,15 @@ output "ubuntu_vm_public_key" { * `read_burstable` - (Optional) The maximum burstable read speed in megabytes per second. * `write` - (Optional) The maximum write speed in megabytes per second. * `write_burstable` - (Optional) The maximum burstable write speed in megabytes per second. - * ssd - (Optional) Whether to use an SSD emulation option for this disk (defaults to `false`). Note that SSD emulation is not supported on VirtIO Block drives. + * `ssd` - (Optional) Whether to use an SSD emulation option for this disk (defaults to `false`). Note that SSD emulation is not supported on VirtIO Block drives. +* `hostpci` - (Optional) A host PCI device mapping (multiple blocks supported). + * `device` - (Required) The PCI device name for Proxmox, in form of `hostpciX` where `X` is a sequential number from 0 to 3. + * `id` - (Required) The PCI device ID. + * `mdev` - (Optional) The mediated device ID to use. + * `pcie` - (Optional) Tells Proxmox to use a PCIe or PCI port. Some guests/device combination require PCIe rather than PCI. PCIe is only available for q35 machine types. + * `rombar` - (Optional) Makes the firmware ROM visible for the VM (defaults to `true`). + * `rom_file` - (Optional) A path to a ROM file for the device to use. This is a relative path under `/usr/share/kvm/`. + * `xvga` - (Optional) Marks the PCI(e) device as the primary GPU of the VM. With this enabled the `vga` configuration argument will be ignored. * `initialization` - (Optional) The cloud-init configuration. * `datastore_id` - (Optional) The identifier for the datastore to create the cloud-init disk in (defaults to `local-lvm`). @@ -245,6 +253,9 @@ output "ubuntu_vm_public_key" { * `sl` - Slovenian. * `sv` - Swedish. * `tr` - Turkish. +* `machine` - (Optional) The VM machine type (defaults to `i440fx`). + * `i440fx` - Standard PC (i440FX + PIIX, 1996). + * `q35` - Standard PC (Q35 + ICH9, 2009). * `memory` - (Optional) The memory configuration. * `dedicated` - (Optional) The dedicated memory in megabytes (defaults to `512`). * `floating` - (Optional) The floating memory in megabytes (defaults to `0`). diff --git a/proxmox/virtual_environment_vm_types.go b/proxmox/virtual_environment_vm_types.go index 656f1de5..c0fcd5eb 100644 --- a/proxmox/virtual_environment_vm_types.go +++ b/proxmox/virtual_environment_vm_types.go @@ -109,7 +109,7 @@ type CustomNUMADevices []CustomNUMADevice // CustomPCIDevice handles QEMU host PCI device mapping parameters. type CustomPCIDevice struct { DeviceIDs []string `json:"host" url:"host,semicolon"` - DevicePath *string `json:"mdev,omitempty" url:"mdev,omitempty"` + MDev *string `json:"mdev,omitempty" url:"mdev,omitempty"` PCIExpress *CustomBool `json:"pcie,omitempty" url:"pcie,omitempty,int"` ROMBAR *CustomBool `json:"rombar,omitempty" url:"rombar,omitempty,int"` ROMFile *string `json:"romfile,omitempty" url:"romfile,omitempty"` @@ -261,7 +261,7 @@ type VirtualEnvironmentVMCreateRequestBody struct { KVMEnabled *CustomBool `json:"kvm,omitempty" url:"kvm,omitempty,int"` LocalTime *CustomBool `json:"localtime,omitempty" url:"localtime,omitempty,int"` Lock *string `json:"lock,omitempty" url:"lock,omitempty"` - MachineType *string `json:"machine,omitempty" url:"machine,omitempty"` + Machine *string `json:"machine,omitempty" url:"machine,omitempty"` MigrateDowntime *float64 `json:"migrate_downtime,omitempty" url:"migrate_downtime,omitempty"` MigrateSpeed *int `json:"migrate_speed,omitempty" url:"migrate_speed,omitempty"` Name *string `json:"name,omitempty" url:"name,omitempty"` @@ -393,7 +393,7 @@ type VirtualEnvironmentVMGetResponseData struct { KVMEnabled *CustomBool `json:"kvm,omitempty"` LocalTime *CustomBool `json:"localtime,omitempty"` Lock *string `json:"lock,omitempty"` - MachineType *string `json:"machine,omitempty"` + Machine *string `json:"machine,omitempty"` MigrateDowntime *float64 `json:"migrate_downtime,omitempty"` MigrateSpeed *int `json:"migrate_speed,omitempty"` Name *string `json:"name,omitempty"` @@ -409,7 +409,10 @@ type VirtualEnvironmentVMGetResponseData struct { NUMAEnabled *CustomBool `json:"numa,omitempty"` OSType *string `json:"ostype,omitempty"` Overwrite *CustomBool `json:"force,omitempty"` - PCIDevices *CustomPCIDevices `json:"hostpci,omitempty"` + PCIDevice0 *CustomPCIDevice `json:"hostpci0,omitempty"` + PCIDevice1 *CustomPCIDevice `json:"hostpci1,omitempty"` + PCIDevice2 *CustomPCIDevice `json:"hostpci2,omitempty"` + PCIDevice3 *CustomPCIDevice `json:"hostpci3,omitempty"` PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` Revert *string `json:"revert,omitempty"` SATADevice0 *CustomStorageDevice `json:"sata0,omitempty"` @@ -877,8 +880,8 @@ func (r CustomPCIDevice) EncodeValues(key string, v *url.Values) error { fmt.Sprintf("host=%s", strings.Join(r.DeviceIDs, ";")), } - if r.DevicePath != nil { - values = append(values, fmt.Sprintf("mdev=%s", *r.DevicePath)) + if r.MDev != nil { + values = append(values, fmt.Sprintf("mdev=%s", *r.MDev)) } if r.PCIExpress != nil { @@ -1507,6 +1510,46 @@ func (r *CustomNetworkDevice) UnmarshalJSON(b []byte) error { return nil } +// UnmarshalJSON converts a CustomPCIDevice string to an object. +func (r *CustomPCIDevice) UnmarshalJSON(b []byte) error { + var s string + + err := json.Unmarshal(b, &s) + + if err != nil { + return err + } + + pairs := strings.Split(s, ",") + + for _, p := range pairs { + v := strings.Split(strings.TrimSpace(p), "=") + if len(v) == 1 { + r.DeviceIDs = strings.Split(v[1], ";") + } else if len(v) == 2 { + switch v[0] { + case "host": + r.DeviceIDs = strings.Split(v[1], ";") + case "mdev": + r.MDev = &v[1] + case "pcie": + bv := CustomBool(v[1] == "1") + r.PCIExpress = &bv + case "rombar": + bv := CustomBool(v[1] == "1") + r.ROMBAR = &bv + case "romfile": + r.ROMFile = &v[1] + case "x-vga": + bv := CustomBool(v[1] == "1") + r.XVGA = &bv + } + } + } + + return nil +} + // UnmarshalJSON converts a CustomSharedMemory string to an object. func (r *CustomSharedMemory) UnmarshalJSON(b []byte) error { var s string diff --git a/proxmoxtf/resource_virtual_environment_vm.go b/proxmoxtf/resource_virtual_environment_vm.go index 681287cb..5316aa8f 100644 --- a/proxmoxtf/resource_virtual_environment_vm.go +++ b/proxmoxtf/resource_virtual_environment_vm.go @@ -16,9 +16,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" - "github.com/bpg/terraform-provider-proxmox/proxmox" "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" ) const ( @@ -58,6 +59,13 @@ const ( dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable = 0 dvResourceVirtualEnvironmentVMDiskSpeedWrite = 0 dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = 0 + dvResourceVirtualEnvironmentVMHostPCIDevice = "" + dvResourceVirtualEnvironmentVMHostPCIDeviceID = "" + dvResourceVirtualEnvironmentVMHostPCIDeviceMDev = "" + dvResourceVirtualEnvironmentVMHostPCIDevicePCIE = 0 + dvResourceVirtualEnvironmentVMHostPCIDeviceROMBAR = 1 + dvResourceVirtualEnvironmentVMHostPCIDeviceROMFile = "" + dvResourceVirtualEnvironmentVMHostPCIDeviceXVGA = 0 dvResourceVirtualEnvironmentVMInitializationDatastoreID = "local-lvm" dvResourceVirtualEnvironmentVMInitializationDNSDomain = "" dvResourceVirtualEnvironmentVMInitializationDNSServer = "" @@ -70,6 +78,7 @@ const ( dvResourceVirtualEnvironmentVMInitializationVendorDataFileID = "" dvResourceVirtualEnvironmentVMInitializationType = "" dvResourceVirtualEnvironmentVMKeyboardLayout = "en-us" + dvResourceVirtualEnvironmentVMMachineType = "" dvResourceVirtualEnvironmentVMMemoryDedicated = 512 dvResourceVirtualEnvironmentVMMemoryFloating = 0 dvResourceVirtualEnvironmentVMMemoryShared = 0 @@ -147,6 +156,14 @@ const ( mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable" mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write" mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable" + mkResourceVirtualEnvironmentVMHostPCI = "hostpci" + mkResourceVirtualEnvironmentVMHostPCIDevice = "device" + mkResourceVirtualEnvironmentVMHostPCIDeviceID = "id" + mkResourceVirtualEnvironmentVMHostPCIDeviceMDev = "mdev" + mkResourceVirtualEnvironmentVMHostPCIDevicePCIE = "pcie" + mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR = "rombar" + mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile = "rom_file" + mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA = "xvga" mkResourceVirtualEnvironmentVMInitialization = "initialization" mkResourceVirtualEnvironmentVMInitializationDatastoreID = "datastore_id" mkResourceVirtualEnvironmentVMInitializationDNS = "dns" @@ -169,6 +186,7 @@ const ( mkResourceVirtualEnvironmentVMIPv4Addresses = "ipv4_addresses" mkResourceVirtualEnvironmentVMIPv6Addresses = "ipv6_addresses" mkResourceVirtualEnvironmentVMKeyboardLayout = "keyboard_layout" + mkResourceVirtualEnvironmentVMMachine = "machine" mkResourceVirtualEnvironmentVMMACAddresses = "mac_addresses" mkResourceVirtualEnvironmentVMMemory = "memory" mkResourceVirtualEnvironmentVMMemoryDedicated = "dedicated" @@ -795,6 +813,64 @@ func resourceVirtualEnvironmentVM() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, }, + mkResourceVirtualEnvironmentVMHostPCI: { + Type: schema.TypeList, + Description: "The Host PCI devices mapped to the VM", + Optional: true, + ForceNew: true, + DefaultFunc: func() (interface{}, error) { + return []interface{}{ + map[string]interface{}{ + mkResourceVirtualEnvironmentVMHostPCIDevice: dvResourceVirtualEnvironmentVMHostPCIDevice, + mkResourceVirtualEnvironmentVMHostPCIDeviceID: dvResourceVirtualEnvironmentVMHostPCIDeviceID, + mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA: dvResourceVirtualEnvironmentVMHostPCIDeviceXVGA, + mkResourceVirtualEnvironmentVMHostPCIDevicePCIE: dvResourceVirtualEnvironmentVMHostPCIDevicePCIE, + mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR: dvResourceVirtualEnvironmentVMHostPCIDeviceROMBAR, + mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile: dvResourceVirtualEnvironmentVMHostPCIDeviceROMFile, + mkResourceVirtualEnvironmentVMHostPCIDeviceMDev: dvResourceVirtualEnvironmentVMHostPCIDeviceMDev, + }, + }, 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)", + Required: 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", @@ -802,6 +878,12 @@ func resourceVirtualEnvironmentVM() *schema.Resource { Default: dvResourceVirtualEnvironmentVMKeyboardLayout, ValidateDiagFunc: getKeyboardLayoutValidator(), }, + 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", @@ -1273,6 +1355,7 @@ func resourceVirtualEnvironmentVMCreateClone(ctx context.Context, d *schema.Reso 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{}) @@ -1416,6 +1499,13 @@ func resourceVirtualEnvironmentVMCreateClone(ctx context.Context, d *schema.Reso updateBody.CloudInitConfig = initializationConfig } + if len(hostPCI) > 0 { + updateBody.PCIDevices, err = resourceVirtualEnvironmentVMGetHostPCIDeviceObjects(d) + if err != nil { + return diag.FromErr(err) + } + } + if len(cdrom) > 0 || len(initialization) > 0 { updateBody.IDEDevices = ideDevices } @@ -1702,6 +1792,11 @@ func resourceVirtualEnvironmentVMCreateCustom(ctx context.Context, d *schema.Res cdromCloudInitFileID = fmt.Sprintf("%s:cloudinit", initializationDatastoreID) } + pciDeviceObjects, err := resourceVirtualEnvironmentVMGetHostPCIDeviceObjects(d) + if err != nil { + return diag.FromErr(err) + } + keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string) memoryBlock, err := getSchemaBlock(resource, d, []string{mkResourceVirtualEnvironmentVMMemory}, 0, true) if err != nil { @@ -1712,6 +1807,7 @@ func resourceVirtualEnvironmentVMCreateCustom(ctx context.Context, d *schema.Res memoryFloating := memoryBlock[mkResourceVirtualEnvironmentVMMemoryFloating].(int) memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int) + machine := d.Get(mkResourceVirtualEnvironmentVMMachine).(string) name := d.Get(mkResourceVirtualEnvironmentVMName).(string) tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{}) @@ -1820,6 +1916,7 @@ func resourceVirtualEnvironmentVMCreateCustom(ctx context.Context, d *schema.Res KeyboardLayout: &keyboardLayout, NetworkDevices: networkDeviceObjects, OSType: &operatingSystemType, + PCIDevices: pciDeviceObjects, SCSIHardware: &scsiHardware, SerialDevices: serialDevices, SharedMemory: memorySharedObject, @@ -1860,6 +1957,10 @@ func resourceVirtualEnvironmentVMCreateCustom(ctx context.Context, d *schema.Res createBody.Tags = &tagsString } + if machine != "" { + createBody.Machine = &machine + } + if name != "" { createBody.Name = &name } @@ -2319,6 +2420,44 @@ func resourceVirtualEnvironmentVMGetDiskDeviceObjects(d *schema.ResourceData, di return diskDeviceObjects, nil } +func resourceVirtualEnvironmentVMGetHostPCIDeviceObjects(d *schema.ResourceData) (proxmox.CustomPCIDevices, error) { + pciDevice := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{}) + pciDeviceObjects := make(proxmox.CustomPCIDevices, len(pciDevice)) + + for i, pciDeviceEntry := range pciDevice { + block := pciDeviceEntry.(map[string]interface{}) + + ids, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceID].(string) + mdev, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceMDev].(string) + pcie := proxmox.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDevicePCIE].(bool)) + rombar := proxmox.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR].(bool)) + romfile, _ := block[mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile].(string) + xvga := proxmox.CustomBool(block[mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA].(bool)) + + device := proxmox.CustomPCIDevice{ + DeviceIDs: strings.Split(ids, ";"), + PCIExpress: &pcie, + ROMBAR: &rombar, + XVGA: &xvga, + } + if ids != "" { + device.DeviceIDs = strings.Split(ids, ";") + } + + if mdev != "" { + device.MDev = &mdev + } + + if romfile != "" { + device.ROMFile = &romfile + } + + pciDeviceObjects[i] = device + } + + return pciDeviceObjects, nil +} + func resourceVirtualEnvironmentVMGetNetworkDeviceObjects(d *schema.ResourceData) (proxmox.CustomNetworkDevices, error) { networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{}) networkDeviceObjects := make(proxmox.CustomNetworkDevices, len(networkDevice)) @@ -2840,6 +2979,75 @@ func resourceVirtualEnvironmentVMReadCustom(ctx context.Context, d *schema.Resou diags = append(diags, diag.FromErr(err)...) } + currentPCIList := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{}) + pciMap := map[string]interface{}{} + var orderedPCIList []interface{} + + pciDevices := getPCIInfo(vmConfig, d) + for pi, pp := range pciDevices { + if (pp == nil) || (pp.DeviceIDs == nil) { + continue + } + + pci := map[string]interface{}{} + + pci[mkResourceVirtualEnvironmentVMHostPCIDevice] = pi + pci[mkResourceVirtualEnvironmentVMHostPCIDeviceID] = strings.Join(pp.DeviceIDs, ";") + + 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 + } + + pciMap[pi] = pci + } + + keyList = []string{} + for key := range pciMap { + keyList = append(keyList, key) + } + sort.Strings(keyList) + + for _, k := range keyList { + orderedPCIList = append(orderedPCIList, pciMap[k]) + } + + if len(clone) > 0 { + if len(currentPCIList) > 0 { + err := d.Set(mkResourceVirtualEnvironmentVMHostPCI, orderedPCIList) + diags = append(diags, diag.FromErr(err)...) + } + } else if len(currentPCIList) > 0 { + // todo: reordering of devices by PVE may cause an issue here + 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{}{} @@ -3388,6 +3596,17 @@ func resourceVirtualEnvironmentVMReadPrimitiveValues(d *schema.ResourceData, vmC 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 { @@ -3504,6 +3723,12 @@ func resourceVirtualEnvironmentVMUpdate(ctx context.Context, d *schema.ResourceD rebootRequired = true } + if d.HasChange(mkResourceVirtualEnvironmentVMMachine) { + machine := d.Get(mkResourceVirtualEnvironmentVMMachine).(string) + updateBody.Machine = &machine + rebootRequired = true + } + name := d.Get(mkResourceVirtualEnvironmentVMName).(string) if name == "" { @@ -3731,6 +3956,16 @@ func resourceVirtualEnvironmentVMUpdate(ctx context.Context, d *schema.ResourceD rebootRequired = true } + // Prepare the new hostpci devices configuration. + if d.HasChange(mkResourceVirtualEnvironmentVMHostPCI) { + updateBody.PCIDevices, err = resourceVirtualEnvironmentVMGetHostPCIDeviceObjects(d) + if err != nil { + return diag.FromErr(err) + } + + rebootRequired = true + } + // Prepare the new memory configuration. if d.HasChange(mkResourceVirtualEnvironmentVMMemory) { memoryBlock, err := getSchemaBlock(resource, d, []string{mkResourceVirtualEnvironmentVMMemory}, 0, true) diff --git a/proxmoxtf/resource_virtual_environment_vm_test.go b/proxmoxtf/resource_virtual_environment_vm_test.go index 57df15d6..7894dd9d 100644 --- a/proxmoxtf/resource_virtual_environment_vm_test.go +++ b/proxmoxtf/resource_virtual_environment_vm_test.go @@ -38,7 +38,9 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) { mkResourceVirtualEnvironmentVMDescription, mkResourceVirtualEnvironmentVMDisk, mkResourceVirtualEnvironmentVMInitialization, + mkResourceVirtualEnvironmentVMHostPCI, mkResourceVirtualEnvironmentVMKeyboardLayout, + mkResourceVirtualEnvironmentVMMachine, mkResourceVirtualEnvironmentVMMemory, mkResourceVirtualEnvironmentVMName, mkResourceVirtualEnvironmentVMNetworkDevice, @@ -67,10 +69,12 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) { mkResourceVirtualEnvironmentVMCPU: schema.TypeList, mkResourceVirtualEnvironmentVMDescription: schema.TypeString, mkResourceVirtualEnvironmentVMDisk: schema.TypeList, + mkResourceVirtualEnvironmentVMHostPCI: schema.TypeList, mkResourceVirtualEnvironmentVMInitialization: schema.TypeList, mkResourceVirtualEnvironmentVMIPv4Addresses: schema.TypeList, mkResourceVirtualEnvironmentVMIPv6Addresses: schema.TypeList, mkResourceVirtualEnvironmentVMKeyboardLayout: schema.TypeString, + mkResourceVirtualEnvironmentVMMachine: schema.TypeString, mkResourceVirtualEnvironmentVMMemory: schema.TypeList, mkResourceVirtualEnvironmentVMName: schema.TypeString, mkResourceVirtualEnvironmentVMNetworkDevice: schema.TypeList, @@ -211,6 +215,25 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) { mkResourceVirtualEnvironmentVMInitializationUserAccount: schema.TypeList, }) + hostPCISchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMHostPCI) + + testOptionalArguments(t, hostPCISchema, []string{ + mkResourceVirtualEnvironmentVMHostPCIDeviceMDev, + mkResourceVirtualEnvironmentVMHostPCIDevicePCIE, + mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR, + mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile, + mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA, + }) + + testValueTypes(t, hostPCISchema, map[string]schema.ValueType{ + mkResourceVirtualEnvironmentVMHostPCIDevice: schema.TypeString, + mkResourceVirtualEnvironmentVMHostPCIDeviceMDev: schema.TypeString, + mkResourceVirtualEnvironmentVMHostPCIDevicePCIE: schema.TypeBool, + mkResourceVirtualEnvironmentVMHostPCIDeviceROMBAR: schema.TypeBool, + mkResourceVirtualEnvironmentVMHostPCIDeviceROMFile: schema.TypeString, + mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA: schema.TypeBool, + }) + initializationDNSSchema := testNestedSchemaExistence(t, initializationSchema, mkResourceVirtualEnvironmentVMInitializationDNS) testOptionalArguments(t, initializationDNSSchema, []string{ diff --git a/proxmoxtf/utils.go b/proxmoxtf/utils.go index 7f3c7bd3..c5e349a0 100644 --- a/proxmoxtf/utils.go +++ b/proxmoxtf/utils.go @@ -18,9 +18,10 @@ import ( "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/bpg/terraform-provider-proxmox/proxmox" "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" ) func getBIOSValidator() schema.SchemaValidateDiagFunc { @@ -519,6 +520,17 @@ func parseDiskSize(size *string) (int, error) { return diskSize, err } +func getPCIInfo(vm *proxmox.VirtualEnvironmentVMGetResponseData, d *schema.ResourceData) map[string]*proxmox.CustomPCIDevice { + pciDevices := map[string]*proxmox.CustomPCIDevice{} + + pciDevices["hostpci0"] = vm.PCIDevice0 + pciDevices["hostpci1"] = vm.PCIDevice1 + pciDevices["hostpci2"] = vm.PCIDevice2 + pciDevices["hostpci3"] = vm.PCIDevice3 + + return pciDevices +} + func getCloudInitTypeValidator() schema.SchemaValidateDiagFunc { return validation.ToDiagFunc(validation.StringInSlice([]string{ "configdrive2",