The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7194

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
This resolves #7189.

From 4867c824e309f655caf3eb16c4bfb1e9514e1081 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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.h...@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
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to