Skip to content

Commit 0276fdd

Browse files
mristoktenthirtyam
authored andcommitted
feat: add vgpu to vm resource
1 parent 4c79f33 commit 0276fdd

File tree

4 files changed

+175
-68
lines changed

4 files changed

+175
-68
lines changed

vsphere/internal/virtualdevice/virtual_machine_device_subresource.go

Lines changed: 126 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ func (c *pciApplyConfig) modifyVirtualPciDevices(devList *schema.Set, op types.V
10131013
}
10141014

10151015
// PciPassthroughApplyOperation checks for changes in a virtual machine's
1016-
// PCI passthrough devices and creates config specs to apply apply to the
1016+
// PCI passthrough devices and creates config specs to apply to the
10171017
// virtual machine.
10181018
func PciPassthroughApplyOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
10191019
old, newValue := d.GetChange("pci_device_id")
@@ -1091,62 +1091,147 @@ func PciPassthroughPostCloneOperation(d *schema.ResourceData, c *govmomi.Client,
10911091
return applyConfig.VirtualDevice, applyConfig.Spec, nil
10921092
}
10931093

1094-
// SharedPciPassthroughApplyOperation checks for changes in a virtual machine's
1095-
// Shared PCI passthrough device and creates config specs to apply apply to the
1096-
// virtual machine.
1097-
func SharedPciPassthroughApplyOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
1098-
old, new := d.GetChange("shared_pci_device_id")
1099-
oldDevId := old.(string)
1100-
newDevId := new.(string)
1094+
// modifyVirtualSharedPciDevices will take a list of devices and an operation and
1095+
// will create the appropriate config spec.
1096+
func (c *pciApplyConfig) modifyVirtualSharedPciDevices(devList *schema.Set, op types.VirtualDeviceConfigSpecOperation) error {
1097+
log.Printf("VirtualMachine: Creating Shared PCI device specs %v", op)
1098+
for _, devId := range devList.List() {
1099+
log.Printf("[DEBUG] modifyVirtualSharedPciDevices: Appending %v spec for %s", op, devId.(string))
11011100

1102-
var specs []types.BaseVirtualDeviceConfigSpec
1103-
if oldDevId == newDevId {
1104-
return l, specs, nil
1105-
}
1101+
dev := &types.VirtualPCIPassthrough{
1102+
VirtualDevice: types.VirtualDevice{
1103+
DynamicData: types.DynamicData{},
1104+
Backing: &types.VirtualPCIPassthroughVmiopBackingInfo{
1105+
Vgpu: devId.(string),
1106+
},
1107+
},
1108+
}
11061109

1107-
if oldDevId != "" {
1108-
vm, err := virtualmachine.FromUUID(c, d.Id())
1110+
vm, err := virtualmachine.FromUUID(c.Client, c.ResourceData.Id())
11091111
if err != nil {
1110-
return nil, nil, err
1112+
return err
11111113
}
1114+
11121115
vprops, err := virtualmachine.Properties(vm)
11131116
if err != nil {
1114-
return nil, nil, err
1117+
return err
11151118
}
1119+
11161120
// This will only find a device for delete operations.
11171121
for _, vmDevP := range vprops.Config.Hardware.Device {
11181122
if vmDev, ok := vmDevP.(*types.VirtualPCIPassthrough); ok {
1119-
if vmDev.Backing.(*types.VirtualPCIPassthroughVmiopBackingInfo).Vgpu == oldDevId {
1120-
dspec, err := object.VirtualDeviceList{vmDev}.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
1121-
if err != nil {
1122-
return nil, nil, err
1123-
}
1124-
specs = append(specs, dspec...)
1125-
1126-
l = applyDeviceChange(l, dspec)
1127-
d.Set("reboot_required", true)
1123+
if vmDev.Backing.(*types.VirtualPCIPassthroughVmiopBackingInfo).Vgpu == devId {
1124+
dev = vmDev
11281125
}
11291126
}
11301127
}
1131-
}
11321128

1133-
if newDevId != "" {
1134-
dev := &types.VirtualPCIPassthrough{
1135-
VirtualDevice: types.VirtualDevice{
1136-
DynamicData: types.DynamicData{},
1137-
Backing: &types.VirtualPCIPassthroughVmiopBackingInfo{
1138-
Vgpu: newDevId,
1139-
},
1140-
},
1141-
}
1142-
dspec, err := object.VirtualDeviceList{dev}.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
1129+
dspec, err := object.VirtualDeviceList{dev}.ConfigSpec(op)
11431130
if err != nil {
1144-
return nil, nil, err
1131+
return err
11451132
}
1146-
specs = append(specs, dspec...)
1147-
l = applyDeviceChange(l, dspec)
1148-
d.Set("reboot_required", true)
1133+
1134+
c.Spec = append(c.Spec, dspec...)
1135+
c.VirtualDevice = applyDeviceChange(c.VirtualDevice, dspec)
1136+
}
1137+
log.Printf("VirtualMachine: Shared PCI device specs created")
1138+
return nil
1139+
}
1140+
1141+
// SharedPciApplyOperation checks for changes in a virtual machine's
1142+
// Shared PCI device and creates config specs to apply to the
1143+
// virtual machine.
1144+
func SharedPciApplyOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
1145+
log.Printf("[DEBUG] SharedPciApplyOperation: Looking for shared PCI device changes")
1146+
1147+
// Get current (old) and new devices
1148+
old, newValue := d.GetChange("shared_pci_device_id")
1149+
oldDevIds := old.(*schema.Set)
1150+
newDevIds := newValue.(*schema.Set)
1151+
1152+
// Compare
1153+
delDevs := oldDevIds.Difference(newDevIds)
1154+
addDevs := newDevIds.Difference(oldDevIds)
1155+
1156+
// Create base apply config
1157+
applyConfig := &pciApplyConfig{
1158+
Client: c,
1159+
ResourceData: d,
1160+
Spec: []types.BaseVirtualDeviceConfigSpec{},
1161+
VirtualDevice: l,
1162+
}
1163+
1164+
// If there are no changes, return as is
1165+
if addDevs.Len() == 0 && delDevs.Len() == 0 {
1166+
log.Printf("[DEBUG] SharedPciApplyOperation: No shared PCI device additions/deletions")
1167+
return applyConfig.VirtualDevice, applyConfig.Spec, nil
1168+
}
1169+
1170+
// Set reboot
1171+
_ = d.Set("reboot_required", true)
1172+
1173+
// Add new Shared PCI devices
1174+
log.Printf("[DEBUG] SharedPciApplyOperation: Identified %d shared PCI device additions",
1175+
addDevs.Len())
1176+
err := applyConfig.modifyVirtualSharedPciDevices(addDevs, types.VirtualDeviceConfigSpecOperationAdd)
1177+
if err != nil {
1178+
return nil, nil, err
11491179
}
11501180

1151-
return l, specs, nil
1181+
// Remove deleted Shared PCI devices
1182+
log.Printf("[DEBUG] SharedPciApplyOperation: Identified %d shared PCI device deletions",
1183+
delDevs.Len())
1184+
err = applyConfig.modifyVirtualSharedPciDevices(delDevs, types.VirtualDeviceConfigSpecOperationRemove)
1185+
if err != nil {
1186+
return nil, nil, err
1187+
}
1188+
1189+
return applyConfig.VirtualDevice, applyConfig.Spec, nil
1190+
}
1191+
1192+
// SharedPciPostCloneOperation normalizes the Shared PCI devices
1193+
// on a newly-cloned virtual machine and outputs any necessary device change
1194+
// operations. It also sets the state in advance of the post-create read.
1195+
func SharedPciPostCloneOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
1196+
log.Printf("[DEBUG] SharedPCIPostCloneOperation: Looking for shared PCI device changes post-clone")
1197+
1198+
// Get current (old) and new devices
1199+
old, newValue := d.GetChange("shared_pci_device_id")
1200+
oldDevIds := old.(*schema.Set)
1201+
newDevIds := newValue.(*schema.Set)
1202+
1203+
// Compare
1204+
delDevs := oldDevIds.Difference(newDevIds)
1205+
addDevs := newDevIds.Difference(oldDevIds)
1206+
1207+
// Create base apply config
1208+
applyConfig := &pciApplyConfig{
1209+
Client: c,
1210+
ResourceData: d,
1211+
Spec: []types.BaseVirtualDeviceConfigSpec{},
1212+
VirtualDevice: l,
1213+
}
1214+
1215+
// If there are no changes, return as is
1216+
if addDevs.Len() <= 0 && delDevs.Len() <= 0 {
1217+
log.Printf("[DEBUG] SharedPCIPostCloneOperation: No shared PCI device additions/deletions post-clone")
1218+
return applyConfig.VirtualDevice, applyConfig.Spec, nil
1219+
}
1220+
1221+
// Add new Shared PCI devices
1222+
log.Printf("[DEBUG] SharedPCIPostCloneOperation: Identified %d shared PCI device additions post-clone",
1223+
addDevs.Len())
1224+
err := applyConfig.modifyVirtualSharedPciDevices(addDevs, types.VirtualDeviceConfigSpecOperationAdd)
1225+
if err != nil {
1226+
return nil, nil, err
1227+
}
1228+
1229+
// Remove deleted Shared PCI devices
1230+
log.Printf("[DEBUG] SharedPCIPostCloneOperation: Identified %d shared PCI device deletions post-clone",
1231+
delDevs.Len())
1232+
err = applyConfig.modifyVirtualSharedPciDevices(delDevs, types.VirtualDeviceConfigSpecOperationRemove)
1233+
if err != nil {
1234+
return nil, nil, err
1235+
}
1236+
return applyConfig.VirtualDevice, applyConfig.Spec, nil
11521237
}

vsphere/resource_vsphere_file.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,10 @@ func createDirectory(datastoreFileManager *object.DatastoreFileManager, f *file)
149149

150150
// fileUpload - upload file to a vSphere datastore
151151
func fileUpload(client *govmomi.Client, dc *object.Datacenter, ds *object.Datastore, source, destination string) error {
152-
dsurl, err := ds.URL(context.TODO(), dc, destination)
153-
if err != nil {
154-
return err
155-
}
152+
dsurl := ds.NewURL(destination)
156153

157154
p := soap.DefaultUpload
158-
err = client.Client.UploadFile(context.TODO(), source, dsurl, &p)
155+
err := client.Client.UploadFile(context.TODO(), source, dsurl, &p)
159156
if err != nil {
160157
return err
161158
}

vsphere/resource_vsphere_virtual_machine.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,9 @@ func resourceVSphereVirtualMachine() *schema.Resource {
244244
Elem: &schema.Schema{Type: schema.TypeString},
245245
},
246246
"shared_pci_device_id": {
247-
Type: schema.TypeString,
247+
Type: schema.TypeSet,
248248
Optional: true,
249-
Description: "Id of Shared PCI passthrough device, 'grid_rtx8000-8q'",
249+
Description: "A list of Shared PCI passthrough device, 'grid_rtx8000-8q'",
250250
Elem: &schema.Schema{Type: schema.TypeString},
251251
},
252252
"clone": {
@@ -515,28 +515,34 @@ func resourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{})
515515

516516
// Read the virtual machine PCI passthrough devices
517517
var pciDevs []string
518+
var sharedPciDevs []string
518519
for _, dev := range vprops.Config.Hardware.Device {
519520
if pci, ok := dev.(*types.VirtualPCIPassthrough); ok {
520-
if pciBacking, ok := pci.Backing.(*types.VirtualPCIPassthroughDeviceBackingInfo); ok {
521-
devId := pciBacking.Id
521+
switch t := pci.Backing.(type) {
522+
case *types.VirtualPCIPassthroughDeviceBackingInfo:
523+
devId := t.Id
522524
pciDevs = append(pciDevs, devId)
523-
} else {
524-
if pciBacking, ok := pci.Backing.(*types.VirtualPCIPassthroughVmiopBackingInfo); ok {
525-
err = d.Set("shared_pci_device_id", pciBacking.Vgpu)
526-
if err != nil {
527-
return err
528-
}
529-
} else {
530-
log.Printf("[WARN] Ignoring VM %q VirtualPCIPassthrough device with backing type of %T",
531-
vm.InventoryPath, pci.Backing)
532-
}
525+
log.Printf("[DEBUG] Identified VM %q VirtualPCIPassthrough device %s with backing type of %T",
526+
vm.InventoryPath, devId, pci.Backing)
527+
case *types.VirtualPCIPassthroughVmiopBackingInfo:
528+
dev := t.Vgpu
529+
sharedPciDevs = append(sharedPciDevs, dev)
530+
log.Printf("[DEBUG] Identified VM %q VirtualPCIPassthrough device %s with backing type of %T",
531+
vm.InventoryPath, dev, pci.Backing)
532+
default:
533+
log.Printf("[WARN] Ignoring VM %q VirtualPCIPassthrough device with backing type of %T",
534+
vm.InventoryPath, pci.Backing)
533535
}
534536
}
535537
}
536538
err = d.Set("pci_device_id", pciDevs)
537539
if err != nil {
538540
return err
539541
}
542+
err = d.Set("shared_pci_device_id", sharedPciDevs)
543+
if err != nil {
544+
return err
545+
}
540546

541547
// Perform pending device read operations.
542548
devices := object.VirtualDeviceList(vprops.Config.Hardware.Device)
@@ -1675,6 +1681,17 @@ func resourceVSphereVirtualMachinePostDeployChanges(d *schema.ResourceData, meta
16751681
)
16761682
}
16771683
cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
1684+
// Shared PCI devices
1685+
devices, delta, err = virtualdevice.SharedPciPostCloneOperation(d, client, devices)
1686+
if err != nil {
1687+
return resourceVSphereVirtualMachineRollbackCreate(
1688+
d,
1689+
meta,
1690+
vm,
1691+
fmt.Errorf("error processing shared PCI device changes post-clone: %s", err),
1692+
)
1693+
}
1694+
cfgSpec.DeviceChange = virtualdevice.AppendDeviceChangeSpec(cfgSpec.DeviceChange, delta...)
16781695
log.Printf("[DEBUG] %s: Final device list: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceListString(devices))
16791696
log.Printf("[DEBUG] %s: Final device change cfgSpec: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceChangeString(cfgSpec.DeviceChange))
16801697

@@ -1978,8 +1995,8 @@ func applyVirtualDevices(d *schema.ResourceData, c *govmomi.Client, l object.Vir
19781995
return nil, err
19791996
}
19801997
spec = virtualdevice.AppendDeviceChangeSpec(spec, delta...)
1981-
// Shared PCI passthrough device
1982-
l, delta, err = virtualdevice.SharedPciPassthroughApplyOperation(d, c, l)
1998+
// Shared PCI device
1999+
l, delta, err = virtualdevice.SharedPciApplyOperation(d, c, l)
19832000
if err != nil {
19842001
return nil, err
19852002
}

website/docs/r/virtual_machine.html.markdown

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,8 @@ The following options are general virtual machine and provider workflow options:
616616

617617
* `clone` - (Optional) When specified, the virtual machine will be created as a clone of a specified template. Optional customization options can be submitted for the resource. See [creating a virtual machine from a template](#creating-a-virtual-machine-from-a-template) for more information.
618618

619+
~> **NOTE:** Cloning requires vCenter Server and is not supported on direct ESXi host connections.
620+
619621
* `extra_config_reboot_required` - (Optional) Allow the virtual machine to be rebooted when a change to `extra_config` occurs. Default: `true`.
620622

621623
* `custom_attributes` - (Optional) Map of custom attribute ids to attribute value strings to set for virtual machine. Please refer to the [`vsphere_custom_attributes`][docs-setting-custom-attributes] resource for more information on setting custom attributes.
@@ -661,14 +663,10 @@ The following options are general virtual machine and provider workflow options:
661663

662664
* `network_interface` - (Required) A specification for a virtual NIC on the virtual machine. See [network interface options](#network-interface-options) for more information.
663665

664-
* `pci_device_id` - (Optional) List of host PCI device IDs in which to create PCI passthroughs.
665-
666-
* `shared_pci_device_id` - (Optional) A shared PCI device ID to create PCI passthrough.
667-
668-
~> **NOTE:** Cloning requires vCenter Server and is not supported on direct ESXi host connections.
669-
670666
* `ovf_deploy` - (Optional) When specified, the virtual machine will be deployed from the provided OVF/OVA template. See [creating a virtual machine from an OVF/OVA template](#creating-a-virtual-machine-from-an-ovf-ova-template) for more information.
671667

668+
* `pci_device_id` - (Optional) List of host PCI device IDs in which to create PCI passthroughs.
669+
672670
* `replace_trigger` - (Optional) Triggers replacement of resource whenever it changes.
673671

674672
For example, `replace_trigger = sha256(format("%s-%s",data.template_file.cloud_init_metadata.rendered,data.template_file.cloud_init_userdata.rendered))` will fingerprint the changes in cloud-init metadata and userdata templates. This will enable a replacement of the resource whenever the dependant template renders a new configuration. (Forces a replacement.)
@@ -685,6 +683,16 @@ For example, `replace_trigger = sha256(format("%s-%s",data.template_file.cloud_i
685683

686684
* `scsi_bus_sharing` - (Optional) The type of SCSI bus sharing for the virtual machine SCSI controller. One of `physicalSharing`, `virtualSharing`, and `noSharing`. Default: `noSharing`.
687685

686+
* `shared_pci_device_id` - (Optional) List of shared PCI device(s) to create
687+
shared PCI passthrough.
688+
689+
For example, attaching a vGPU to the virtual machine:
690+
`shared_pci_device_id = ["grid_a100d-40c"]`
691+
692+
~> **NOTE:** The use of vGPU requires that the VM has memory reservation set equal
693+
to the amount of provisioned memory. This can be accomplished by leveraging
694+
the [`memory_reservation`](#memory_reservation) option.
695+
688696
* `storage_policy_id` - (Optional) The ID of the storage policy to assign to the home directory of a virtual machine.
689697

690698
* `tags` - (Optional) The IDs of any tags to attach to this resource. Please refer to the [`vsphere_tag`][docs-applying-tags] resource for more information on applying tags to virtual machine resources.

0 commit comments

Comments
 (0)