Merge ~ltrager/maas:lp1923268 into maas:master

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: 58c29c38561f3b8a0548a55ca70a969f71b7d806
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:lp1923268
Merge into: maas:master
Diff against target: 232 lines (+71/-38)
4 files modified
dev/null (+0/-29)
src/provisioningserver/__main__.py (+1/-3)
src/provisioningserver/boot/grub.py (+12/-6)
src/provisioningserver/boot/tests/test_grub.py (+58/-0)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
MAAS Lander Approve
Review via email: mp+402228@code.launchpad.net

Commit message

LP: #1923268 - Make default grub.cfg architecture agnostic.

Description of the change

I have tested this manually in the CI and was able enlist our AMD64, ARM64, and PPC64 machines.

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

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

STATUS: SUCCESS
COMMIT: 58c29c38561f3b8a0548a55ca70a969f71b7d806

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

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/provisioningserver/__main__.py b/src/provisioningserver/__main__.py
2index 6cf81d9..5f22393 100644
3--- a/src/provisioningserver/__main__.py
4+++ b/src/provisioningserver/__main__.py
5@@ -1,5 +1,5 @@
6 #!/usr/bin/env python3
7-# Copyright 2012-2017 Canonical Ltd. This software is licensed under the
8+# Copyright 2012-2021 Canonical Ltd. This software is licensed under the
9 # GNU Affero General Public License version 3 (see the file LICENSE).
10
11 """Command-line interface for the MAAS provisioning component."""
12@@ -7,7 +7,6 @@
13 import sys
14
15 from provisioningserver import security
16-import provisioningserver.boot.install_grub
17 import provisioningserver.cluster_config_command
18 import provisioningserver.dns.commands.edit_named_options
19 import provisioningserver.dns.commands.get_named_conf
20@@ -39,7 +38,6 @@ RACK_ONLY_COMMANDS = {
21 "check-for-shared-secret": security.CheckForSharedSecretScript,
22 "config": provisioningserver.cluster_config_command,
23 "install-shared-secret": security.InstallSharedSecretScript,
24- "install-uefi-config": provisioningserver.boot.install_grub,
25 "register": provisioningserver.register_command,
26 "support-dump": provisioningserver.support_dump,
27 "upgrade-cluster": provisioningserver.upgrade_cluster,
28diff --git a/src/provisioningserver/boot/grub.py b/src/provisioningserver/boot/grub.py
29index 5528877..6143e88 100644
30--- a/src/provisioningserver/boot/grub.py
31+++ b/src/provisioningserver/boot/grub.py
32@@ -31,9 +31,9 @@ CONFIG_FILE = dedent(
33 # Load based on MAC address first.
34 configfile /grub/grub.cfg-${net_default_mac}
35
36- # Failed to load based on MAC address.
37- # Load amd64 by default, UEFI only supported by 64-bit
38- configfile /grub/grub.cfg-default-amd64
39+ # Failed to load based on MAC address. Load based on the CPU
40+ # architecture.
41+ configfile /grub/grub.cfg-default-${grub_cpu}
42 """
43 )
44
45@@ -41,7 +41,7 @@ CONFIG_FILE = dedent(
46 # format. Required for UEFI as GRUB2 only presents the MAC address
47 # in colon-seperated format.
48 re_mac_address_octet = r"[0-9a-f]{2}"
49-re_mac_address = re.compile(":".join(repeat(re_mac_address_octet, 6)))
50+re_mac_address = re.compile("[:-]".join(repeat(re_mac_address_octet, 6)))
51
52 # Match the grub/grub.cfg-* request for UEFI (aka. GRUB2)
53 re_config_file = r"""
54@@ -53,10 +53,10 @@ re_config_file = r"""
55 (?P<mac>{re_mac_address.pattern}) # Capture UEFI MAC.
56 | # or "default"
57 default
58- (?: # perhaps with specified arch, with a separator of '-'
59+ (?: # perhaps with specified arch, with a separator of '-'
60 [-](?P<arch>\w+) # arch
61 (?:-(?P<subarch>\w+))? # optional subarch
62- )?
63+ )?
64 )
65 $
66 """
67@@ -94,6 +94,12 @@ class UEFIAMD64BootMethod(BootMethod):
68 mac = params.get("mac")
69 if mac is not None:
70 params["mac"] = mac.replace(":", "-")
71+ arch = params.get("arch")
72+ if arch == "x86_64":
73+ # MAAS uses Debian architectures while GRUB uses standard Linux
74+ # architectures. Convert x86_64 to amd64 as they mean the same
75+ # thing.
76+ params["arch"] = "amd64"
77
78 return params
79
80diff --git a/src/provisioningserver/boot/install_grub.py b/src/provisioningserver/boot/install_grub.py
81deleted file mode 100644
82index 27ebd36..0000000
83--- a/src/provisioningserver/boot/install_grub.py
84+++ /dev/null
85@@ -1,36 +0,0 @@
86-# Copyright 2014-2015 Canonical Ltd. This software is licensed under the
87-# GNU Affero General Public License version 3 (see the file LICENSE).
88-
89-"""Install a GRUB2 pre-boot loader config for TFTP download."""
90-
91-
92-import os
93-
94-from provisioningserver.config import ClusterConfiguration
95-from provisioningserver.utils.fs import write_text_file
96-
97-CONFIG_FILE = """
98-# MAAS GRUB2 pre-loader configuration file
99-
100-# Load based on MAC address first.
101-configfile /grub/grub.cfg-${net_default_mac}
102-
103-# Failed to load based on MAC address.
104-# Load amd64 by default, UEFI only supported by 64-bit
105-configfile /grub/grub.cfg-default-amd64
106-"""
107-
108-
109-def add_arguments(parser):
110- pass
111-
112-
113-def run(args):
114- """Install a GRUB2 pre-boot loader config into the TFTP
115- directory structure.
116- """
117- with ClusterConfiguration.open() as config:
118- if not os.path.exists(config.grub_root):
119- os.makedirs(config.grub_root)
120- destination_file = os.path.join(config.grub_root, "grub.cfg")
121- write_text_file(destination_file, CONFIG_FILE)
122diff --git a/src/provisioningserver/boot/tests/test_grub.py b/src/provisioningserver/boot/tests/test_grub.py
123index 688b6c0..449d7b2 100644
124--- a/src/provisioningserver/boot/tests/test_grub.py
125+++ b/src/provisioningserver/boot/tests/test_grub.py
126@@ -330,6 +330,8 @@ class TestUEFIAMD64BootMethodRegex(MAASTestCase):
127 )
128
129 def test_re_config_file_does_not_match_default_grub_config_file(self):
130+ # The default grub.cfg is on the filesystem let the normal handler
131+ # grab it.
132 self.assertIsNone(re_config_file.match(b"grub/grub.cfg"))
133
134 def test_re_config_file_with_default(self):
135@@ -362,6 +364,62 @@ class TestUEFIAMD64BootMethodRegex(MAASTestCase):
136 class TestUEFIAMD64BootMethod(MAASTestCase):
137 """Tests `provisioningserver.boot.uefi_amd64.UEFIAMD64BootMethod`."""
138
139+ def test_match_path_none(self):
140+ method = UEFIAMD64BootMethod()
141+ backend = random.choice(["http", "tftp"])
142+ self.assertIsNone(
143+ method.match_path(backend, factory.make_string().encode())
144+ )
145+
146+ def test_match_path_mac_colon(self):
147+ method = UEFIAMD64BootMethod()
148+ backend = random.choice(["http", "tftp"])
149+ mac = factory.make_mac_address()
150+ self.assertEqual(
151+ {"mac": mac.replace(":", "-")},
152+ method.match_path(backend, f"/grub/grub.cfg-{mac}".encode()),
153+ )
154+
155+ def test_match_path_mac_dash(self):
156+ method = UEFIAMD64BootMethod()
157+ backend = random.choice(["http", "tftp"])
158+ mac = factory.make_mac_address().replace(":", "-")
159+ self.assertEqual(
160+ {"mac": mac},
161+ method.match_path(backend, f"/grub/grub.cfg-{mac}".encode()),
162+ )
163+
164+ def test_match_path_arch(self):
165+ method = UEFIAMD64BootMethod()
166+ backend = random.choice(["http", "tftp"])
167+ arch = factory.make_string()
168+ self.assertEqual(
169+ {"arch": arch},
170+ method.match_path(
171+ backend, f"/grub/grub.cfg-default-{arch}".encode()
172+ ),
173+ )
174+
175+ def test_match_path_arch_x86_64(self):
176+ method = UEFIAMD64BootMethod()
177+ backend = random.choice(["http", "tftp"])
178+ self.assertEqual(
179+ {"arch": "amd64"},
180+ method.match_path(backend, b"/grub/grub.cfg-default-x86_64"),
181+ )
182+
183+ def test_match_path_arch_subarch(self):
184+ method = UEFIAMD64BootMethod()
185+ backend = random.choice(["http", "tftp"])
186+ arch = factory.make_string()
187+ subarch = factory.make_string()
188+ self.assertEqual(
189+ {"arch": arch, "subarch": subarch},
190+ method.match_path(
191+ backend, f"/grub/grub.cfg-default-{arch}-{subarch}".encode()
192+ ),
193+ )
194+
195 def test_link_bootloader_creates_grub_cfg(self):
196 method = UEFIAMD64BootMethod()
197 with tempdir() as tmp:
198diff --git a/src/provisioningserver/boot/tests/test_install_grub.py b/src/provisioningserver/boot/tests/test_install_grub.py
199deleted file mode 100644
200index 6cb876f..0000000
201--- a/src/provisioningserver/boot/tests/test_install_grub.py
202+++ /dev/null
203@@ -1,29 +0,0 @@
204-# Copyright 2014-2016 Canonical Ltd. This software is licensed under the
205-# GNU Affero General Public License version 3 (see the file LICENSE).
206-
207-"""Tests for the install_grub command."""
208-
209-
210-import os.path
211-
212-from testtools.matchers import FileExists
213-
214-from maastesting.factory import factory
215-from maastesting.testcase import MAASTestCase
216-import provisioningserver.boot.install_grub
217-from provisioningserver.testing.config import ClusterConfigurationFixture
218-from provisioningserver.utils.script import MainScript
219-
220-
221-class TestInstallGrub(MAASTestCase):
222- def test_integration(self):
223- tftproot = self.make_dir()
224- self.useFixture(ClusterConfigurationFixture(tftp_root=tftproot))
225-
226- action = factory.make_name("action")
227- script = MainScript(action)
228- script.register(action, provisioningserver.boot.install_grub)
229- script.execute((action,))
230-
231- config_filename = os.path.join("grub", "grub.cfg")
232- self.assertThat(os.path.join(tftproot, config_filename), FileExists())

Subscribers

People subscribed via source and target branches