[lxc-devel] [lxd/master] Provide system information

monstermunchkin on Github lxc-bot at linuxcontainers.org
Wed Apr 15 11:02:41 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 322 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200415/3a44ad2c/attachment-0001.bin>
-------------- next part --------------
From 4867c824e309f655caf3eb16c4bfb1e9514e1081 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 15 Apr 2020 11:03:37 +0200
Subject: [PATCH 1/5] shared/version/api: Add resources_system 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 87e58fd56b..7f85dd1243 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -205,6 +205,7 @@ var APIExtensions = []string{
 	"resources_cpu_threads_numa",
 	"resources_cpu_core_die",
 	"api_os",
+	"resources_system",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From aad4e285db780bbbbfa34d0092cecf24ab29b0c2 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 15 Apr 2020 12:54:02 +0200
Subject: [PATCH 2/5] doc/api-extensions: Add resources_system

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 82fb120b81..7601e96ed8 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -1013,3 +1013,6 @@ Exposes the die\_id information on each core.
 This introduces two new fields in `/1.0`, `os` and `os\_version`.
 
 Those are taken from the os-release data on the system.
+
+## resources\_system
+This adds system information to the output of `/1.0/resources`.

From 14aec82ceb1e21ca3e9f518bfc8118a461465a0a Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 15 Apr 2020 12:49:00 +0200
Subject: [PATCH 3/5] shared/api/resource: Add system resources

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

diff --git a/shared/api/resource.go b/shared/api/resource.go
index 8cd2fc0dc4..bff1ca95df 100644
--- a/shared/api/resource.go
+++ b/shared/api/resource.go
@@ -16,6 +16,9 @@ type Resources struct {
 	// API extension: resources_usb_pci
 	USB ResourcesUSB `json:"usb" yaml:"usb"`
 	PCI ResourcesPCI `json:"pci" yaml:"pci"`
+
+	// API extention: resources_system
+	System ResourcesSystem `json:"system" yaml:"system"`
 }
 
 // ResourcesCPU represents the cpu resources available on the system
@@ -353,3 +356,46 @@ type ResourcesPCIDevice struct {
 	Vendor        string `json:"vendor" yaml:"vendor"`
 	VendorID      string `json:"vendor_id" yaml:"vendor_id"`
 }
+
+// ResourcesSystem represents the system
+// API extension: resources_system
+type ResourcesSystem struct {
+	UUID    string `json:"uuid" yaml:"uuid"`
+	Vendor  string `json:"vendor" yaml:"vendor"`
+	Product string `json:"product" yaml:"product"`
+	Family  string `json:"family" yaml:"family"`
+	Version string `json:"version" yaml:"version"`
+	Sku     string `json:"sku" yaml:"sku"`
+	Serial  string `json:"serial" yaml:"serial"`
+	Type    string `json:"type" yaml:"type"`
+
+	Firmware    *ResourcesSystemFirmware    `json:"firmware" yaml:"firmware"`
+	Chassis     *ResourcesSystemChassis     `json:"chassis" yaml:"chassis"`
+	Motherboard *ResourcesSystemMotherboard `json:"motherboard" yaml:"motherboard"`
+}
+
+// ResourcesSystemFirmware represents the system firmware
+// API extension: resources_system
+type ResourcesSystemFirmware struct {
+	Vendor  string `json:"vendor" yaml:"vendor"`
+	Date    string `json:"date" yaml:"date"`
+	Version string `json:"version" yaml:"version"`
+}
+
+// ResourcesSystemChassis represents the system chassis
+// API extension: resources_system
+type ResourcesSystemChassis struct {
+	Vendor  string `json:"vendor" yaml:"vendor"`
+	Type    string `json:"type" yaml:"type"`
+	Serial  string `json:"serial" yaml:"serial"`
+	Version string `json:"version" yaml:"version"`
+}
+
+// ResourcesSystemMotherboard represents the motherboard
+// API extension: resources_system
+type ResourcesSystemMotherboard struct {
+	Vendor  string `json:"vendor" yaml:"vendor"`
+	Product string `json:"product" yaml:"product"`
+	Serial  string `json:"serial" yaml:"serial"`
+	Version string `json:"version" yaml:"version"`
+}

From f12b1242fcf3f7c5f54412eb08bc499572ef0944 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 15 Apr 2020 12:49:42 +0200
Subject: [PATCH 4/5] lxd/resources: Add new system resources

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/resources/system.go | 328 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 328 insertions(+)
 create mode 100644 lxd/resources/system.go

diff --git a/lxd/resources/system.go b/lxd/resources/system.go
new file mode 100644
index 0000000000..57cc889e78
--- /dev/null
+++ b/lxd/resources/system.go
@@ -0,0 +1,328 @@
+package resources
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+
+	"github.com/pkg/errors"
+
+	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/api"
+)
+
+var sysClassDMIID = "/sys/class/dmi/id"
+var systemType string
+
+// GetSystem returns a filled api.ResourcesSystem struct ready for use by LXD
+func GetSystem() (*api.ResourcesSystem, error) {
+	var err error
+	system := api.ResourcesSystem{}
+
+	// Cache the system type
+	if systemType == "" {
+		// According to the man page, if a virtualization technology is detected, 0 is returned, a non-zero code otherwise.
+		out, err := shared.RunCommand("systemd-detect-virt")
+		if err != nil {
+			systemType = "physical"
+		} else {
+			systemType = out
+		}
+	}
+
+	system.Type = systemType
+
+	if !sysfsExists(sysClassDMIID) {
+		return &system, nil
+	}
+
+	// Product UUID
+	productUUIDPath := filepath.Join(sysClassDMIID, "product_uuid")
+	if sysfsExists(productUUIDPath) {
+		content, err := ioutil.ReadFile(productUUIDPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", productUUIDPath)
+
+		}
+
+		system.UUID = strings.TrimSpace(string(content))
+	}
+
+	// Vendor
+	vendorPath := filepath.Join(sysClassDMIID, "sys_vendor")
+	if sysfsExists(vendorPath) {
+		content, err := ioutil.ReadFile(vendorPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", vendorPath)
+
+		}
+
+		system.Vendor = strings.TrimSpace(string(content))
+	}
+
+	// Product name
+	productNamePath := filepath.Join(sysClassDMIID, "product_name")
+	if sysfsExists(productNamePath) {
+		content, err := ioutil.ReadFile(productNamePath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", productNamePath)
+
+		}
+
+		system.Product = strings.TrimSpace(string(content))
+	}
+
+	// Product family
+	productFamilyPath := filepath.Join(sysClassDMIID, "product_family")
+	if sysfsExists(productFamilyPath) {
+		content, err := ioutil.ReadFile(productFamilyPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", productFamilyPath)
+
+		}
+
+		system.Family = strings.TrimSpace(string(content))
+	}
+
+	// Product version
+	productVersion := filepath.Join(sysClassDMIID, "product_version")
+	if sysfsExists(productVersion) {
+		content, err := ioutil.ReadFile(productVersion)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", productVersion)
+
+		}
+
+		system.Version = strings.TrimSpace(string(content))
+	}
+
+	// Product SKU
+	productSKUPath := filepath.Join(sysClassDMIID, "product_sku")
+	if sysfsExists(productSKUPath) {
+		content, err := ioutil.ReadFile(productSKUPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", productSKUPath)
+
+		}
+
+		system.Sku = strings.TrimSpace(string(content))
+	}
+
+	// Product serial
+	productSerialPath := filepath.Join(sysClassDMIID, "product_serial")
+	if sysfsExists(productSerialPath) {
+		content, err := ioutil.ReadFile(productSerialPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", productSerialPath)
+
+		}
+
+		system.Serial = strings.TrimSpace(string(content))
+	}
+
+	system.Firmware, err = getSystemFirmware()
+	if err != nil {
+		return nil, err
+	}
+
+	system.Chassis, err = getSystemChassis()
+	if err != nil {
+		return nil, err
+	}
+
+	system.Motherboard, err = getSystemMotherboard()
+	if err != nil {
+		return nil, err
+	}
+
+	return &system, nil
+}
+
+func getSystemFirmware() (*api.ResourcesSystemFirmware, error) {
+	firmware := api.ResourcesSystemFirmware{}
+
+	// Firmware vendor
+	biosVendorPath := filepath.Join(sysClassDMIID, "bios_vendor")
+	if sysfsExists(biosVendorPath) {
+		content, err := ioutil.ReadFile(biosVendorPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", biosVendorPath)
+
+		}
+
+		firmware.Vendor = strings.TrimSpace(string(content))
+	}
+
+	// Firmware date
+	biosDatePath := filepath.Join(sysClassDMIID, "bios_date")
+	if sysfsExists(biosDatePath) {
+		content, err := ioutil.ReadFile(biosDatePath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", biosDatePath)
+
+		}
+
+		firmware.Date = strings.TrimSpace(string(content))
+	}
+
+	// Firmware version
+	biosVersionPath := filepath.Join(sysClassDMIID, "bios_version")
+	if sysfsExists(biosVersionPath) {
+		content, err := ioutil.ReadFile(biosVersionPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", biosVersionPath)
+
+		}
+
+		firmware.Version = strings.TrimSpace(string(content))
+	}
+
+	return &firmware, nil
+}
+
+func getSystemChassis() (*api.ResourcesSystemChassis, error) {
+	chassis := api.ResourcesSystemChassis{}
+
+	// Chassis vendor
+	chassisVendorPath := filepath.Join(sysClassDMIID, "chassis_vendor")
+	if sysfsExists(chassisVendorPath) {
+		content, err := ioutil.ReadFile(chassisVendorPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", chassisVendorPath)
+
+		}
+
+		chassis.Vendor = strings.TrimSpace(string(content))
+	}
+
+	// Chassis types according to the DMTF SMBIOS Spec
+	chassisTypes := map[uint64]string{
+		0x1:  "Other",
+		0x2:  "Unknown",
+		0x3:  "Desktop",
+		0x4:  "Low Profile Desktop",
+		0x5:  "Pizza Box",
+		0x6:  "Mini Tower",
+		0x7:  "Tower",
+		0x8:  "Portable",
+		0x9:  "Laptop",
+		0xA:  "Notebook",
+		0xB:  "Hand Held",
+		0xC:  "Docking Station",
+		0xD:  "All in One",
+		0xE:  "Sub Notebook",
+		0xF:  "Space-saving",
+		0x10: "Lunch Box",
+		0x11: "Main Server Chassis",
+		0x12: "Expansion Chassis",
+		0x13: "SubChassis",
+		0x14: "Bus Expansion Chassis",
+		0x15: "Peripheral Chassis",
+		0x16: "RAID Chassis",
+		0x17: "Rack Mount Chassis",
+		0x18: "Sealed-case PC",
+		0x19: "Multi-system chassis",
+		0x1A: "Compact PCI",
+		0x1B: "Advanced TCA",
+		0x1C: "Blade",
+		0x1D: "Blade Enclosure",
+		0x1E: "Tablet",
+		0x1F: "Convertible",
+		0x20: "Detachable",
+		0x21: "IoT Gateway",
+		0x22: "Embedded PC",
+		0x23: "Mini PC",
+		0x24: "Stick PC",
+	}
+
+	// Chassis type
+	chassisTypePath := filepath.Join(sysClassDMIID, "chassis_type")
+	if sysfsExists(chassisTypePath) {
+		chassisType, err := readUint(chassisTypePath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to parse %q", chassisTypePath)
+		}
+
+		chassis.Type = chassisTypes[chassisType]
+	}
+
+	// Chassis serial
+	chassisSerialPath := filepath.Join(sysClassDMIID, "chassis_serial")
+	if sysfsExists(chassisSerialPath) {
+		content, err := ioutil.ReadFile(chassisSerialPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", chassisSerialPath)
+
+		}
+
+		chassis.Serial = strings.TrimSpace(string(content))
+	}
+
+	// Chassis version
+	chassisVersionPath := filepath.Join(sysClassDMIID, "chassis_version")
+	if sysfsExists(chassisVersionPath) {
+		content, err := ioutil.ReadFile(chassisVersionPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", chassisVersionPath)
+
+		}
+
+		chassis.Version = strings.TrimSpace(string(content))
+	}
+
+	return &chassis, nil
+}
+
+func getSystemMotherboard() (*api.ResourcesSystemMotherboard, error) {
+	motherboard := api.ResourcesSystemMotherboard{}
+
+	// Motherboard vendor name
+	boardVendorPath := filepath.Join(sysClassDMIID, "board_vendor")
+	if sysfsExists(boardVendorPath) {
+		content, err := ioutil.ReadFile(boardVendorPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", boardVendorPath)
+
+		}
+
+		motherboard.Vendor = strings.TrimSpace(string(content))
+	}
+
+	// Motherboard product name
+	boardNamePath := filepath.Join(sysClassDMIID, "board_name")
+	if sysfsExists(boardNamePath) {
+		content, err := ioutil.ReadFile(boardNamePath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", boardNamePath)
+
+		}
+
+		motherboard.Product = strings.TrimSpace(string(content))
+	}
+
+	// Motherboard serial
+	boardSerialPath := filepath.Join(sysClassDMIID, "board_serial")
+	if sysfsExists(boardSerialPath) {
+		content, err := ioutil.ReadFile(boardSerialPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", boardSerialPath)
+
+		}
+
+		motherboard.Serial = strings.TrimSpace(string(content))
+	}
+
+	// Motherboard version
+	boardVersionPath := filepath.Join(sysClassDMIID, "board_version")
+	if sysfsExists(boardVersionPath) {
+		content, err := ioutil.ReadFile(boardVersionPath)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Failed to read %q", boardVersionPath)
+
+		}
+
+		motherboard.Version = strings.TrimSpace(string(content))
+	}
+
+	return &motherboard, nil
+}

From 928ff91ccfd5a8866154cf257e85fbaa32e57e84 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 15 Apr 2020 12:50:02 +0200
Subject: [PATCH 5/5] lxd/resources: Retrieve system information

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/resources/resources.go | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lxd/resources/resources.go b/lxd/resources/resources.go
index c43acc43b7..c35f8876eb 100644
--- a/lxd/resources/resources.go
+++ b/lxd/resources/resources.go
@@ -50,6 +50,12 @@ func GetResources() (*api.Resources, error) {
 		return nil, errors.Wrap(err, "Failed to retrieve PCI information")
 	}
 
+	// Get system information
+	system, err := GetSystem()
+	if err != nil {
+		return nil, errors.Wrap(err, "Failed to retrieve system information")
+	}
+
 	// Build the final struct
 	resources := api.Resources{
 		CPU:     *cpu,
@@ -59,6 +65,7 @@ func GetResources() (*api.Resources, error) {
 		Storage: *storage,
 		USB:     *usb,
 		PCI:     *pci,
+		System:  *system,
 	}
 
 	return &resources, nil


More information about the lxc-devel mailing list