Merge ~ltrager/maas:lp1876217 into maas:master

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: b3108a300dc6e7c0d390a4c0d2d035be6b2d5207
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:lp1876217
Merge into: maas:master
Diff against target: 300 lines (+118/-35)
5 files modified
src/machine-resources/Makefile (+2/-2)
src/machine-resources/src/machine-resources/Gopkg.lock (+12/-15)
src/machine-resources/src/machine-resources/main.go (+79/-11)
src/metadataserver/builtin_scripts/hooks.py (+5/-1)
src/metadataserver/builtin_scripts/tests/test_hooks.py (+20/-6)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
MAAS Lander Approve
Review via email: mp+383467@code.launchpad.net

Commit message

LP: #1876217 - Gather OS information from /etc/os-release or /etc/lsb-release

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp1876217 lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 478ea10101b8e36aa7ac4f986052ca85514a9742

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

I think GetLSBRelease should do the following:

- try to open /etc/lsb-release
- if it fails, get the info from osarch.GetLSBRelease()
- otherwise, parse /etc/lsb-release
- return a struct with just "name" and "version" fields (so the calling code doesn't need to know where does the info come from.

review: Needs Fixing
Revision history for this message
Lee Trager (ltrager) wrote :

In the bug Ian suggested we use a system-files interface for /etc/os-release. Do you see any reason not to do that and dump the modification to main.go?

~ltrager/maas:lp1876217 updated
a91eff5... by Lee Trager

Merge branch 'master' into lp1876217

22ebee1... by Lee Trager

Try /etc/os-release, fallback on /etc/lsb-release

Revision history for this message
Lee Trager (ltrager) wrote :

Adam updated me on the meeting with snapd that happened last night. My understanding is snapd will expose /etc/os-release in the future by default. As such I've updated this MP to try /etc/os-release, /usr/lib/os-release, and /etc/lsb-release. If an os-release file has the ID of ubuntu-core its skipped as that is the base Snap. The information is then gathered from /etc/lsb-release.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp1876217 lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 983f3dd298867877e6e186a1fad1ee065877b69a

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp1876217 lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 22ebee150f37fd210374c64fad8bdb7829864637

review: Approve
~ltrager/maas:lp1876217 updated
1036759... by Lee Trager

Merge branch 'master' into lp1876217

a52321f... by Lee Trager

Don't send OS information if it isn't detectable. If no OS information is
sent don't update the controller's OS.

Revision history for this message
Lee Trager (ltrager) wrote :

I've updated this to make this highly resilient. Machine resources will only fail to detect OS information when running the Snap on a host OS other then Ubuntu. This will be fixed once snapd passes through /etc/os-release. In the case that detecting OS information fails nothing is sent, MAAS will keep the current OS information.

To answer your question on IRC I'm scanning /etc/os-release and /usr/lib/os-release incase the host OS only provides /usr/lib/os-release. LXD[1] does the same thing when getting OS information.

[1] https://github.com/lxc/lxd/blob/master/shared/osarch/release.go#L11

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lp1876217 lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: a52321f63f9f51d592a1a8104e5e5e6b0b8ecb60

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

A couple of comments inline

~ltrager/maas:lp1876217 updated
4d3e828... by Lee Trager

Use OSInfo struct

Revision history for this message
Lee Trager (ltrager) wrote :

Updated to use an OSInfo struct as suggested.

/usr/lib/os-release is actually defined by the freedesktop.org spec[1]. It is suggested both are searched. Fedora and CentOS both provide /usr/lib/os-release.

[1] https://www.freedesktop.org/software/systemd/man/os-release.html

~ltrager/maas:lp1876217 updated
b3108a3... by Lee Trager

make format

Revision history for this message
Alberto Donato (ack) wrote :

+1, thanks for the changes

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/machine-resources/Makefile b/src/machine-resources/Makefile
index a083aec..d28199d 100644
--- a/src/machine-resources/Makefile
+++ b/src/machine-resources/Makefile
@@ -27,7 +27,7 @@ DEP := cd $(PACKAGE_DIR); dep
27build: $(BINARIES)27build: $(BINARIES)
28.PHONY: build28.PHONY: build
2929
30$(BINARIES): $(PACKAGE_VENDOR_DIR)30$(BINARIES): $(PACKAGE_VENDOR_DIR) $(PACKAGE_DIR)/main.go
31 GOCACHE=$(GO_CACHE_DIR) GOARCH=$(GOARCH_$(notdir $@)) go build -ldflags '-s -w' -o $@ machine-resources31 GOCACHE=$(GO_CACHE_DIR) GOARCH=$(GOARCH_$(notdir $@)) go build -ldflags '-s -w' -o $@ machine-resources
3232
33$(PACKAGE_VENDOR_DIR) vendor:33$(PACKAGE_VENDOR_DIR) vendor:
@@ -41,5 +41,5 @@ clean:
41.PHONY: clean41.PHONY: clean
4242
43update-deps:43update-deps:
44 $(DEP) ensure -update github.com/lxc/lxd44 $(DEP) ensure -update
45.PHONY: update-deps45.PHONY: update-deps
diff --git a/src/machine-resources/src/machine-resources/Gopkg.lock b/src/machine-resources/src/machine-resources/Gopkg.lock
index 6c2f652..ce7f9ec 100644
--- a/src/machine-resources/src/machine-resources/Gopkg.lock
+++ b/src/machine-resources/src/machine-resources/Gopkg.lock
@@ -4,30 +4,26 @@
4[[projects]]4[[projects]]
5 name = "github.com/flosch/pongo2"5 name = "github.com/flosch/pongo2"
6 packages = ["."]6 packages = ["."]
7 revision = "bbf5a6c351f4d4e883daa40046a404d7553e0a00"7 revision = "5e81b817a0c48c1c57cdf1a9056cf76bdee02ca9"
8 version = "v3.0"
89
9[[projects]]10[[projects]]
10 name = "github.com/gorilla/websocket"11 name = "github.com/gorilla/websocket"
11 packages = ["."]12 packages = ["."]
12 revision = ""13 revision = "b65e62901fc1c0d968042419e74789f6af455eb9"
13 version = "v1.4.2"14 version = "v1.4.2"
1415
15[[projects]]16[[projects]]
16 name = "github.com/jaypipes/pcidb"17 name = "github.com/jaypipes/pcidb"
17 packages = ["."]18 packages = ["."]
18 revision = "49e68a8beb0c155efb2db43bea4b354ec4843ff1"19 revision = "98ef3ee36c69f20650fe7855047ad042338c6983"
19 version = "0.5.0"20 version = "v0.5.0"
20
21[[projects]]
22 name = "github.com/juju/errors"
23 packages = ["."]
24 revision = "3fe23663418fc1d724868c84f21b7519bbac7441"
2521
26[[projects]]22[[projects]]
27 branch = "master"23 branch = "master"
28 name = "github.com/lxc/lxd"24 name = "github.com/lxc/lxd"
29 packages = ["lxd/resources","shared","shared/api","shared/cancel","shared/ioprogress","shared/logger","shared/osarch","shared/units","shared/usbid","shared/version"]25 packages = ["lxd/resources","shared","shared/api","shared/cancel","shared/ioprogress","shared/logger","shared/osarch","shared/units","shared/usbid","shared/version"]
30 revision = "7516f0a2968b025efd1ea5066e78b16d6646b1c8"26 revision = "a101c996de697dda597538ffb95eb4949711f3ec"
3127
32[[projects]]28[[projects]]
33 name = "github.com/mitchellh/go-homedir"29 name = "github.com/mitchellh/go-homedir"
@@ -38,16 +34,17 @@
38[[projects]]34[[projects]]
39 name = "github.com/pkg/errors"35 name = "github.com/pkg/errors"
40 packages = ["."]36 packages = ["."]
41 revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"37 revision = "614d223910a179a466c1767a985424175c39b465"
42 version = "v0.8.1"38 version = "v0.9.1"
4339
44[[projects]]40[[projects]]
45 branch = "master"41 branch = "master"
46 name = "golang.org/x/sys"42 name = "golang.org/x/sys"
47 packages = ["unix","windows"]43 packages = ["internal/unsafeheader","unix","windows"]
48 revision = "b09406accb4736d857a32bf9444cd7edae2ffa79"44 revision = "fe76b779f299728f3bd63f77ea2c815504229c3b"
4945
50[[projects]]46[[projects]]
47 branch = "v2"
51 name = "gopkg.in/robfig/cron.v2"48 name = "gopkg.in/robfig/cron.v2"
52 packages = ["."]49 packages = ["."]
53 revision = "be2e0b0deed5a68ffee390b4583a13aff8321535"50 revision = "be2e0b0deed5a68ffee390b4583a13aff8321535"
@@ -55,6 +52,6 @@
55[solve-meta]52[solve-meta]
56 analyzer-name = "dep"53 analyzer-name = "dep"
57 analyzer-version = 154 analyzer-version = 1
58 inputs-digest = "c48e39bda2f6db455f595dad9fbb6171c11d952e37c8b1b1a0f4e85ca1330e8e"55 inputs-digest = "0aa702d9956536c2ac11ab31b412046932bc18980bb395c40153f94d457e48c6"
59 solver-name = "gps-cdcl"56 solver-name = "gps-cdcl"
60 solver-version = 157 solver-version = 1
diff --git a/src/machine-resources/src/machine-resources/main.go b/src/machine-resources/src/machine-resources/main.go
index be9f16d..7ba5884 100644
--- a/src/machine-resources/src/machine-resources/main.go
+++ b/src/machine-resources/src/machine-resources/main.go
@@ -4,26 +4,31 @@
4package main4package main
55
6import (6import (
7 "bufio"
7 "encoding/json"8 "encoding/json"
8 "fmt"9 "fmt"
9 "os"10 "os"
11 "strings"
1012
11 "github.com/lxc/lxd/lxd/resources"13 "github.com/lxc/lxd/lxd/resources"
12 "github.com/lxc/lxd/shared"14 "github.com/lxc/lxd/shared"
13 "github.com/lxc/lxd/shared/osarch"
14 "github.com/lxc/lxd/shared/version"15 "github.com/lxc/lxd/shared/version"
15)16)
1617
18type OSInfo struct {
19 OSName string `json:"os_name" yaml:"os_name"`
20 OSVersion string `json:"os_version" yaml:"os_version"`
21}
22
17// Subset of github.com/lxc/lxd/shared/api/server.ServerEnvironment23// Subset of github.com/lxc/lxd/shared/api/server.ServerEnvironment
18type ServerEnvironment struct {24type ServerEnvironment struct {
19 Kernel string `json:"kernel" yaml:"kernel"`25 Kernel string `json:"kernel" yaml:"kernel"`
20 KernelArchitecture string `json:"kernel_architecture" yaml:"kernel_architecture"`26 KernelArchitecture string `json:"kernel_architecture" yaml:"kernel_architecture"`
21 KernelVersion string `json:"kernel_version" yaml:"kernel_version"`27 KernelVersion string `json:"kernel_version" yaml:"kernel_version"`
22 OSName string `json:"os_name" yaml:"os_name"`28 OSInfo
23 OSVersion string `json:"os_version" yaml:"os_version"`29 Server string `json:"server" yaml:"server"`
24 Server string `json:"server" yaml:"server"`30 ServerName string `json:"server_name" yaml:"server_name"`
25 ServerName string `json:"server_name" yaml:"server_name"`31 ServerVersion string `json:"server_version" yaml:"server_version"`
26 ServerVersion string `json:"server_version" yaml:"server_version"`
27}32}
2833
29// Subset of github.com/lxc/lxd/shared/api/server.HostInfo34// Subset of github.com/lxc/lxd/shared/api/server.HostInfo
@@ -45,6 +50,66 @@ func check_error(err error) {
45 }50 }
46}51}
4752
53func parseKeyValueFile(path string) (map[string]string, error) {
54 parsed_file := make(map[string]string)
55 file, err := os.Open(path)
56 if err != nil {
57 return parsed_file, err
58 }
59 defer file.Close()
60
61 scanner := bufio.NewScanner(file)
62 for scanner.Scan() {
63 line := strings.TrimSpace(scanner.Text())
64 if strings.HasPrefix(line, "#") {
65 continue
66 }
67
68 tokens := strings.SplitN(line, "=", 2)
69 if len(tokens) != 2 {
70 continue
71 }
72 parsed_file[strings.Trim(tokens[0], `'"`)] = strings.Trim(tokens[1], `'"`)
73 }
74
75 return parsed_file, nil
76}
77
78func getOSNameVersion() OSInfo {
79 // Search both pathes as suggested by
80 // https://www.freedesktop.org/software/systemd/man/os-release.html
81 for _, path := range []string{"/etc/os-release", "/usr/lib/os-release"} {
82 parsed_file, err := parseKeyValueFile(path)
83 // LP:1876217 - As of 2.44 snapd only gives confined Snaps
84 // access to /etc/lsb-release from the host OS. /etc/os-release
85 // currently contains the Ubuntu Core version the Snap is
86 // running. Try /etc/os-release first for controllers installed
87 // with the Debian packages. At some point in the future snapd
88 // may provide the host OS version of /etc/os-release.
89 if err == nil && parsed_file["ID"] != "ubuntu-core" {
90 return OSInfo{
91 OSName: strings.ToLower(parsed_file["ID"]),
92 OSVersion: strings.ToLower(parsed_file["VERSION_ID"]),
93 }
94 }
95 }
96 // If /etc/os-release isn't found or only contains the Ubuntu Core
97 // version try to get OS information from /etc/lsb-release as this
98 // file is passed from the running host OS. Only Ubuntu and Manjaro
99 // provide /etc/lsb-release.
100 parsed_file, err := parseKeyValueFile("/etc/lsb-release")
101 if err == nil {
102 return OSInfo{
103 OSName: strings.ToLower(parsed_file["DISTRIB_ID"]),
104 OSVersion: strings.ToLower(parsed_file["DISTRIB_RELEASE"]),
105 }
106 } else {
107 // If the OS information isn't detectable don't send anything.
108 // MAAS will keep the current OS information.
109 return OSInfo{}
110 }
111}
112
48func GetHostInfo() HostInfo {113func GetHostInfo() HostInfo {
49 hostname, err := os.Hostname()114 hostname, err := os.Hostname()
50 check_error(err)115 check_error(err)
@@ -52,14 +117,18 @@ func GetHostInfo() HostInfo {
52 uname, err := shared.Uname()117 uname, err := shared.Uname()
53 check_error(err)118 check_error(err)
54119
55 osInfo, err := osarch.GetLSBRelease()
56 check_error(err)
57
58 return HostInfo{120 return HostInfo{
59 // These are the API extensions machine-resources reproduces.121 // These are the API extensions machine-resources reproduces.
60 APIExtensions: []string{122 APIExtensions: []string{
61 "resources",123 "resources",
124 "resources_cpu_socket",
125 "resources_gpu",
126 "resources_numa",
62 "resources_v2",127 "resources_v2",
128 "resources_disk_sata",
129 "resources_usb_pci",
130 "resources_cpu_threads_numa",
131 "resources_cpu_core_die",
63 "api_os",132 "api_os",
64 "resources_system",133 "resources_system",
65 },134 },
@@ -71,8 +140,7 @@ func GetHostInfo() HostInfo {
71 Kernel: uname.Sysname,140 Kernel: uname.Sysname,
72 KernelArchitecture: uname.Machine,141 KernelArchitecture: uname.Machine,
73 KernelVersion: uname.Release,142 KernelVersion: uname.Release,
74 OSName: osInfo["NAME"],143 OSInfo: getOSNameVersion(),
75 OSVersion: osInfo["VERSION_ID"],
76 Server: "maas-machine-resources",144 Server: "maas-machine-resources",
77 ServerName: hostname,145 ServerName: hostname,
78 // Use the imported LXD version as the data comes from146 // Use the imported LXD version as the data comes from
diff --git a/src/metadataserver/builtin_scripts/hooks.py b/src/metadataserver/builtin_scripts/hooks.py
index d247e5a..056af1a 100644
--- a/src/metadataserver/builtin_scripts/hooks.py
+++ b/src/metadataserver/builtin_scripts/hooks.py
@@ -662,7 +662,11 @@ def _process_lxd_environment(node, data):
662 # on the running machine and LXD Pods are getting this data from LXD662 # on the running machine and LXD Pods are getting this data from LXD
663 # on the running machine. In those cases the information gathered below663 # on the running machine. In those cases the information gathered below
664 # is correct.664 # is correct.
665 if node.is_controller or node.is_pod:665 if (
666 (node.is_controller or node.is_pod)
667 and data.get("os_name")
668 and data.get("os_version")
669 ):
666 # This is how the hostname gets set on controllers and stays in sync on Pods.670 # This is how the hostname gets set on controllers and stays in sync on Pods.
667 node.hostname = data["server_name"]671 node.hostname = data["server_name"]
668672
diff --git a/src/metadataserver/builtin_scripts/tests/test_hooks.py b/src/metadataserver/builtin_scripts/tests/test_hooks.py
index de3424a..5616ee6 100644
--- a/src/metadataserver/builtin_scripts/tests/test_hooks.py
+++ b/src/metadataserver/builtin_scripts/tests/test_hooks.py
@@ -490,19 +490,19 @@ def make_lxd_host_info(
490 "api_os",490 "api_os",
491 "resources_system",491 "resources_system",
492 ]492 ]
493 if not api_version:493 if api_version is None:
494 api_version = "1.0"494 api_version = "1.0"
495 if not kernel_architecture:495 if kernel_architecture is None:
496 kernel_architecture = random.choice(496 kernel_architecture = random.choice(
497 ["i686", "x86_64", "aarch64", "ppc64le", "s390x", "mips", "mips64"]497 ["i686", "x86_64", "aarch64", "ppc64le", "s390x", "mips", "mips64"]
498 )498 )
499 if not kernel_version:499 if kernel_version is None:
500 kernel_version = factory.make_name("kernel_version")500 kernel_version = factory.make_name("kernel_version")
501 if not os_name:501 if os_name is None:
502 os_name = "Ubuntu"502 os_name = "Ubuntu"
503 if not os_version:503 if os_version is None:
504 os_version = random.choice(["16.04", "18.04", "20.04"])504 os_version = random.choice(["16.04", "18.04", "20.04"])
505 if not server_name:505 if server_name is None:
506 server_name = factory.make_hostname()506 server_name = factory.make_hostname()
507 return {507 return {
508 "api_extensions": api_extensions,508 "api_extensions": api_extensions,
@@ -1270,6 +1270,20 @@ class TestProcessLXDResults(MAASServerTestCase):
1270 self.assertEquals(ubuntu_release["series"], rack.distro_series)1270 self.assertEquals(ubuntu_release["series"], rack.distro_series)
1271 self.assertEquals(server_name, rack.hostname)1271 self.assertEquals(server_name, rack.hostname)
12721272
1273 def test__doesnt_set_os_for_controller_if_blank(self):
1274 osystem = factory.make_name("osystem")
1275 distro_series = factory.make_name("distro_series")
1276 rack = factory.make_RackController(
1277 osystem=osystem, distro_series=distro_series
1278 )
1279 create_IPADDR_OUTPUT_NAME_script(rack, IP_ADDR_OUTPUT)
1280 process_lxd_results(
1281 rack, make_lxd_output_json(os_name="", os_version=""), 0
1282 )
1283 rack = reload_object(rack)
1284 self.assertEquals(osystem, rack.osystem)
1285 self.assertEquals(distro_series, rack.distro_series)
1286
1273 def test__sets_os_for_pod(self):1287 def test__sets_os_for_pod(self):
1274 pod = factory.make_Pod()1288 pod = factory.make_Pod()
1275 node = factory.make_Node(1289 node = factory.make_Node(

Subscribers

People subscribed via source and target branches