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: add data sources to extract node and device information #1042

Merged
merged 17 commits into from
Dec 28, 2023
Merged
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
750 changes: 750 additions & 0 deletions libvirt/data_source_libvirt_node_device_info.go

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions libvirt/data_source_libvirt_node_device_info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package libvirt

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccLibvirtNodeDeviceInfoDataSource(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceNodeDeviceInfoPCI,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"data.libvirt_node_device_info.device", "name", regexp.MustCompile(`^pci_0000_00_00_0$`)),
),
}, {
Config: testAccDataSourceNodeDeviceInfoSystem,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"data.libvirt_node_device_info.device", "name", regexp.MustCompile(`^computer$`)),
),
},
},
})
}

const testAccDataSourceNodeDeviceInfoPCI = `
data "libvirt_node_device_info" "device" {
name = "pci_0000_00_00_0"
}`

const testAccDataSourceNodeDeviceInfoSystem = `
data "libvirt_node_device_info" "device" {
name = "computer"
}`
73 changes: 73 additions & 0 deletions libvirt/data_source_libvirt_node_devices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package libvirt

import (
"fmt"
"log"
"sort"
"strconv"

libvirt "github.com/digitalocean/go-libvirt"
"github.com/dmacvicar/terraform-provider-libvirt/libvirt/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

// a libvirt nodeinfo datasource
//
// Datasource example:
//
// data "libvirt_node_devices" "list" {
// }
//
// output "cpus" {
// value = data.libvirt_node_devices.list.devices
// }
func datasourceLibvirtNodeDevices() *schema.Resource {
return &schema.Resource{
Read: resourceLibvirtNodeDevicesRead,
Schema: map[string]*schema.Schema{
"capability": {
Type: schema.TypeString,
Optional: true,
},
"devices": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

func resourceLibvirtNodeDevicesRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Read data source libvirt_nodedevices")

virConn := meta.(*Client).libvirt
if virConn == nil {
return fmt.Errorf(LibVirtConIsNil)
}

var cap libvirt.OptString

if capability, ok := d.GetOk("capability"); ok {
cap = append(cap, capability.(string))
log.Printf("[DEBUG] Got capability: %v", cap)
}

rnum, err := virConn.NodeNumOfDevices(libvirt.OptString(cap), 0)
if err != nil {
return fmt.Errorf("failed to retrieve number of devices: %w", err)
}

devices, err := virConn.NodeListDevices(cap, rnum, 0)
if err != nil {
return fmt.Errorf("failed to retrieve list of node devices: %w", err)
}

sort.Strings(devices)
d.Set("devices", devices)
d.SetId(strconv.Itoa(hashcode.String(fmt.Sprintf("%v", devices))))

return nil
}
41 changes: 41 additions & 0 deletions libvirt/data_source_libvirt_node_devices_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package libvirt

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccLibvirtNodeDevicesDataSource(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceNodeDevicesPci,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"data.libvirt_node_devices.node", "devices[0]", regexp.MustCompile(`^pci_\d{4}_\d{2}_\d{2}_\d{1}`)),
),
},
{
Config: testAccDataSourceNodeDevicesSystem,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"data.libvirt_node_devices.node", "devices[0]", regexp.MustCompile(`^computer$`)),
),
},
},
})
}

const testAccDataSourceNodeDevicesPci = `
data "libvirt_node_devices" "node" {
capability = "pci"
}`

const testAccDataSourceNodeDevicesSystem = `
data "libvirt_node_devices" "node" {
capability = "system"
}`
94 changes: 94 additions & 0 deletions libvirt/data_source_libvirt_node_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package libvirt

import (
"fmt"
"log"
"strconv"

"github.com/dmacvicar/terraform-provider-libvirt/libvirt/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

// a libvirt nodeinfo datasource
//
// Datasource example:
//
// data "libvirt_nodeinfo" "info" {
// }
//
// output "cpus" {
// value = data.libvirt_nodeinfo.info.cpus
// }
func datasourceLibvirtNodeInfo() *schema.Resource {
return &schema.Resource{
Read: resourceLibvirtNodeInfoRead,
Schema: map[string]*schema.Schema{
"cpu_model": {
Type: schema.TypeString,
Computed: true,
},
"memory_total_kb": {
Type: schema.TypeInt,
Computed: true,
},
"cpu_cores_total": {
Type: schema.TypeInt,
Computed: true,
},
"numa_nodes": {
Type: schema.TypeInt,
Computed: true,
},
"cpu_sockets": {
Type: schema.TypeInt,
Computed: true,
},
"cpu_cores_per_socket": {
Type: schema.TypeInt,
Computed: true,
},
"cpu_threads_per_core": {
Type: schema.TypeInt,
Computed: true,
},
},
}
}

func resourceLibvirtNodeInfoRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Read data source libvirt_nodeinfo")

virConn := meta.(*Client).libvirt
if virConn == nil {
return fmt.Errorf(LibVirtConIsNil)
}

model, memory, cpus, _, nodes, sockets, cores, threads, err := virConn.NodeGetInfo()

if err != nil {
return fmt.Errorf("failed to retrieve domains: %w", err)
}

d.Set("cpu_model", int8ToString(model))
d.Set("cpu_cores_total", cpus)
d.Set("cpu_cores_per_socket", cores)
d.Set("numa_nodes", nodes)
d.Set("cpu_sockets", sockets)
d.Set("cpu_threads_per_core", threads)
d.Set("numa_nodes", nodes)
d.Set("memory_total_kb", memory)
d.SetId(strconv.Itoa(hashcode.String(fmt.Sprintf("%v%v%v%v%v%v%v", model, memory, cpus, nodes, sockets, cores, threads))))

return nil
}

func int8ToString(bs [32]int8) string {
ba := []uint8{}
for _, b := range bs {
if b == 0 {
break
}
ba = append(ba, uint8(b))
}
return string(ba)
}
35 changes: 35 additions & 0 deletions libvirt/data_source_libvirt_node_info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package libvirt

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccLibvirtNodeInfoDataSource(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceNodeInfo,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"data.libvirt_node_info.info", "cpus", regexp.MustCompile(`^\d+`)),
),
}, {
Config: testAccDataSourceNodeInfo,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr(
"data.libvirt_node_info.info", "numa_nodes", regexp.MustCompile(`^\d+`)),
),
},
},
})
}

const testAccDataSourceNodeInfo = `
data "libvirt_node_info" "info" {

}`
3 changes: 3 additions & 0 deletions libvirt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ func Provider() *schema.Provider {
"libvirt_network_dns_host_template": datasourceLibvirtNetworkDNSHostTemplate(),
"libvirt_network_dns_srv_template": datasourceLibvirtNetworkDNSSRVTemplate(),
"libvirt_network_dnsmasq_options_template": datasourceLibvirtNetworkDnsmasqOptionsTemplate(),
"libvirt_node_info": datasourceLibvirtNodeInfo(),
"libvirt_node_device_info": datasourceLibvirtNodeDeviceInfo(),
"libvirt_node_devices": datasourceLibvirtNodeDevices(),
},

ConfigureFunc: providerConfigure,
Expand Down
89 changes: 89 additions & 0 deletions website/docs/d/node_device_info.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
layout: "libvirt"
page_title: "Libvirt: libvirt_node_device_info"
sidebar_current: "docs-libvirt-node-device-info"
description: |-
Use this data source to get information about a specific device on the current node
---

# Data Source: libvirt\_node\_device\_info

Retrieve information about a specific device on the current node

## Example Usage

```hcl
data "libvirt_node_device_info" "device" {
name = "pci_0000_00_00_0"
}
```

## Argument Reference

* `name` - (Required) The name of the device name as expected by [libvirt](https://www.libvirt.org/manpages/virsh.html#nodedev-commands).

## Attribute Reference

This data source exports the following attributes in addition to the arguments above:

* `capability` - A map of the various attributes for the device, depending on the type of device.
Currently implemented are `pci`, `storage`, `usb`
* `type` - Device type: `pci`, `storage`, `usb`
* `devnode`: For devices with a `/dev` special file. A mandatory attribute type specify the kind of file `path`, which may be either dev for the main name, or `link` for additional symlinks.

For `pci` devices the additional attributes are:
* `class` - Device PCI class
* `domain` - Device PCI domain
* `bus` - Device PCI bus
* `slot` - Device PCI slot
* `function` - Device PCI function
* `product` - Device PCI product map of `id` and `name`
* `vendor` - Device PCI vendor map of `id` and `name`
* `iommu_group` - Structure that holds IOMMU Group `number` and the list of devices that are part of the group

For `storage` devices the additional attributes are:
* `block` - Block device name
* `drive_type` - Device drive type
* `model` - Device model
* `serial` - Device serial number
* `size` - Device size in bytes
* `logical_block_size` - Device logical block size
* `num_blocks` - Number of blocks on the device

For `usb` devices the additional attributes are:
* `number` - Device number
* `class` - Device class
* `subclass` - Device subclass
* `protocol` - Device protocol

For `usb_device` devices the additional attributes are:
* `bus` - Which bus the device belongs to
* `device` - Which device within the \
* `product` - If present, the product `id` and `name` from the device ROM
* `vendor` - If present, the vendor `id` and `name` from the device ROM

For `net` devices the additional attributes are:
* `interface` - The interface name tied to this device
* `address` - If present, the MAC address of the device
* `link` - Optional to reflect the status of the link via `speed` and `state` keys
* `feature` - List of features supported by the network interface
* `capability` - Holds key `type` that describes the type of network interface: `80203` for IEEE 802.3 or `80211` for IEEE 802.11

For `scsi_host` devices the additional attributes are:
* `host` - The SCSI host number
* `unique_id` - This optionally provides the value from the 'unique_id' file found in the scsi_host's directory

For `scsi` devices the additional attributes are:
* `host` - The SCSI host containing the device
* `bus` - The bus within the host
* `target` - The target within the bus
* `lun` - The lun within the target
* `scsi_type` - The type of SCSI device

For `drm` devices the additional attributes are:
* `drm_type` - Type of DRM device: `render` or `card`

* `parent` - The parent of this device in the hierarchy
* `path` - Full path of the device
* `xml` - The XML returned by the libvirt API call
* `devnode` - For type `drm` holds the `path` and `link` that point to the device
Loading
Loading