[lxc-devel] [lxd/master] Add USB and PCI devices to resources API

monstermunchkin on Github lxc-bot at linuxcontainers.org
Wed Mar 25 17:41:50 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200325/1587eb5f/attachment.bin>
-------------- next part --------------
From ab6c532d7dd48b5e3364aeb0a7b47fb9a4958ae4 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 25 Mar 2020 18:21:26 +0100
Subject: [PATCH 1/5] shared/version/api: Add usb_pci_resources API extension

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 shared/version/api.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/shared/version/api.go b/shared/version/api.go
index ba5f2f00ee..fc92e2bfb2 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -198,6 +198,7 @@ var APIExtensions = []string{
 	"volume_snapshot_scheduling",
 	"trust_ca_certificates",
 	"snapshot_disk_usage",
+	"usb_pci_resources",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From d4ee5abd1d794eb82b648424424bd60fb2d7f9a5 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 25 Mar 2020 18:24:47 +0100
Subject: [PATCH 2/5] doc: Add usb_pci_resources

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 doc/api-extensions.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 3340012872..a1eb7a29a3 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -972,3 +972,6 @@ In this case, it will ask for the password.
 
 ## snapshot\_disk\_usage
 This adds a new `size` field to the output of `/1.0/instances/<name>/snapshots/<snapshot>` which represents the disk usage of the snapshot.
+
+## usb\_pci\_resources
+This adds USB and PCI devices to the output of `/1.0/resources`.

From f763623152dbf339eb74f1b1c1425a8503489f29 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 25 Mar 2020 14:42:25 +0100
Subject: [PATCH 3/5] shared/api: Add USB and PCI resources

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 shared/api/resource.go | 44 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/shared/api/resource.go b/shared/api/resource.go
index c3d3c0ff9d..08aac037a3 100644
--- a/shared/api/resource.go
+++ b/shared/api/resource.go
@@ -12,6 +12,10 @@ type Resources struct {
 	// API extension: resources_v2
 	Network ResourcesNetwork `json:"network" yaml:"network"`
 	Storage ResourcesStorage `json:"storage" yaml:"storage"`
+
+	// API extension: usb_pci_resources
+	USB ResourcesUSB `json:"usb" yaml:"usb"`
+	PCI ResourcesPCI `json:"pci" yaml:"pci"`
 }
 
 // ResourcesCPU represents the cpu resources available on the system
@@ -294,3 +298,43 @@ type ResourcesStoragePoolInodes struct {
 	Used  uint64 `json:"used" yaml:"used"`
 	Total uint64 `json:"total" yaml:"total"`
 }
+
+// ResourcesUSB represents the USB devices available on the system
+// API extension: usb_pci_resources
+type ResourcesUSB struct {
+	Devices []ResourcesUSBDevice `json:"devices" yaml:"devices"`
+	Total   uint64               `json:"total" yaml:"total"`
+}
+
+// ResourcesUSBDevice represents a USB device
+// API extension: usb_pci_resources
+type ResourcesUSBDevice struct {
+	BusAddress    uint64 `json:"bus_address" yaml:"bus_address"`
+	DeviceAddress uint64 `json:"device_address" yaml:"device_address"`
+	Driver        string `json:"driver" yaml:"driver"`
+	DriverVersion string `json:"driver_version" yaml:"driver_version"`
+	Product       string `json:"product" yaml:"product"`
+	ProductID     string `json:"product_id" yaml:"product_id"`
+	Vendor        string `json:"vendor" yaml:"vendor"`
+	VendorID      string `json:"vendor_id" yaml:"vendor_id"`
+}
+
+// ResourcesPCI represents the PCI devices available on the system
+// API extension: usb_pci_resources
+type ResourcesPCI struct {
+	Devices []ResourcesPCIDevice `json:"devices" yaml:"devices"`
+	Total   uint64               `json:"total" yaml:"total"`
+}
+
+// ResourcesPCIDevice represents a PCI device
+// API extension: usb_pci_resources
+type ResourcesPCIDevice struct {
+	Driver        string `json:"driver" yaml:"driver"`
+	DriverVersion string `json:"driver_version" yaml:"driver_version"`
+	NumaNode      string `json:"numa_node" yaml:"numa_node"`
+	PCIAddress    string `json:"pci_address" yaml:"pci_address"`
+	Product       string `json:"product" yaml:"product"`
+	ProductID     string `json:"product_id" yaml:"product_id"`
+	Vendor        string `json:"vendor" yaml:"vendor"`
+	VendorID      string `json:"vendor_id" yaml:"vendor_id"`
+}

From 66298740002724a11c9344044cc5e9d1244bf4e6 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 25 Mar 2020 14:43:03 +0100
Subject: [PATCH 4/5] lxd/resources: Add USB resource

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/resources/resources.go |   7 ++
 lxd/resources/usb.go       | 158 +++++++++++++++++++++++++++++++++++++
 2 files changed, 165 insertions(+)
 create mode 100644 lxd/resources/usb.go

diff --git a/lxd/resources/resources.go b/lxd/resources/resources.go
index a8c2ba6437..2b7185e171 100644
--- a/lxd/resources/resources.go
+++ b/lxd/resources/resources.go
@@ -38,6 +38,12 @@ func GetResources() (*api.Resources, error) {
 		return nil, errors.Wrap(err, "Failed to retrieve storage information")
 	}
 
+	// Get USB information
+	usb, err := GetUSB()
+	if err != nil {
+		return nil, errors.Wrap(err, "Failed to retrieve USB information")
+	}
+
 	// Build the final struct
 	resources := api.Resources{
 		CPU:     *cpu,
@@ -45,6 +51,7 @@ func GetResources() (*api.Resources, error) {
 		GPU:     *gpu,
 		Network: *network,
 		Storage: *storage,
+		USB:     *usb,
 	}
 
 	return &resources, nil
diff --git a/lxd/resources/usb.go b/lxd/resources/usb.go
new file mode 100644
index 0000000000..34c69b0475
--- /dev/null
+++ b/lxd/resources/usb.go
@@ -0,0 +1,158 @@
+package resources
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"strconv"
+	"strings"
+
+	"github.com/lxc/lxd/shared/api"
+	"github.com/pkg/errors"
+)
+
+var sysBusUSB = "/sys/bus/usb/devices"
+
+// GetUSB returns a filled api.ResourcesUSB struct ready for use by LXD
+func GetUSB() (*api.ResourcesUSB, error) {
+	usb := api.ResourcesUSB{}
+	devices := []api.ResourcesUSBDevice{}
+
+	// List all USB devices
+	entries, err := ioutil.ReadDir(sysBusUSB)
+	if err != nil {
+		return nil, errors.Wrapf(err, "Failed to list %q", sysBusUSB)
+	}
+
+	for _, entry := range entries {
+		entryPath := filepath.Join(sysBusUSB, entry.Name())
+
+		// Skip entries without a bus address
+		if !sysfsExists(filepath.Join(entryPath, "busnum")) {
+			continue
+		}
+
+		devClassFile := filepath.Join(entryPath, "bDeviceClass")
+
+		if sysfsExists(devClassFile) {
+			content, err := ioutil.ReadFile(devClassFile)
+			if err != nil {
+				return nil, errors.Wrapf(err, "Failed to read %q", devClassFile)
+			}
+
+			devClass, err := strconv.ParseUint(strings.TrimSpace(string(content)), 16, 64)
+			if err != nil {
+				return nil, errors.Wrapf(err, "Failed to parse device class %q", content)
+			}
+
+			// Skip USB hubs
+			if devClass == 9 {
+				continue
+			}
+		}
+
+		busAddress, err := readUint(filepath.Join(entryPath, "busnum"))
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(entryPath, "busnum"))
+		}
+
+		devAddress, err := readUint(filepath.Join(entryPath, "devnum"))
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(entryPath, "devnum"))
+		}
+
+		var product string
+
+		if sysfsExists(filepath.Join(entryPath, "product")) {
+			productBytes, err := ioutil.ReadFile(filepath.Join(entryPath, "product"))
+			if err != nil {
+				return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(entryPath, "product"))
+			}
+
+			product = strings.TrimSpace(string(productBytes))
+		}
+
+		productID, err := ioutil.ReadFile(filepath.Join(entryPath, "idProduct"))
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(entryPath, "idProduct"))
+		}
+
+		var vendor string
+
+		if sysfsExists(filepath.Join(entryPath, "vendor")) {
+			vendorBytes, err := ioutil.ReadFile(filepath.Join(entryPath, "vendor"))
+			if err != nil {
+				return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(entryPath, "vendor"))
+			}
+
+			vendor = strings.TrimSpace(string(vendorBytes))
+
+		}
+
+		vendorID, err := ioutil.ReadFile(filepath.Join(entryPath, "idVendor"))
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(entryPath, "idVendor"))
+		}
+
+		subEntries, err := ioutil.ReadDir(entryPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to list %q", entryPath)
+		}
+
+		for _, subEntry := range subEntries {
+			// Skip irrelevant directories and file entries
+			if !subEntry.IsDir() || !strings.HasPrefix(subEntry.Name(), entry.Name()) {
+				continue
+			}
+
+			device := api.ResourcesUSBDevice{
+				BusAddress:    busAddress,
+				DeviceAddress: devAddress,
+				Product:       strings.TrimSpace(string(product)),
+				ProductID:     strings.TrimSpace(string(productID)),
+				Vendor:        strings.TrimSpace(string(vendor)),
+				VendorID:      strings.TrimSpace(string(vendorID)),
+			}
+
+			driverPath := filepath.Join(entryPath, subEntry.Name(), "driver")
+
+			if sysfsExists(driverPath) {
+				target, err := os.Readlink(driverPath)
+				if err != nil {
+					return nil, errors.Wrapf(err, "Failed to get driver of %q", filepath.Join(entryPath, subEntry.Name()))
+				}
+
+				device.Driver = filepath.Base(target)
+			}
+
+			driverModuleVersion := filepath.Join(entryPath, subEntry.Name(), "driver", "module", "version")
+
+			if sysfsExists(driverModuleVersion) {
+				version, err := ioutil.ReadFile(driverModuleVersion)
+				if err != nil {
+					return nil, errors.Wrapf(err, "Failed to read %q", driverModuleVersion)
+				}
+
+				device.DriverVersion = strings.TrimSpace(string(version))
+			}
+
+			devices = append(devices, device)
+		}
+	}
+
+	usb.Devices = append(usb.Devices, devices[0])
+
+	// Remove duplicates
+	for i, device := range devices {
+		if i == 0 || reflect.DeepEqual(device, devices[i-1]) {
+			continue
+		}
+
+		usb.Devices = append(usb.Devices, device)
+	}
+
+	usb.Total = uint64(len(usb.Devices))
+
+	return &usb, nil
+}

From 5ed9db1fb5a697dbf1019fa3a95d6e02e408b704 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 25 Mar 2020 17:35:38 +0100
Subject: [PATCH 5/5] lxd/resources: Add PCI resource

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/resources/pci.go       | 8 ++++++++
 lxd/resources/resources.go | 7 +++++++
 2 files changed, 15 insertions(+)
 create mode 100644 lxd/resources/pci.go

diff --git a/lxd/resources/pci.go b/lxd/resources/pci.go
new file mode 100644
index 0000000000..200d5b38a0
--- /dev/null
+++ b/lxd/resources/pci.go
@@ -0,0 +1,8 @@
+package resources
+
+import "github.com/lxc/lxd/shared/api"
+
+// GetPCI returns a filled api.ResourcesPCI struct ready for use by LXD
+func GetPCI() (*api.ResourcesPCI, error) {
+	return &api.ResourcesPCI{}, nil
+}
diff --git a/lxd/resources/resources.go b/lxd/resources/resources.go
index 2b7185e171..c43acc43b7 100644
--- a/lxd/resources/resources.go
+++ b/lxd/resources/resources.go
@@ -44,6 +44,12 @@ func GetResources() (*api.Resources, error) {
 		return nil, errors.Wrap(err, "Failed to retrieve USB information")
 	}
 
+	// Get PCI information
+	pci, err := GetPCI()
+	if err != nil {
+		return nil, errors.Wrap(err, "Failed to retrieve PCI information")
+	}
+
 	// Build the final struct
 	resources := api.Resources{
 		CPU:     *cpu,
@@ -52,6 +58,7 @@ func GetResources() (*api.Resources, error) {
 		Network: *network,
 		Storage: *storage,
 		USB:     *usb,
+		PCI:     *pci,
 	}
 
 	return &resources, nil


More information about the lxc-devel mailing list