Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: extend d/vsphere_host_pci_device support #2049

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 122 additions & 8 deletions vsphere/data_source_vsphere_host_pci_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package vsphere

import (
"crypto/sha256"
"fmt"
"log"
"regexp"
"strconv"
Expand Down Expand Up @@ -38,31 +40,114 @@ 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.",
},
},
},
},
},
}
}

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 {
Expand All @@ -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)
Expand All @@ -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
}

Expand Down
68 changes: 68 additions & 0 deletions vsphere/data_source_vsphere_host_pci_device_test.go
Original file line number Diff line number Diff line change
@@ -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"))
}
75 changes: 51 additions & 24 deletions website/docs/d/host_pci_device.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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.