mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-08-23 03:48:35 +00:00
feat(vm): add support for USB devices passthrough (#666)
* feat: support usb devices for vm; fixes #665 Signed-off-by: Daniel Muehlbachler-Pietrzykowski <daniel@muehlbachler.io> * chore: fix linter errors Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> --------- Signed-off-by: Daniel Muehlbachler-Pietrzykowski <daniel@muehlbachler.io> Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
db842a2399
commit
cec4e65868
@ -301,6 +301,12 @@ output "ubuntu_vm_public_key" {
|
|||||||
is a relative path under `/usr/share/kvm/`.
|
is a relative path under `/usr/share/kvm/`.
|
||||||
- `xvga` - (Optional) Marks the PCI(e) device as the primary GPU of the VM.
|
- `xvga` - (Optional) Marks the PCI(e) device as the primary GPU of the VM.
|
||||||
With this enabled the `vga` configuration argument will be ignored.
|
With this enabled the `vga` configuration argument will be ignored.
|
||||||
|
- `usb` - (Optional) A host USB device mapping (multiple blocks supported).
|
||||||
|
- `host` - (Optional) The USB device ID. Use either this or `mapping`.
|
||||||
|
- `mapping` - (Optional) The resource mapping name of the device, for
|
||||||
|
example usbdevice. Use either this or `id`.
|
||||||
|
- `usb3` - (Optional) Makes the USB device a USB3 device for the VM (defaults
|
||||||
|
to `false`).
|
||||||
- `initialization` - (Optional) The cloud-init configuration.
|
- `initialization` - (Optional) The cloud-init configuration.
|
||||||
- `datastore_id` - (Optional) The identifier for the datastore to create the
|
- `datastore_id` - (Optional) The identifier for the datastore to create the
|
||||||
cloud-init disk in (defaults to `local-lvm`).
|
cloud-init disk in (defaults to `local-lvm`).
|
||||||
|
@ -165,9 +165,21 @@ resource "proxmox_virtual_environment_vm" "example" {
|
|||||||
# pcie = true
|
# pcie = true
|
||||||
#}
|
#}
|
||||||
|
|
||||||
|
#usb {
|
||||||
|
# host = "0000:1234"
|
||||||
|
# mapping = "usbdevice1"
|
||||||
|
# usb3 = false
|
||||||
|
#}
|
||||||
|
|
||||||
|
#usb {
|
||||||
|
# host = "0000:5678"
|
||||||
|
# mapping = "usbdevice2"
|
||||||
|
# usb3 = false
|
||||||
|
#}
|
||||||
|
|
||||||
# attached disks from data_vm
|
# attached disks from data_vm
|
||||||
dynamic "disk" {
|
dynamic "disk" {
|
||||||
for_each = {for idx, val in proxmox_virtual_environment_vm.data_vm.disk : idx => val}
|
for_each = { for idx, val in proxmox_virtual_environment_vm.data_vm.disk : idx => val }
|
||||||
iterator = data_disk
|
iterator = data_disk
|
||||||
content {
|
content {
|
||||||
datastore_id = data_disk.value["datastore_id"]
|
datastore_id = data_disk.value["datastore_id"]
|
||||||
|
@ -242,7 +242,8 @@ type CustomStorageDevices map[string]CustomStorageDevice
|
|||||||
|
|
||||||
// CustomUSBDevice handles QEMU USB device parameters.
|
// CustomUSBDevice handles QEMU USB device parameters.
|
||||||
type CustomUSBDevice struct {
|
type CustomUSBDevice struct {
|
||||||
HostDevice string `json:"host" url:"host"`
|
HostDevice *string `json:"host" url:"host"`
|
||||||
|
Mapping *string `json:"mapping,omitempty" url:"mapping,omitempty"`
|
||||||
USB3 *types.CustomBool `json:"usb3,omitempty" url:"usb3,omitempty,int"`
|
USB3 *types.CustomBool `json:"usb3,omitempty" url:"usb3,omitempty,int"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,7 +518,10 @@ type GetResponseData struct {
|
|||||||
Tags *string `json:"tags,omitempty"`
|
Tags *string `json:"tags,omitempty"`
|
||||||
Template *types.CustomBool `json:"template,omitempty"`
|
Template *types.CustomBool `json:"template,omitempty"`
|
||||||
TimeDriftFixEnabled *types.CustomBool `json:"tdf,omitempty"`
|
TimeDriftFixEnabled *types.CustomBool `json:"tdf,omitempty"`
|
||||||
USBDevices *CustomUSBDevices `json:"usb,omitempty"`
|
USBDevice0 *CustomUSBDevice `json:"usb0,omitempty"`
|
||||||
|
USBDevice1 *CustomUSBDevice `json:"usb1,omitempty"`
|
||||||
|
USBDevice2 *CustomUSBDevice `json:"usb2,omitempty"`
|
||||||
|
USBDevice3 *CustomUSBDevice `json:"usb3,omitempty"`
|
||||||
VGADevice *CustomVGADevice `json:"vga,omitempty"`
|
VGADevice *CustomVGADevice `json:"vga,omitempty"`
|
||||||
VirtualCPUCount *int `json:"vcpus,omitempty"`
|
VirtualCPUCount *int `json:"vcpus,omitempty"`
|
||||||
VirtualIODevice0 *CustomStorageDevice `json:"virtio0,omitempty"`
|
VirtualIODevice0 *CustomStorageDevice `json:"virtio0,omitempty"`
|
||||||
@ -1232,8 +1236,16 @@ func (r CustomStorageDevices) EncodeValues(_ string, v *url.Values) error {
|
|||||||
|
|
||||||
// EncodeValues converts a CustomUSBDevice struct to a URL vlaue.
|
// EncodeValues converts a CustomUSBDevice struct to a URL vlaue.
|
||||||
func (r CustomUSBDevice) EncodeValues(key string, v *url.Values) error {
|
func (r CustomUSBDevice) EncodeValues(key string, v *url.Values) error {
|
||||||
|
if r.HostDevice == nil && r.Mapping == nil {
|
||||||
|
return fmt.Errorf("either device ID or resource mapping must be set")
|
||||||
|
}
|
||||||
|
|
||||||
values := []string{
|
values := []string{
|
||||||
fmt.Sprintf("host=%s", r.HostDevice),
|
fmt.Sprintf("host=%s", *(r.HostDevice)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Mapping != nil {
|
||||||
|
values = append(values, fmt.Sprintf("mapping=%s", *r.Mapping))
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.USB3 != nil {
|
if r.USB3 != nil {
|
||||||
@ -1696,6 +1708,36 @@ func (r *CustomPCIDevice) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON converts a CustomUSBDevice string to an object.
|
||||||
|
func (r *CustomUSBDevice) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal CustomUSBDevice: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs := strings.Split(s, ",")
|
||||||
|
|
||||||
|
for _, p := range pairs {
|
||||||
|
v := strings.Split(strings.TrimSpace(p), "=")
|
||||||
|
if len(v) == 1 {
|
||||||
|
r.HostDevice = &v[1]
|
||||||
|
} else if len(v) == 2 {
|
||||||
|
switch v[0] {
|
||||||
|
case "host":
|
||||||
|
r.HostDevice = &v[1]
|
||||||
|
case "mapping":
|
||||||
|
r.Mapping = &v[1]
|
||||||
|
case "usb3":
|
||||||
|
bv := types.CustomBool(v[1] == "1")
|
||||||
|
r.USB3 = &bv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalJSON converts a CustomSharedMemory string to an object.
|
// UnmarshalJSON converts a CustomSharedMemory string to an object.
|
||||||
func (r *CustomSharedMemory) UnmarshalJSON(b []byte) error {
|
func (r *CustomSharedMemory) UnmarshalJSON(b []byte) error {
|
||||||
var s string
|
var s string
|
||||||
|
@ -124,3 +124,50 @@ func TestCustomPCIDevice_UnmarshalJSON(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCustomUSBDevice_UnmarshalJSON(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
line string
|
||||||
|
want *CustomUSBDevice
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "id only usb device",
|
||||||
|
line: `"host=0000:81"`,
|
||||||
|
want: &CustomUSBDevice{
|
||||||
|
HostDevice: types.StrPtr("0000:81"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "usb device with more details",
|
||||||
|
line: `"host=81:00,usb3=0"`,
|
||||||
|
want: &CustomUSBDevice{
|
||||||
|
HostDevice: types.StrPtr("81:00"),
|
||||||
|
USB3: types.BoolPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "usb device with mapping",
|
||||||
|
line: `"mapping=mappeddevice,usb=0"`,
|
||||||
|
want: &CustomUSBDevice{
|
||||||
|
HostDevice: nil,
|
||||||
|
Mapping: types.StrPtr("mappeddevice"),
|
||||||
|
USB3: types.BoolPtr(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
r := &CustomUSBDevice{}
|
||||||
|
if err := r.UnmarshalJSON([]byte(tt.line)); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -140,6 +140,7 @@ const (
|
|||||||
maxResourceVirtualEnvironmentVMNetworkDevices = 8
|
maxResourceVirtualEnvironmentVMNetworkDevices = 8
|
||||||
maxResourceVirtualEnvironmentVMSerialDevices = 4
|
maxResourceVirtualEnvironmentVMSerialDevices = 4
|
||||||
maxResourceVirtualEnvironmentVMHostPCIDevices = 8
|
maxResourceVirtualEnvironmentVMHostPCIDevices = 8
|
||||||
|
maxResourceVirtualEnvironmentVMHostUSBDevices = 4
|
||||||
|
|
||||||
mkResourceVirtualEnvironmentVMRebootAfterCreation = "reboot"
|
mkResourceVirtualEnvironmentVMRebootAfterCreation = "reboot"
|
||||||
mkResourceVirtualEnvironmentVMOnBoot = "on_boot"
|
mkResourceVirtualEnvironmentVMOnBoot = "on_boot"
|
||||||
@ -280,6 +281,10 @@ const (
|
|||||||
mkResourceVirtualEnvironmentVMTimeoutShutdownVM = "timeout_shutdown_vm"
|
mkResourceVirtualEnvironmentVMTimeoutShutdownVM = "timeout_shutdown_vm"
|
||||||
mkResourceVirtualEnvironmentVMTimeoutStartVM = "timeout_start_vm"
|
mkResourceVirtualEnvironmentVMTimeoutStartVM = "timeout_start_vm"
|
||||||
mkResourceVirtualEnvironmentVMTimeoutStopVM = "timeout_stop_vm"
|
mkResourceVirtualEnvironmentVMTimeoutStopVM = "timeout_stop_vm"
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSB = "usb"
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDevice = "host"
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDeviceMapping = "mapping"
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDeviceUSB3 = "usb3"
|
||||||
mkResourceVirtualEnvironmentVMVGA = "vga"
|
mkResourceVirtualEnvironmentVMVGA = "vga"
|
||||||
mkResourceVirtualEnvironmentVMVGAEnabled = "enabled"
|
mkResourceVirtualEnvironmentVMVGAEnabled = "enabled"
|
||||||
mkResourceVirtualEnvironmentVMVGAMemory = "memory"
|
mkResourceVirtualEnvironmentVMVGAMemory = "memory"
|
||||||
@ -1061,6 +1066,34 @@ func VM() *schema.Resource {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSB: {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Description: "The Host USB devices mapped to the VM",
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: false,
|
||||||
|
DefaultFunc: func() (interface{}, error) {
|
||||||
|
return []interface{}{}, nil
|
||||||
|
},
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDevice: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The USB device ID for Proxmox, in form of '<MANUFACTURER>:<ID>'",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDeviceMapping: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The resource mapping name of the device, for example usbdisk. Use either this or id.",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDeviceUSB3: {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Description: "Makes the USB device a USB3 device for the machine. Default is false",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout: {
|
mkResourceVirtualEnvironmentVMKeyboardLayout: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Description: "The keyboard layout",
|
Description: "The keyboard layout",
|
||||||
@ -1831,6 +1864,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
cpu := d.Get(mkResourceVirtualEnvironmentVMCPU).([]interface{})
|
cpu := d.Get(mkResourceVirtualEnvironmentVMCPU).([]interface{})
|
||||||
initialization := d.Get(mkResourceVirtualEnvironmentVMInitialization).([]interface{})
|
initialization := d.Get(mkResourceVirtualEnvironmentVMInitialization).([]interface{})
|
||||||
hostPCI := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
hostPCI := d.Get(mkResourceVirtualEnvironmentVMHostPCI).([]interface{})
|
||||||
|
hostUSB := d.Get(mkResourceVirtualEnvironmentVMHostUSB).([]interface{})
|
||||||
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||||||
memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
|
memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
|
||||||
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
||||||
@ -1997,6 +2031,10 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
|
|||||||
updateBody.PCIDevices = vmGetHostPCIDeviceObjects(d)
|
updateBody.PCIDevices = vmGetHostPCIDeviceObjects(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(hostUSB) > 0 {
|
||||||
|
updateBody.USBDevices = vmGetHostUSBDeviceObjects(d)
|
||||||
|
}
|
||||||
|
|
||||||
if len(cdrom) > 0 || len(initialization) > 0 {
|
if len(cdrom) > 0 || len(initialization) > 0 {
|
||||||
updateBody.IDEDevices = ideDevices
|
updateBody.IDEDevices = ideDevices
|
||||||
}
|
}
|
||||||
@ -2393,6 +2431,8 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
|
|
||||||
pciDeviceObjects := vmGetHostPCIDeviceObjects(d)
|
pciDeviceObjects := vmGetHostPCIDeviceObjects(d)
|
||||||
|
|
||||||
|
usbDeviceObjects := vmGetHostUSBDeviceObjects(d)
|
||||||
|
|
||||||
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||||||
memoryBlock, err := structure.GetSchemaBlock(
|
memoryBlock, err := structure.GetSchemaBlock(
|
||||||
resource,
|
resource,
|
||||||
@ -2562,6 +2602,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||||||
StartupOrder: startupOrder,
|
StartupOrder: startupOrder,
|
||||||
TabletDeviceEnabled: &tabletDevice,
|
TabletDeviceEnabled: &tabletDevice,
|
||||||
Template: &template,
|
Template: &template,
|
||||||
|
USBDevices: usbDeviceObjects,
|
||||||
VGADevice: vgaDevice,
|
VGADevice: vgaDevice,
|
||||||
VMID: &vmID,
|
VMID: &vmID,
|
||||||
}
|
}
|
||||||
@ -3241,6 +3282,31 @@ func vmGetHostPCIDeviceObjects(d *schema.ResourceData) vms.CustomPCIDevices {
|
|||||||
return pciDeviceObjects
|
return pciDeviceObjects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vmGetHostUSBDeviceObjects(d *schema.ResourceData) vms.CustomUSBDevices {
|
||||||
|
usbDevice := d.Get(mkResourceVirtualEnvironmentVMHostUSB).([]interface{})
|
||||||
|
usbDeviceObjects := make(vms.CustomUSBDevices, len(usbDevice))
|
||||||
|
|
||||||
|
for i, usbDeviceEntry := range usbDevice {
|
||||||
|
block := usbDeviceEntry.(map[string]interface{})
|
||||||
|
|
||||||
|
host, _ := block[mkResourceVirtualEnvironmentVMHostUSBDevice].(string)
|
||||||
|
usb3 := types.CustomBool(block[mkResourceVirtualEnvironmentVMHostUSBDeviceUSB3].(bool))
|
||||||
|
mapping, _ := block[mkResourceVirtualEnvironmentVMHostUSBDeviceMapping].(string)
|
||||||
|
|
||||||
|
device := vms.CustomUSBDevice{
|
||||||
|
HostDevice: &host,
|
||||||
|
USB3: &usb3,
|
||||||
|
}
|
||||||
|
if mapping != "" {
|
||||||
|
device.Mapping = &mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
usbDeviceObjects[i] = device
|
||||||
|
}
|
||||||
|
|
||||||
|
return usbDeviceObjects
|
||||||
|
}
|
||||||
|
|
||||||
func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices {
|
func vmGetNetworkDeviceObjects(d *schema.ResourceData) vms.CustomNetworkDevices {
|
||||||
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
||||||
networkDeviceObjects := make(vms.CustomNetworkDevices, len(networkDevice))
|
networkDeviceObjects := make(vms.CustomNetworkDevices, len(networkDevice))
|
||||||
@ -3417,33 +3483,38 @@ func vmGetStartupOrder(d *schema.ResourceData) *vms.CustomStartupOrder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func vmGetTagsString(d *schema.ResourceData) string {
|
func vmGetTagsString(d *schema.ResourceData) string {
|
||||||
tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{})
|
|
||||||
var sanitizedTags []string
|
var sanitizedTags []string
|
||||||
|
|
||||||
|
tags := d.Get(mkResourceVirtualEnvironmentVMTags).([]interface{})
|
||||||
for i := 0; i < len(tags); i++ {
|
for i := 0; i < len(tags); i++ {
|
||||||
tag := strings.TrimSpace(tags[i].(string))
|
tag := strings.TrimSpace(tags[i].(string))
|
||||||
if len(tag) > 0 {
|
if len(tag) > 0 {
|
||||||
sanitizedTags = append(sanitizedTags, tag)
|
sanitizedTags = append(sanitizedTags, tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(sanitizedTags)
|
sort.Strings(sanitizedTags)
|
||||||
|
|
||||||
return strings.Join(sanitizedTags, ";")
|
return strings.Join(sanitizedTags, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
func vmGetSerialDeviceValidator() schema.SchemaValidateDiagFunc {
|
func vmGetSerialDeviceValidator() schema.SchemaValidateDiagFunc {
|
||||||
return validation.ToDiagFunc(func(i interface{}, k string) (s []string, es []error) {
|
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
|
||||||
v, ok := i.(string)
|
v, ok := i.(string)
|
||||||
|
|
||||||
|
var es []error
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
es = append(es, fmt.Errorf("expected type of %s to be string", k))
|
es = append(es, fmt.Errorf("expected type of %s to be string", k))
|
||||||
return
|
return nil, es
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(v, "/dev/") && v != "socket" {
|
if !strings.HasPrefix(v, "/dev/") && v != "socket" {
|
||||||
es = append(es, fmt.Errorf("expected %s to be '/dev/*' or 'socket'", k))
|
es = append(es, fmt.Errorf("expected %s to be '/dev/*' or 'socket'", k))
|
||||||
return
|
return nil, es
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return nil, es
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4068,12 +4139,50 @@ func vmReadCustom(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(currentPCIList) > 0 {
|
if len(currentPCIList) > 0 {
|
||||||
// todo: reordering of devices by PVE may cause an issue here
|
|
||||||
orderedPCIList := orderedListFromMap(pciMap)
|
orderedPCIList := orderedListFromMap(pciMap)
|
||||||
err := d.Set(mkResourceVirtualEnvironmentVMHostPCI, orderedPCIList)
|
err := d.Set(mkResourceVirtualEnvironmentVMHostPCI, orderedPCIList)
|
||||||
diags = append(diags, diag.FromErr(err)...)
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentUSBList := d.Get(mkResourceVirtualEnvironmentVMHostUSB).([]interface{})
|
||||||
|
usbMap := map[string]interface{}{}
|
||||||
|
|
||||||
|
usbDevices := getUSBInfo(vmConfig, d)
|
||||||
|
for pi, pp := range usbDevices {
|
||||||
|
if (pp == nil) || (pp.HostDevice == nil && pp.Mapping == nil) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
usb := map[string]interface{}{}
|
||||||
|
|
||||||
|
if pp.HostDevice != nil {
|
||||||
|
usb[mkResourceVirtualEnvironmentVMHostUSBDevice] = *pp.HostDevice
|
||||||
|
} else {
|
||||||
|
usb[mkResourceVirtualEnvironmentVMHostUSBDevice] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp.USB3 != nil {
|
||||||
|
usb[mkResourceVirtualEnvironmentVMHostUSBDeviceUSB3] = *pp.USB3
|
||||||
|
} else {
|
||||||
|
usb[mkResourceVirtualEnvironmentVMHostUSBDeviceUSB3] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp.Mapping != nil {
|
||||||
|
usb[mkResourceVirtualEnvironmentVMHostUSBDeviceMapping] = *pp.Mapping
|
||||||
|
} else {
|
||||||
|
usb[mkResourceVirtualEnvironmentVMHostUSBDeviceMapping] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
usbMap[pi] = usb
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(currentUSBList) > 0 {
|
||||||
|
// todo: reordering of devices by PVE may cause an issue here
|
||||||
|
orderedUSBList := orderedListFromMap(usbMap)
|
||||||
|
err := d.Set(mkResourceVirtualEnvironmentVMHostUSB, orderedUSBList)
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Compare the initialization configuration to the one stored in the state.
|
// Compare the initialization configuration to the one stored in the state.
|
||||||
initialization := map[string]interface{}{}
|
initialization := map[string]interface{}{}
|
||||||
|
|
||||||
@ -5410,6 +5519,17 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
|
|||||||
rebootRequired = true
|
rebootRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare the new usb devices configuration.
|
||||||
|
if d.HasChange(mkResourceVirtualEnvironmentVMHostUSB) {
|
||||||
|
updateBody.USBDevices = vmGetHostUSBDeviceObjects(d)
|
||||||
|
|
||||||
|
for i := len(updateBody.USBDevices); i < maxResourceVirtualEnvironmentVMHostUSBDevices; i++ {
|
||||||
|
del = append(del, fmt.Sprintf("usb%d", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
rebootRequired = true
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare the new memory configuration.
|
// Prepare the new memory configuration.
|
||||||
if d.HasChange(mkResourceVirtualEnvironmentVMMemory) {
|
if d.HasChange(mkResourceVirtualEnvironmentVMMemory) {
|
||||||
memoryBlock, err := structure.GetSchemaBlock(
|
memoryBlock, err := structure.GetSchemaBlock(
|
||||||
@ -5922,6 +6042,17 @@ func getPCIInfo(resp *vms.GetResponseData, _ *schema.ResourceData) map[string]*v
|
|||||||
return pciDevices
|
return pciDevices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUSBInfo(resp *vms.GetResponseData, _ *schema.ResourceData) map[string]*vms.CustomUSBDevice {
|
||||||
|
usbDevices := map[string]*vms.CustomUSBDevice{}
|
||||||
|
|
||||||
|
usbDevices["usb0"] = resp.USBDevice0
|
||||||
|
usbDevices["usb1"] = resp.USBDevice1
|
||||||
|
usbDevices["usb2"] = resp.USBDevice2
|
||||||
|
usbDevices["usb3"] = resp.USBDevice3
|
||||||
|
|
||||||
|
return usbDevices
|
||||||
|
}
|
||||||
|
|
||||||
func parseImportIDWithNodeName(id string) (string, string, error) {
|
func parseImportIDWithNodeName(id string) (string, string, error) {
|
||||||
nodeName, id, found := strings.Cut(id, "/")
|
nodeName, id, found := strings.Cut(id, "/")
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMEFIDisk,
|
mkResourceVirtualEnvironmentVMEFIDisk,
|
||||||
mkResourceVirtualEnvironmentVMInitialization,
|
mkResourceVirtualEnvironmentVMInitialization,
|
||||||
mkResourceVirtualEnvironmentVMHostPCI,
|
mkResourceVirtualEnvironmentVMHostPCI,
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSB,
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
||||||
mkResourceVirtualEnvironmentVMKVMArguments,
|
mkResourceVirtualEnvironmentVMKVMArguments,
|
||||||
mkResourceVirtualEnvironmentVMMachine,
|
mkResourceVirtualEnvironmentVMMachine,
|
||||||
@ -84,6 +85,7 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMDisk: schema.TypeList,
|
mkResourceVirtualEnvironmentVMDisk: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMEFIDisk: schema.TypeList,
|
mkResourceVirtualEnvironmentVMEFIDisk: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMHostPCI: schema.TypeList,
|
mkResourceVirtualEnvironmentVMHostPCI: schema.TypeList,
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSB: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMInitialization: schema.TypeList,
|
mkResourceVirtualEnvironmentVMInitialization: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMIPv4Addresses: schema.TypeList,
|
mkResourceVirtualEnvironmentVMIPv4Addresses: schema.TypeList,
|
||||||
mkResourceVirtualEnvironmentVMIPv6Addresses: schema.TypeList,
|
mkResourceVirtualEnvironmentVMIPv6Addresses: schema.TypeList,
|
||||||
@ -278,6 +280,17 @@ func TestVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA: schema.TypeBool,
|
mkResourceVirtualEnvironmentVMHostPCIDeviceXVGA: schema.TypeBool,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
hostUSBSchema := test.AssertNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMHostUSB)
|
||||||
|
|
||||||
|
test.AssertOptionalArguments(t, hostUSBSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDeviceMapping,
|
||||||
|
})
|
||||||
|
|
||||||
|
test.AssertValueTypes(t, hostUSBSchema, map[string]schema.ValueType{
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDevice: schema.TypeString,
|
||||||
|
mkResourceVirtualEnvironmentVMHostUSBDeviceUSB3: schema.TypeBool,
|
||||||
|
})
|
||||||
|
|
||||||
initializationDNSSchema := test.AssertNestedSchemaExistence(
|
initializationDNSSchema := test.AssertNestedSchemaExistence(
|
||||||
t,
|
t,
|
||||||
initializationSchema,
|
initializationSchema,
|
||||||
|
Loading…
Reference in New Issue
Block a user