Merge ~ltrager/maas:lp1923268_2.9 into maas:2.9

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: 7e807e75aa72cf998ef656c8f1e05e940752aea3
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:lp1923268_2.9
Merge into: maas:2.9
Prerequisite: ~ltrager/maas:grub_debug_2.9
Diff against target: 275 lines (+100/-40)
4 files modified
dev/null (+0/-29)
src/provisioningserver/__main__.py (+1/-3)
src/provisioningserver/boot/tests/test_uefi_amd64.py (+83/-0)
src/provisioningserver/boot/uefi_amd64.py (+16/-8)
Reviewer Review Type Date Requested Status
Lee Trager (community) Approve
Review via email: mp+402288@code.launchpad.net

Commit message

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

Backport of f606805

To post a comment you must log in.
Revision history for this message
Lee Trager (ltrager) :
review: Approve
~ltrager/maas:lp1923268_2.9 updated
cb8f5f0... by Lee Trager

Add GRUB mapping for PPC64EL

9dce8a1... by Lee Trager

Merge branch '2.9' into grub_debug_2.9

30ceda5... by Lee Trager

Merge branch 'grub_debug_2.9' into lp1923268_2.9

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/install_grub.py b/src/provisioningserver/boot/install_grub.py
29deleted file mode 100644
30index 27ebd36..0000000
31--- a/src/provisioningserver/boot/install_grub.py
32+++ /dev/null
33@@ -1,36 +0,0 @@
34-# Copyright 2014-2015 Canonical Ltd. This software is licensed under the
35-# GNU Affero General Public License version 3 (see the file LICENSE).
36-
37-"""Install a GRUB2 pre-boot loader config for TFTP download."""
38-
39-
40-import os
41-
42-from provisioningserver.config import ClusterConfiguration
43-from provisioningserver.utils.fs import write_text_file
44-
45-CONFIG_FILE = """
46-# MAAS GRUB2 pre-loader configuration file
47-
48-# Load based on MAC address first.
49-configfile /grub/grub.cfg-${net_default_mac}
50-
51-# Failed to load based on MAC address.
52-# Load amd64 by default, UEFI only supported by 64-bit
53-configfile /grub/grub.cfg-default-amd64
54-"""
55-
56-
57-def add_arguments(parser):
58- pass
59-
60-
61-def run(args):
62- """Install a GRUB2 pre-boot loader config into the TFTP
63- directory structure.
64- """
65- with ClusterConfiguration.open() as config:
66- if not os.path.exists(config.grub_root):
67- os.makedirs(config.grub_root)
68- destination_file = os.path.join(config.grub_root, "grub.cfg")
69- write_text_file(destination_file, CONFIG_FILE)
70diff --git a/src/provisioningserver/boot/tests/test_install_grub.py b/src/provisioningserver/boot/tests/test_install_grub.py
71deleted file mode 100644
72index 6cb876f..0000000
73--- a/src/provisioningserver/boot/tests/test_install_grub.py
74+++ /dev/null
75@@ -1,29 +0,0 @@
76-# Copyright 2014-2016 Canonical Ltd. This software is licensed under the
77-# GNU Affero General Public License version 3 (see the file LICENSE).
78-
79-"""Tests for the install_grub command."""
80-
81-
82-import os.path
83-
84-from testtools.matchers import FileExists
85-
86-from maastesting.factory import factory
87-from maastesting.testcase import MAASTestCase
88-import provisioningserver.boot.install_grub
89-from provisioningserver.testing.config import ClusterConfigurationFixture
90-from provisioningserver.utils.script import MainScript
91-
92-
93-class TestInstallGrub(MAASTestCase):
94- def test_integration(self):
95- tftproot = self.make_dir()
96- self.useFixture(ClusterConfigurationFixture(tftp_root=tftproot))
97-
98- action = factory.make_name("action")
99- script = MainScript(action)
100- script.register(action, provisioningserver.boot.install_grub)
101- script.execute((action,))
102-
103- config_filename = os.path.join("grub", "grub.cfg")
104- self.assertThat(os.path.join(tftproot, config_filename), FileExists())
105diff --git a/src/provisioningserver/boot/tests/test_uefi_amd64.py b/src/provisioningserver/boot/tests/test_uefi_amd64.py
106index a8a23bf..886dca5 100644
107--- a/src/provisioningserver/boot/tests/test_uefi_amd64.py
108+++ b/src/provisioningserver/boot/tests/test_uefi_amd64.py
109@@ -5,6 +5,7 @@
110
111
112 import os
113+import random
114 import re
115 from unittest.mock import sentinel
116
117@@ -269,6 +270,8 @@ class TestUEFIAMD64BootMethodRegex(MAASTestCase):
118 )
119
120 def test_re_config_file_does_not_match_default_grub_config_file(self):
121+ # The default grub.cfg is on the filesystem let the normal handler
122+ # grab it.
123 self.assertIsNone(re_config_file.match(b"grub/grub.cfg"))
124
125 def test_re_config_file_with_default(self):
126@@ -301,6 +304,86 @@ class TestUEFIAMD64BootMethodRegex(MAASTestCase):
127 class TestUEFIAMD64BootMethod(MAASTestCase):
128 """Tests `provisioningserver.boot.uefi_amd64.UEFIAMD64BootMethod`."""
129
130+ def test_match_path_none(self):
131+ method = UEFIAMD64BootMethod()
132+ backend = random.choice(["http", "tftp"])
133+ self.assertIsNone(
134+ method.match_path(backend, factory.make_string().encode())
135+ )
136+
137+ def test_match_path_mac_colon(self):
138+ method = UEFIAMD64BootMethod()
139+ backend = random.choice(["http", "tftp"])
140+ mac = factory.make_mac_address()
141+ self.assertEqual(
142+ {"mac": mac.replace(":", "-")},
143+ method.match_path(backend, f"/grub/grub.cfg-{mac}".encode()),
144+ )
145+
146+ def test_match_path_mac_dash(self):
147+ method = UEFIAMD64BootMethod()
148+ backend = random.choice(["http", "tftp"])
149+ mac = factory.make_mac_address().replace(":", "-")
150+ self.assertEqual(
151+ {"mac": mac},
152+ method.match_path(backend, f"/grub/grub.cfg-{mac}".encode()),
153+ )
154+
155+ def test_match_path_arch(self):
156+ method = UEFIAMD64BootMethod()
157+ backend = random.choice(["http", "tftp"])
158+ arch = factory.make_string()
159+ self.assertEqual(
160+ {"arch": arch},
161+ method.match_path(
162+ backend, f"/grub/grub.cfg-default-{arch}".encode()
163+ ),
164+ )
165+
166+ def test_match_path_arch_x86_64(self):
167+ method = UEFIAMD64BootMethod()
168+ backend = random.choice(["http", "tftp"])
169+ self.assertEqual(
170+ {"arch": "amd64"},
171+ method.match_path(backend, b"/grub/grub.cfg-default-x86_64"),
172+ )
173+
174+ def test_match_path_arch_powerpc(self):
175+ method = UEFIAMD64BootMethod()
176+ backend = random.choice(["http", "tftp"])
177+ self.assertEqual(
178+ {"arch": "ppc64el"},
179+ method.match_path(backend, b"/grub/grub.cfg-default-powerpc"),
180+ )
181+
182+ def test_match_path_arch_ppc64(self):
183+ method = UEFIAMD64BootMethod()
184+ backend = random.choice(["http", "tftp"])
185+ self.assertEqual(
186+ {"arch": "ppc64el"},
187+ method.match_path(backend, b"/grub/grub.cfg-default-ppc64"),
188+ )
189+
190+ def test_match_path_arch_ppc64le(self):
191+ method = UEFIAMD64BootMethod()
192+ backend = random.choice(["http", "tftp"])
193+ self.assertEqual(
194+ {"arch": "ppc64el"},
195+ method.match_path(backend, b"/grub/grub.cfg-default-ppc64le"),
196+ )
197+
198+ def test_match_path_arch_subarch(self):
199+ method = UEFIAMD64BootMethod()
200+ backend = random.choice(["http", "tftp"])
201+ arch = factory.make_string()
202+ subarch = factory.make_string()
203+ self.assertEqual(
204+ {"arch": arch, "subarch": subarch},
205+ method.match_path(
206+ backend, f"/grub/grub.cfg-default-{arch}-{subarch}".encode()
207+ ),
208+ )
209+
210 def test_link_bootloader_creates_grub_cfg(self):
211 method = UEFIAMD64BootMethod()
212 with tempdir() as tmp:
213diff --git a/src/provisioningserver/boot/uefi_amd64.py b/src/provisioningserver/boot/uefi_amd64.py
214index 74b007a..1399fa0 100644
215--- a/src/provisioningserver/boot/uefi_amd64.py
216+++ b/src/provisioningserver/boot/uefi_amd64.py
217@@ -1,4 +1,4 @@
218-# Copyright 2014-2016 Canonical Ltd. This software is licensed under the
219+# Copyright 2014-2021 Canonical Ltd. This software is licensed under the
220 # GNU Affero General Public License version 3 (see the file LICENSE).
221
222 """UEFI AMD64 Boot Method"""
223@@ -24,11 +24,11 @@ CONFIG_FILE = dedent(
224 # MAAS GRUB2 pre-loader configuration file
225
226 # Load based on MAC address first.
227- configfile (pxe)/grub/grub.cfg-${net_default_mac}
228+ configfile /grub/grub.cfg-${net_default_mac}
229
230- # Failed to load based on MAC address.
231- # Load amd64 by default, UEFI only supported by 64-bit
232- configfile (pxe)/grub/grub.cfg-default-amd64
233+ # Failed to load based on MAC address. Load based on the CPU
234+ # architecture.
235+ configfile /grub/grub.cfg-default-${grub_cpu}
236 """
237 )
238
239@@ -36,7 +36,7 @@ CONFIG_FILE = dedent(
240 # format. Required for UEFI as GRUB2 only presents the MAC address
241 # in colon-seperated format.
242 re_mac_address_octet = r"[0-9a-f]{2}"
243-re_mac_address = re.compile(":".join(repeat(re_mac_address_octet, 6)))
244+re_mac_address = re.compile("[:-]".join(repeat(re_mac_address_octet, 6)))
245
246 # Match the grub/grub.cfg-* request for UEFI (aka. GRUB2)
247 re_config_file = r"""
248@@ -48,10 +48,10 @@ re_config_file = r"""
249 (?P<mac>{re_mac_address.pattern}) # Capture UEFI MAC.
250 | # or "default"
251 default
252- (?: # perhaps with specified arch, with a separator of '-'
253+ (?: # perhaps with specified arch, with a separator of '-'
254 [-](?P<arch>\w+) # arch
255 (?:-(?P<subarch>\w+))? # optional subarch
256- )?
257+ )?
258 )
259 $
260 """
261@@ -90,6 +90,14 @@ class UEFIAMD64BootMethod(BootMethod):
262 if mac is not None:
263 params["mac"] = mac.replace(":", "-")
264
265+ # MAAS uses Debian architectures while GRUB uses standard Linux
266+ # architectures.
267+ arch = params.get("arch")
268+ if arch == "x86_64":
269+ params["arch"] = "amd64"
270+ elif arch in {"powerpc", "ppc64", "ppc64le"}:
271+ params["arch"] = "ppc64el"
272+
273 return params
274
275 def get_reader(self, backend, kernel_params, **extra):

Subscribers

People subscribed via source and target branches