diff --git a/vsphere/data_source_vsphere_host_pci_device.go b/vsphere/data_source_vsphere_host_pci_device.go index 30a0969a0..54334038d 100644 --- a/vsphere/data_source_vsphere_host_pci_device.go +++ b/vsphere/data_source_vsphere_host_pci_device.go @@ -4,6 +4,8 @@ package vsphere import ( + "crypto/sha256" + "fmt" "log" "regexp" "strconv" @@ -38,10 +40,74 @@ func dataSourceVSphereHostPciDevice() *schema.Resource { Optional: true, Description: "The hexadecimal value of the PCI device's vendor ID.", }, - "name": { - Type: schema.TypeString, + "pci_devices": { + Type: schema.TypeList, Computed: true, - Description: "The name of the PCI device.", + Description: "The list of matching PCI Devices available on the host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The name ID of this PCI, composed of 'bus:slot.function'", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the PCI device.", + }, + "bus": { + Type: schema.TypeString, + Computed: true, + Description: "The bus ID of the PCI device.", + }, + "slot": { + Type: schema.TypeString, + Computed: true, + Description: "The slot ID of the PCI device.", + }, + "function": { + Type: schema.TypeString, + Computed: true, + Description: "The function ID of the PCI device.", + }, + "class_id": { + Type: schema.TypeString, + Computed: true, + Description: "The hexadecimal value of the PCI device's class ID.", + }, + "vendor_id": { + Type: schema.TypeString, + Computed: true, + Description: "The hexadecimal value of the PCI device's vendor ID.", + }, + "sub_vendor_id": { + Type: schema.TypeString, + Computed: true, + Description: "The hexadecimal value of the PCI device's sub vendor ID.", + }, + "vendor_name": { + Type: schema.TypeString, + Computed: true, + Description: "The vendor name of the PCI device.", + }, + "device_id": { + Type: schema.TypeString, + Computed: true, + Description: "The hexadecimal value of the PCI device's device ID.", + }, + "sub_device_id": { + Type: schema.TypeString, + Computed: true, + Description: "The hexadecimal value of the PCI device's sub device ID.", + }, + "parent_bridge": { + Type: schema.TypeString, + Computed: true, + Description: "The parent bridge of the PCI device.", + }, + }, + }, }, }, } @@ -49,20 +115,39 @@ func dataSourceVSphereHostPciDevice() *schema.Resource { func dataSourceVSphereHostPciDeviceRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] DataHostPCIDev: Beginning PCI device lookup on %s", d.Get("host_id").(string)) + client := meta.(*Client).vimClient + host, err := hostsystem.FromID(client, d.Get("host_id").(string)) if err != nil { return err } + hprops, err := hostsystem.Properties(host) if err != nil { return err } + + // Create unique ID based on the host_id + idsum := sha256.New() + if _, err := idsum.Write([]byte(fmt.Sprintf("%#v", d.Get("host_id").(string)))); err != nil { + return err + } + + d.SetId(fmt.Sprintf("%x", idsum.Sum(nil))) + + // Identify PCI devices matching name_regex (if any) devices, err := matchName(d, hprops.Hardware.PciDevice) if err != nil { return err } + + // Output slice + pciDevices := make([]interface{}, 0, len(devices)) + log.Printf("[DEBUG] DataHostPCIDev: Looking for a device with matching class_id and vendor_id") + + // Loop through devices for _, device := range devices { // Match the class_id if it is set. if class, exists := d.GetOk("class_id"); exists { @@ -74,6 +159,7 @@ func dataSourceVSphereHostPciDeviceRead(d *schema.ResourceData, meta interface{} continue } } + // Now match the vendor_id if it is set. if vendor, exists := d.GetOk("vendor_id"); exists { vendorInt, err := strconv.ParseInt(vendor.(string), 16, 16) @@ -84,15 +170,43 @@ func dataSourceVSphereHostPciDeviceRead(d *schema.ResourceData, meta interface{} continue } } + + // Convertions classHex := strconv.FormatInt(int64(device.ClassId), 16) vendorHex := strconv.FormatInt(int64(device.VendorId), 16) - d.SetId(device.Id) - _ = d.Set("name", device.DeviceName) - _ = d.Set("class_id", classHex) - _ = d.Set("vendor_id", vendorHex) + subVendorHex := strconv.FormatInt(int64(device.SubVendorId), 16) + deviceHex := strconv.FormatInt(int64(device.DeviceId), 16) + subDeviceHex := strconv.FormatInt(int64(device.SubDeviceId), 16) + busString := fmt.Sprintf("%v", device.Bus) + slotString := fmt.Sprintf("%v", device.Slot) + functionString := fmt.Sprintf("%v", device.Function) + + dev := map[string]interface{}{ + "id": device.Id, + "name": device.DeviceName, + "class_id": classHex, + "vendor_id": vendorHex, + "sub_vendor_id": subVendorHex, + "device_id": deviceHex, + "sub_device_id": subDeviceHex, + "bus": busString, + "slot": slotString, + "function": functionString, + "parent_bridge": device.ParentBridge, + "vendor_name": device.VendorName, + } + + // Add PCI device to output slice + pciDevices = append(pciDevices, dev) + log.Printf("[DEBUG] DataHostPCIDev: Matching PCI device found: %s", device.DeviceName) - return nil } + + // Set the `pci_devices` output to all PCI devices + if err := d.Set("pci_devices", pciDevices); err != nil { + return err + } + return nil } diff --git a/vsphere/data_source_vsphere_host_pci_device_test.go b/vsphere/data_source_vsphere_host_pci_device_test.go new file mode 100644 index 000000000..5f03dec1a --- /dev/null +++ b/vsphere/data_source_vsphere_host_pci_device_test.go @@ -0,0 +1,68 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vsphere + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/testhelper" +) + +func TestAccDataSourceVSphereHostPciDevice_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccDataSourceVSphereHostPciDevicePreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceVSphereHostPciDeviceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "data.vsphere_host_pci_device.device", + "pci_devices.#", + ), + ), + }, + { + Config: testAccDataSourceVSphereHostPciDeviceConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "data.vsphere_host_pci_device.device", + "pci_devices.0.name", + ), + ), + }, + }, + }) +} + +func testAccDataSourceVSphereHostPciDevicePreCheck(t *testing.T) { + if os.Getenv("TF_VAR_VSPHERE_DATACENTER") == "" { + t.Skip("set TF_VAR_VSPHERE_DATACENTER to run vsphere_host_pci_device acceptance tests") + } + if os.Getenv("TF_VAR_VSPHERE_ESXI1") == "" { + t.Skip("set TF_VAR_VSPHERE_ESXI1 to run vsphere_host_pci_device acceptance tests") + } +} + +func testAccDataSourceVSphereHostPciDeviceConfig() string { + return fmt.Sprintf(` +%s + +data "vsphere_host" "host" { + name = "%s" + datacenter_id = "${data.vsphere_datacenter.rootdc1.id}" +} + +data "vsphere_host_pci_device" "device" { + host_id = "${data.vsphere_host.host.id}" + name_regex = "" +} +`, testhelper.CombineConfigs(testhelper.ConfigDataRootDC1(), testhelper.ConfigDataRootPortGroup1()), os.Getenv("TF_VAR_VSPHERE_ESXI1")) +} diff --git a/website/docs/d/host_pci_device.html.markdown b/website/docs/d/host_pci_device.html.markdown index 367798923..768657a3b 100644 --- a/website/docs/d/host_pci_device.html.markdown +++ b/website/docs/d/host_pci_device.html.markdown @@ -4,14 +4,20 @@ layout: "vsphere" page_title: "VMware vSphere: vsphere_host_pci_device" sidebar_current: "docs-vsphere-data-source-host_pci_device" description: |- - A data source that can be used to get information for a PCI passthrough - device on an ESXi host. + A data source that can be used to get information for PCI passthrough + device(s) on an ESXi host. The returned attribute `pci_devices` will + be a list of matching PCI Passthrough devices, based on the criteria: + - name_regex + - class_id + - vendor_id + + **NOTE** - The matching criteria above are evaluated in that order. --- # vsphere_host_pci_device -The `vsphere_host_pci_device` data source can be used to discover the device ID -of a vSphere host's PCI device. This can then be used with +The `vsphere_host_pci_device` data source can be used to discover the device ID(s) +of a vSphere host's PCI device(s). This can then be used with `vsphere_virtual_machine`'s `pci_device_id`. ## Example Usage with Vendor ID and Class ID @@ -32,23 +38,24 @@ data "vsphere_host_pci_device" "dev" { vendor_id = 456 } ``` + ## Example Usage with Name Regular Expression - - ```hcl - data "vsphere_datacenter" "datacenter" { - name = "dc-01" - } - - data "vsphere_host" "host" { - name = "esxi-01.example.com" - datacenter_id = data.vsphere_datacenter.datacenter.id - } - - data "vsphere_host_pci_device" "dev" { - host_id = data.vsphere_host.host.id - name_regex = "MMC" - } - ``` + +```hcl +data "vsphere_datacenter" "datacenter" { + name = "dc-01" +} + +data "vsphere_host" "host" { + name = "esxi-01.example.com" + datacenter_id = data.vsphere_datacenter.datacenter.id +} + +data "vsphere_host_pci_device" "dev" { + host_id = data.vsphere_host.host.id + name_regex = "MMC" +} +``` ## Argument Reference @@ -57,14 +64,34 @@ The following arguments are supported: * `host_id` - (Required) The [managed object reference ID][docs-about-morefs] of a host. * `name_regex` - (Optional) A regular expression that will be used to match the host PCI device name. +* `class_id` - (Optional) The hexadecimal PCI device class ID. * `vendor_id` - (Optional) The hexadecimal PCI device vendor ID. -* `class_id` - (Optional) The hexadecimal PCI device class ID [docs-about-morefs]: /docs/providers/vsphere/index.html#use-of-managed-object-references-by-the-vsphere-provider -~> **NOTE:** `name_regex`, `vendor_id`, and `class_id` can all be used together. +~> **NOTE:** `name_regex`, `class_id`, and `vendor_id` can all be used together. +The above arguments are evaluated and filter PCI Device results in the above order. ## Attribute Reference -* `id` - The device ID of the PCI device. -* `name` - The name of the PCI device. +The following attributes are exported: + +* `host_id` - The [managed objectID][docs-about-morefs] of the ESXi host. +* `id` - Unique (SHA256) id based on the host_id if the ESXi host. +* `name_regex` - (Optional) A regular expression that will be used to match the + host vGPU profile name. +* `class_id` - (Optional) The hexadecimal PCI device class ID. +* `vendor_id` - (Optional) The hexadecimal PCI device vendor ID. +* `pci_devices` - The list of matching PCI Devices available on the host. + * `id` - The name ID of this PCI, composed of "bus:slot.function" + * `name` - The name of the PCI device. + * `bus` - The bus ID of the PCI device. + * `class_id` - The hexadecimal value of the PCI device's class ID. + * `device_id` - The hexadecimal value of the PCI device's device ID. + * `function` - The function ID of the PCI device. + * `parent_bridge` - The parent bridge of the PCI device. + * `slot` - The slot ID of the PCI device. + * `sub_device_id` - The hexadecimal value of the PCI device's sub device ID. + * `sub_vendor_id` - The hexadecimal value of the PCI device's sub vendor ID. + * `vendor_id` - The hexadecimal value of the PCI device's vendor ID. + * `vendor_name` - The vendor name of the PCI device.