Merge ~ltrager/maas:ibm_z_partition into maas:master

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: d442febf09fb6f157f5d7460bbccf6298a9d1aa3
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:ibm_z_partition
Merge into: maas:master
Diff against target: 380 lines (+273/-4)
11 files modified
src/maasserver/models/node.py (+4/-2)
src/maasserver/models/tests/test_node.py (+5/-1)
src/provisioningserver/boot/__init__.py (+5/-1)
src/provisioningserver/boot/s390x_partition.py (+80/-0)
src/provisioningserver/boot/tests/test_s390x_partition.py (+166/-0)
src/provisioningserver/templates/s390x_partition/config.commissioning.s390x.template (+7/-0)
src/provisioningserver/templates/s390x_partition/config.enlist.s390x.template (+1/-0)
src/provisioningserver/templates/s390x_partition/config.install.s390x.template (+1/-0)
src/provisioningserver/templates/s390x_partition/config.local.s390x.template (+2/-0)
src/provisioningserver/templates/s390x_partition/config.poweroff.s390x.template (+1/-0)
src/provisioningserver/templates/s390x_partition/config.xinstall.s390x.template (+1/-0)
Reviewer Review Type Date Requested Status
Björn Tillenius Approve
MAAS Lander Approve
Review via email: mp+398408@code.launchpad.net

Commit message

Add boot support for IBM Z Partitions.

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

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

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/9272/console
COMMIT: fb7449dbfc652b26ea481179db00db429aa52520

review: Needs Fixing
Revision history for this message
Adam Collard (adam-collard) wrote :

jenkins: !test

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

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

STATUS: SUCCESS
COMMIT: fb7449dbfc652b26ea481179db00db429aa52520

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

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

STATUS: SUCCESS
COMMIT: d442febf09fb6f157f5d7460bbccf6298a9d1aa3

review: Approve
Revision history for this message
Björn Tillenius (bjornt) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
2index f58429f..b6f5008 100644
3--- a/src/maasserver/models/node.py
4+++ b/src/maasserver/models/node.py
5@@ -1,4 +1,4 @@
6-# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
7+# Copyright 2012-2021 Canonical Ltd. This software is licensed under the
8 # GNU Affero General Public License version 3 (see the file LICENSE).
9
10 """Node objects."""
11@@ -235,7 +235,9 @@ maaslog = get_maas_logger("node")
12
13 # Holds the known `bios_boot_methods`. If `bios_boot_method` is not in this
14 # list then it will fallback to `DEFAULT_BIOS_BOOT_METHOD`.
15-KNOWN_BIOS_BOOT_METHODS = frozenset(["pxe", "uefi", "powernv", "powerkvm"])
16+KNOWN_BIOS_BOOT_METHODS = frozenset(
17+ ["pxe", "uefi", "powernv", "powerkvm", "s390x_partition"]
18+)
19
20 # Default `bios_boot_method`. See `KNOWN_BIOS_BOOT_METHOD` above for usage.
21 DEFAULT_BIOS_BOOT_METHOD = "pxe"
22diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
23index 384cb4a..83489a4 100644
24--- a/src/maasserver/models/tests/test_node.py
25+++ b/src/maasserver/models/tests/test_node.py
26@@ -1,4 +1,4 @@
27-# Copyright 2012-2020 Canonical Ltd. This software is licensed under the
28+# Copyright 2012-2021 Canonical Ltd. This software is licensed under the
29 # GNU Affero General Public License version 3 (see the file LICENSE).
30
31 """Test maasserver models."""
32@@ -1369,6 +1369,10 @@ class TestNode(MAASServerTestCase):
33 node = factory.make_Node(bios_boot_method="powerkvm")
34 self.assertEqual("powerkvm", node.get_bios_boot_method())
35
36+ def test_get_bios_boot_method_returns_s390x_partition(self):
37+ node = factory.make_Node(bios_boot_method="s390x_partition")
38+ self.assertEqual("s390x_partition", node.get_bios_boot_method())
39+
40 def test_get_bios_boot_method_ipmi_efi_fallback(self):
41 ipmi_efi = factory.make_BMC(
42 power_type="ipmi",
43diff --git a/src/provisioningserver/boot/__init__.py b/src/provisioningserver/boot/__init__.py
44index d4b13e2..2e20863 100644
45--- a/src/provisioningserver/boot/__init__.py
46+++ b/src/provisioningserver/boot/__init__.py
47@@ -1,4 +1,4 @@
48-# Copyright 2012-2018 Canonical Ltd. This software is licensed under the
49+# Copyright 2012-2021 Canonical Ltd. This software is licensed under the
50 # GNU Affero General Public License version 3 (see the file LICENSE).
51
52 """Boot Methods."""
53@@ -439,6 +439,9 @@ from provisioningserver.boot.pxe import PXEBootMethod # noqa:E402 isort:skip
54 from provisioningserver.boot.s390x import ( # noqa:E402 isort:skip
55 S390XBootMethod,
56 )
57+from provisioningserver.boot.s390x_partition import ( # noqa:E402 isort:skip
58+ S390XPartitionBootMethod,
59+)
60 from provisioningserver.boot.uefi_amd64 import ( # noqa:E402 isort:skip
61 UEFIAMD64BootMethod,
62 )
63@@ -460,6 +463,7 @@ builtin_boot_methods = [
64 PowerNVBootMethod(),
65 WindowsPXEBootMethod(),
66 S390XBootMethod(),
67+ S390XPartitionBootMethod(),
68 ]
69 for method in builtin_boot_methods:
70 BootMethodRegistry.register_item(method.name, method)
71diff --git a/src/provisioningserver/boot/s390x_partition.py b/src/provisioningserver/boot/s390x_partition.py
72new file mode 100644
73index 0000000..c45e348
74--- /dev/null
75+++ b/src/provisioningserver/boot/s390x_partition.py
76@@ -0,0 +1,80 @@
77+# Copyright 2021 Canonical Ltd. This software is licensed under the
78+# GNU Affero General Public License version 3 (see the file LICENSE).
79+
80+"""S390X DPM Partition Boot Method"""
81+
82+import tempita
83+
84+from provisioningserver.boot import BootMethod, BytesReader, get_remote_mac
85+from provisioningserver.boot.s390x import format_bootif
86+from provisioningserver.kernel_opts import compose_kernel_command_line
87+
88+
89+class S390XPartitionBootMethod(BootMethod):
90+
91+ name = "s390x_partition"
92+ bios_boot_method = "s390x_partition"
93+ template_subdir = "s390x_partition"
94+ # S390X partitions has its bootloader built into the firmware. The
95+ # "bootloader" provided must be the bootloader configuration file. The
96+ # file format is similar to pxelinux.cfg but supports limited options
97+ # and requires an '=' to be used between keys and values.
98+ # https://www.ibm.com/support/pages/sites/default/files/inline-files/SB10-7176-01.pdf
99+ bootloader_path = "s390x_partition/maas"
100+ arch_octet = "00:20"
101+ user_class = None
102+
103+ def match_path(self, backend, path):
104+ """Checks path for the configuration file that needs to be
105+ generated.
106+
107+ :param backend: requesting backend
108+ :param path: requested path
109+ :return: dict of match params from path, None if no match
110+ """
111+ if path == self.bootloader_path.encode():
112+ return {
113+ "arch": "s390x",
114+ "mac": get_remote_mac(),
115+ }
116+ else:
117+ return None
118+
119+ def get_reader(self, backend, kernel_params, mac=None, **extra):
120+ """Render a configuration file as a unicode string.
121+
122+ :param backend: requesting backend
123+ :param kernel_params: An instance of `KernelParameters`.
124+ :param mac: Optional MAC address discovered by `match_path`.
125+ :param extra: Allow for other arguments. This is a safety valve;
126+ parameters generated in another component (for example, see
127+ `TFTPBackend.get_boot_method_reader`) won't cause this to break.
128+ """
129+ template = self.get_template(
130+ kernel_params.purpose, kernel_params.arch, kernel_params.subarch
131+ )
132+ namespace = self.compose_template_namespace(kernel_params)
133+
134+ def kernel_command(params):
135+ cmd_line = compose_kernel_command_line(params)
136+ # Modify the kernel_command to inject the BOOTIF. S390X doesn't
137+ # support the IPAPPEND pxelinux flag.
138+ if mac is not None:
139+ return "%s BOOTIF=%s" % (cmd_line, format_bootif(mac))
140+ return cmd_line
141+
142+ namespace["kernel_command"] = kernel_command
143+
144+ # We are going to do 2 passes of tempita substitution because there
145+ # may be things like kernel params which include variables that can
146+ # only be populated at run time and thus contain variables themselves.
147+ # For example, an OS may need a kernel parameter that points back to
148+ # fs_host and the kernel parameter comes through as part of the simple
149+ # stream.
150+ step1 = template.substitute(namespace)
151+ return BytesReader(
152+ tempita.Template(step1).substitute(namespace).encode("utf-8")
153+ )
154+
155+ def link_bootloader(self, destination):
156+ """Does nothing. No extra boot files are required."""
157diff --git a/src/provisioningserver/boot/tests/test_s390x_partition.py b/src/provisioningserver/boot/tests/test_s390x_partition.py
158new file mode 100644
159index 0000000..063df7a
160--- /dev/null
161+++ b/src/provisioningserver/boot/tests/test_s390x_partition.py
162@@ -0,0 +1,166 @@
163+# Copyright 2021 Canonical Ltd. This software is licensed under the
164+# GNU Affero General Public License version 3 (see the file LICENSE).
165+
166+"""Tests for `provisioningserver.boot.s390x`."""
167+
168+
169+import random
170+import re
171+
172+from testtools.matchers import MatchesAll, MatchesRegex
173+
174+from maastesting.factory import factory
175+from maastesting.testcase import MAASTestCase
176+from provisioningserver.boot import s390x_partition as s390x_partition_module
177+from provisioningserver.boot.s390x import format_bootif
178+from provisioningserver.boot.s390x_partition import S390XPartitionBootMethod
179+from provisioningserver.boot.tftppath import compose_image_path
180+from provisioningserver.tests.test_kernel_opts import make_kernel_parameters
181+
182+
183+class TestS390XPartitionBootMethod(MAASTestCase):
184+ def test_settings(self):
185+ s390x_partition = S390XPartitionBootMethod()
186+
187+ self.assertEqual("s390x_partition", s390x_partition.name)
188+ self.assertEqual("s390x_partition", s390x_partition.bios_boot_method)
189+ self.assertEqual("s390x_partition", s390x_partition.template_subdir)
190+ self.assertEqual(
191+ "s390x_partition/maas", s390x_partition.bootloader_path
192+ )
193+ self.assertEqual("00:20", s390x_partition.arch_octet)
194+ self.assertIsNone(s390x_partition.user_class)
195+
196+ def test_match_path_matches(self):
197+ s390x_partition = S390XPartitionBootMethod()
198+ mac = factory.make_mac_address()
199+ self.patch(s390x_partition_module, "get_remote_mac").return_value = mac
200+
201+ self.assertEqual(
202+ {
203+ "arch": "s390x",
204+ "mac": mac,
205+ },
206+ s390x_partition.match_path(
207+ factory.make_name("backend"),
208+ s390x_partition.bootloader_path.encode(),
209+ ),
210+ )
211+
212+ def test_match_path_doesnt_match(self):
213+ s390x_partition = S390XPartitionBootMethod()
214+
215+ self.assertIsNone(
216+ s390x_partition.match_path(
217+ factory.make_name("backend"), factory.make_name("path")
218+ )
219+ )
220+
221+ def test_get_reader_ephemeral(self):
222+ s390x_partition = S390XPartitionBootMethod()
223+ mac = factory.make_mac_address()
224+ params = make_kernel_parameters(
225+ self,
226+ arch="s390x",
227+ purpose=random.choice(
228+ ["commissioning", "enlist", "install", "xinstall"]
229+ ),
230+ )
231+
232+ output = s390x_partition.get_reader(None, params, mac)
233+ output = output.read(output.size).decode()
234+ image_dir = compose_image_path(
235+ osystem=params.osystem,
236+ arch=params.arch,
237+ subarch=params.subarch,
238+ release=params.release,
239+ label=params.label,
240+ )
241+
242+ self.assertThat(
243+ output,
244+ MatchesAll(
245+ MatchesRegex(
246+ r".*^\s+kernel=%s/%s$"
247+ % (re.escape(image_dir), params.kernel),
248+ re.MULTILINE | re.DOTALL,
249+ ),
250+ MatchesRegex(
251+ r".*^\s+initrd=%s/%s$"
252+ % (re.escape(image_dir), params.initrd),
253+ re.MULTILINE | re.DOTALL,
254+ ),
255+ MatchesRegex(
256+ r".*^\s+append=.*BOOTIF=%s$" % format_bootif(mac),
257+ re.MULTILINE | re.DOTALL,
258+ ),
259+ ),
260+ )
261+
262+ def test_get_reader_ephemeral_no_mac(self):
263+ s390x_partition = S390XPartitionBootMethod()
264+ params = make_kernel_parameters(
265+ self,
266+ arch="s390x",
267+ purpose=random.choice(
268+ ["commissioning", "enlist", "install", "xinstall"]
269+ ),
270+ )
271+
272+ output = s390x_partition.get_reader(None, params)
273+ output = output.read(output.size).decode()
274+ image_dir = compose_image_path(
275+ osystem=params.osystem,
276+ arch=params.arch,
277+ subarch=params.subarch,
278+ release=params.release,
279+ label=params.label,
280+ )
281+
282+ self.assertThat(
283+ output,
284+ MatchesAll(
285+ MatchesRegex(
286+ r".*^\s+kernel=%s/%s$"
287+ % (re.escape(image_dir), params.kernel),
288+ re.MULTILINE | re.DOTALL,
289+ ),
290+ MatchesRegex(
291+ r".*^\s+initrd=%s/%s$"
292+ % (re.escape(image_dir), params.initrd),
293+ re.MULTILINE | re.DOTALL,
294+ ),
295+ MatchesRegex(r".*^\s+append=.*$", re.MULTILINE | re.DOTALL),
296+ ),
297+ )
298+
299+ def test_get_reader_poweroff(self):
300+ s390x_partition = S390XPartitionBootMethod()
301+ mac = factory.make_mac_address()
302+ params = make_kernel_parameters(
303+ self, arch="s390x", purpose=random.choice(["local", "poweroff"])
304+ )
305+
306+ output = s390x_partition.get_reader(None, params, mac)
307+ output = [
308+ line
309+ for line in output.read(output.size).decode().splitlines()
310+ if line.split("#", 1)[0].strip()
311+ ]
312+
313+ self.assertEqual([], output)
314+
315+ def test_get_reader_poweroff_no_mac(self):
316+ s390x_partition = S390XPartitionBootMethod()
317+ params = make_kernel_parameters(
318+ self, arch="s390x", purpose=random.choice(["local", "poweroff"])
319+ )
320+
321+ output = s390x_partition.get_reader(None, params)
322+ output = [
323+ line
324+ for line in output.read(output.size).decode().splitlines()
325+ if line.split("#", 1)[0].strip()
326+ ]
327+
328+ self.assertEqual([], output)
329diff --git a/src/provisioningserver/templates/s390x_partition/config.commissioning.s390x.template b/src/provisioningserver/templates/s390x_partition/config.commissioning.s390x.template
330new file mode 100644
331index 0000000..d6a4d5a
332--- /dev/null
333+++ b/src/provisioningserver/templates/s390x_partition/config.commissioning.s390x.template
334@@ -0,0 +1,7 @@
335+PROMPT 0
336+DEFAULT netboot
337+TIMEOUT 0
338+LABEL netboot
339+ kernel={{kernel_params | kernel_path }}
340+ initrd={{kernel_params | initrd_path }}
341+ append={{kernel_params | kernel_command}}
342diff --git a/src/provisioningserver/templates/s390x_partition/config.enlist.s390x.template b/src/provisioningserver/templates/s390x_partition/config.enlist.s390x.template
343new file mode 120000
344index 0000000..0b09e93
345--- /dev/null
346+++ b/src/provisioningserver/templates/s390x_partition/config.enlist.s390x.template
347@@ -0,0 +1 @@
348+config.commissioning.s390x.template
349\ No newline at end of file
350diff --git a/src/provisioningserver/templates/s390x_partition/config.install.s390x.template b/src/provisioningserver/templates/s390x_partition/config.install.s390x.template
351new file mode 120000
352index 0000000..0b09e93
353--- /dev/null
354+++ b/src/provisioningserver/templates/s390x_partition/config.install.s390x.template
355@@ -0,0 +1 @@
356+config.commissioning.s390x.template
357\ No newline at end of file
358diff --git a/src/provisioningserver/templates/s390x_partition/config.local.s390x.template b/src/provisioningserver/templates/s390x_partition/config.local.s390x.template
359new file mode 100644
360index 0000000..a35672e
361--- /dev/null
362+++ b/src/provisioningserver/templates/s390x_partition/config.local.s390x.template
363@@ -0,0 +1,2 @@
364+# S390X partition must be configured via the HMC to boot locally
365+# No config will cause the partition to turn off.
366diff --git a/src/provisioningserver/templates/s390x_partition/config.poweroff.s390x.template b/src/provisioningserver/templates/s390x_partition/config.poweroff.s390x.template
367new file mode 100644
368index 0000000..4541aca
369--- /dev/null
370+++ b/src/provisioningserver/templates/s390x_partition/config.poweroff.s390x.template
371@@ -0,0 +1 @@
372+# Sending no config turns off a S390X partition
373diff --git a/src/provisioningserver/templates/s390x_partition/config.xinstall.s390x.template b/src/provisioningserver/templates/s390x_partition/config.xinstall.s390x.template
374new file mode 120000
375index 0000000..0b09e93
376--- /dev/null
377+++ b/src/provisioningserver/templates/s390x_partition/config.xinstall.s390x.template
378@@ -0,0 +1 @@
379+config.commissioning.s390x.template
380\ No newline at end of file

Subscribers

People subscribed via source and target branches