Merge ~cgrabowski/maas:fix_lp1982866_3.1_backport into maas:3.1

Proposed by Christian Grabowski
Status: Merged
Approved by: Christian Grabowski
Approved revision: cd09b8b718cbbd68b07c4c9df9b3d7091e57c867
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~cgrabowski/maas:fix_lp1982866_3.1_backport
Merge into: maas:3.1
Diff against target: 316 lines (+187/-14)
6 files modified
src/maasserver/forms/__init__.py (+17/-4)
src/maasserver/forms/tests/test_bootresource.py (+40/-5)
src/maasserver/models/bootresource.py (+11/-0)
src/maasserver/models/tests/test_bootresource.py (+12/-0)
src/maasserver/rpc/boot.py (+1/-4)
src/maasserver/rpc/tests/test_boot.py (+106/-1)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
MAAS Maintainers Pending
Review via email: mp+430384@code.launchpad.net

Commit message

use BootResource's method for splitting base image to get a default value in get_boot_config_for_machine()
(cherry picked from commit d58366ec06df69f22273b72338e80e9459ee3f7c)

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

UNIT TESTS
-b fix_lp1982866_3.1_backport lp:~cgrabowski/maas/+git/maas into -b 3.1 lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas-tester/753/consoleText
COMMIT: 446f3819632f3ea854ba22bca64e59c2739031f6

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

UNIT TESTS
-b fix_lp1982866_3.1_backport lp:~cgrabowski/maas/+git/maas into -b 3.1 lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas-tester/756/consoleText
COMMIT: cd09b8b718cbbd68b07c4c9df9b3d7091e57c867

review: Needs Fixing
Revision history for this message
Christian Grabowski (cgrabowski) wrote :

jenkins: !test

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

UNIT TESTS
-b fix_lp1982866_3.1_backport lp:~cgrabowski/maas/+git/maas into -b 3.1 lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: cd09b8b718cbbd68b07c4c9df9b3d7091e57c867

review: Approve
Revision history for this message
Christian Grabowski (cgrabowski) wrote :

self-approving backport

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/forms/__init__.py b/src/maasserver/forms/__init__.py
2index d88c443..2c0cf46 100644
3--- a/src/maasserver/forms/__init__.py
4+++ b/src/maasserver/forms/__init__.py
5@@ -2493,7 +2493,7 @@ class BootResourceForm(MAASModelForm):
6 return name
7
8 def _get_base_image_info(self):
9- base_image = self.cleaned_data["base_image"]
10+ base_image = self.cleaned_data.get("base_image")
11 if not base_image:
12 existing_resource = super().save(commit=False)
13 existing_resource.name = self.cleaned_data["name"]
14@@ -2522,9 +2522,22 @@ class BootResourceForm(MAASModelForm):
15 try:
16 base_osystem, base_version = self._get_base_image_info()
17 except ValueError:
18- raise ValidationError(
19- "custom images require a valid base image name"
20- )
21+ if not self.data.get("base_image"):
22+ return "/".join(
23+ [
24+ val
25+ for val in Config.objects.get_configs(
26+ [
27+ "commissioning_osystem",
28+ "commissioning_distro_series",
29+ ]
30+ ).values()
31+ ]
32+ )
33+ else:
34+ raise ValidationError(
35+ "a base image must follow the format: <osystem>/<series>"
36+ )
37 else:
38 if base_osystem not in ("ubuntu", "centos", "rhel"):
39 raise ValidationError(
40diff --git a/src/maasserver/forms/tests/test_bootresource.py b/src/maasserver/forms/tests/test_bootresource.py
41index e8aaeec..4544a1d 100644
42--- a/src/maasserver/forms/tests/test_bootresource.py
43+++ b/src/maasserver/forms/tests/test_bootresource.py
44@@ -10,7 +10,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
45
46 from maasserver.enum import BOOT_RESOURCE_FILE_TYPE, BOOT_RESOURCE_TYPE
47 from maasserver.forms import BootResourceForm
48-from maasserver.models import BootResource, BootResourceFile
49+from maasserver.models import BootResource, BootResourceFile, Config
50 from maasserver.models.signals import bootresourcefiles, bootsources
51 from maasserver.testing.architecture import make_usable_architecture
52 from maasserver.testing.factory import factory
53@@ -303,7 +303,7 @@ class TestBootResourceForm(MAASServerTestCase):
54 image = form.save()
55 self.assertEqual(image.base_image, data["base_image"])
56
57- def test_invalidates_nonexistent_custom_image_base_os(self):
58+ def test_uses_commissioning_os_for_nonexistent_custom_image_base_os(self):
59 name = "custom/%s" % factory.make_name("name")
60 upload_type, filetype = self.pick_filetype()
61 size = random.randint(1024, 2048)
62@@ -315,10 +315,17 @@ class TestBootResourceForm(MAASServerTestCase):
63 "title": factory.make_name("title"),
64 "architecture": make_usable_architecture(self),
65 "filetype": upload_type,
66- "base_image": factory.make_name("invalid"),
67+ "base_image": "",
68 }
69 form = BootResourceForm(data=data, files={"content": uploaded_file})
70- self.assertFalse(form.is_valid())
71+ form.save()
72+ cfg = Config.objects.get_configs(
73+ ["commissioning_osystem", "commissioning_distro_series"]
74+ )
75+ self.assertEqual(
76+ f"{cfg['commissioning_osystem']}/{cfg['commissioning_distro_series']}",
77+ form.instance.base_image,
78+ )
79
80 def test_invalidates_nonexistent_custom_image_base_os_no_prefix(self):
81 name = factory.make_name("name")
82@@ -485,7 +492,7 @@ class TestBootResourceForm(MAASServerTestCase):
83 form = BootResourceForm(data={})
84 self.assertFalse(form.is_valid(), form.errors)
85 self.assertEqual(
86- {"name", "architecture", "filetype", "content", "base_image"},
87+ {"name", "architecture", "filetype", "content"},
88 form.errors.keys(),
89 )
90
91@@ -522,3 +529,31 @@ class TestBootResourceForm(MAASServerTestCase):
92 resource_set__resource=resource
93 ).count(),
94 )
95+
96+ def test_clean_base_image_sets_commissioning_osystem_and_distro_series_where_none_is_given(
97+ self,
98+ ):
99+ architecture = make_usable_architecture(self)
100+ upload_type, filetype = self.pick_filetype()
101+ content = factory.make_string(1024).encode("utf-8")
102+ upload_name = factory.make_name("filename")
103+ uploaded_file = SimpleUploadedFile(content=content, name=upload_name)
104+ data = {
105+ "name": f"custom/{factory.make_name()}",
106+ "architecture": architecture,
107+ "filetype": upload_type,
108+ }
109+ form = BootResourceForm(data=data, files={"content": uploaded_file})
110+ form.save()
111+ cfg = Config.objects.get_configs(
112+ ["commissioning_osystem", "commissioning_distro_series"]
113+ )
114+ self.assertEqual(
115+ "/".join(
116+ [
117+ cfg["commissioning_osystem"],
118+ cfg["commissioning_distro_series"],
119+ ]
120+ ),
121+ form.instance.base_image,
122+ )
123diff --git a/src/maasserver/models/bootresource.py b/src/maasserver/models/bootresource.py
124index 0c16c67..39b6a66 100644
125--- a/src/maasserver/models/bootresource.py
126+++ b/src/maasserver/models/bootresource.py
127@@ -28,6 +28,7 @@ from maasserver.fields import JSONObjectField
128 from maasserver.models.bootresourceset import BootResourceSet
129 from maasserver.models.bootsourcecache import BootSourceCache
130 from maasserver.models.cleansave import CleanSave
131+from maasserver.models.config import Config
132 from maasserver.models.timestampedmodel import now, TimestampedModel
133 from maasserver.utils.orm import get_first, get_one
134 from provisioningserver.drivers.osystem import OperatingSystemRegistry
135@@ -582,6 +583,16 @@ class BootResource(CleanSave, TimestampedModel):
136 return self.architecture.split("/")
137
138 def split_base_image(self):
139+ # handle older custom images that may not have a base image
140+ if not self.base_image:
141+ cfg = Config.objects.get_configs(
142+ ["commissioning_osystem", "commissioning_distro_series"]
143+ )
144+ return (
145+ cfg["commissioning_osystem"],
146+ cfg["commissioning_distro_series"],
147+ )
148+
149 return self.base_image.split("/")
150
151 def get_next_version_name(self):
152diff --git a/src/maasserver/models/tests/test_bootresource.py b/src/maasserver/models/tests/test_bootresource.py
153index e4280b0..ba3dc71 100644
154--- a/src/maasserver/models/tests/test_bootresource.py
155+++ b/src/maasserver/models/tests/test_bootresource.py
156@@ -21,6 +21,7 @@ from maasserver.models.bootresource import (
157 BootResource,
158 RTYPE_REQUIRING_OS_SERIES_NAME,
159 )
160+from maasserver.models.config import Config
161 from maasserver.models.signals import bootsources
162 from maasserver.testing.factory import factory
163 from maasserver.testing.testcase import MAASServerTestCase
164@@ -1155,3 +1156,14 @@ class TestBootResource(MAASServerTestCase):
165 self.assertFalse(
166 resource.supports_subarch(factory.make_name("subarch"))
167 )
168+
169+ def test_split_base_image_handles_no_base_image(self):
170+ resource = factory.make_BootResource()
171+ resource.base_image = None
172+ cfg = Config.objects.get_configs(
173+ ["commissioning_osystem", "commissioning_distro_series"]
174+ )
175+ self.assertCountEqual(
176+ (cfg["commissioning_osystem"], cfg["commissioning_distro_series"]),
177+ resource.split_base_image(),
178+ )
179diff --git a/src/maasserver/rpc/boot.py b/src/maasserver/rpc/boot.py
180index 12f14b4..22f4102 100644
181--- a/src/maasserver/rpc/boot.py
182+++ b/src/maasserver/rpc/boot.py
183@@ -231,10 +231,7 @@ def get_boot_config_for_machine(machine, configs, purpose):
184 install_image = None
185 if osystem == "custom":
186 install_image = BootResource.objects.get(name=series)
187- if install_image.base_image is not None:
188- # if the operating system is a custom ubuntu image,
189- # use the base image to install
190- osystem, series = install_image.base_image.split("/")
191+ osystem, series = install_image.split_base_image()
192
193 if install_image is None or osystem != "ubuntu":
194 # Use only the commissioning osystem and series, for operating
195diff --git a/src/maasserver/rpc/tests/test_boot.py b/src/maasserver/rpc/tests/test_boot.py
196index 517dc8e..a0cae0d 100644
197--- a/src/maasserver/rpc/tests/test_boot.py
198+++ b/src/maasserver/rpc/tests/test_boot.py
199@@ -24,7 +24,11 @@ from maasserver.models.timestampedmodel import now
200 from maasserver.node_status import get_node_timeout, MONITORED_STATUSES
201 from maasserver.preseed import compose_enlistment_preseed_url
202 from maasserver.rpc import boot as boot_module
203-from maasserver.rpc.boot import event_log_pxe_request, get_boot_filenames
204+from maasserver.rpc.boot import (
205+ event_log_pxe_request,
206+ get_boot_config_for_machine,
207+ get_boot_filenames,
208+)
209 from maasserver.rpc.boot import get_config as orig_get_config
210 from maasserver.rpc.boot import merge_kparams_with_extra
211 from maasserver.testing.architecture import make_usable_architecture
212@@ -1520,3 +1524,104 @@ class TestGetBootFilenames(MAASServerTestCase):
213 initrd,
214 )
215 self.assertIsNone(boot_dbt)
216+
217+
218+class TestGetBootConfigForMachine(MAASServerTestCase):
219+ def test_get_boot_config_for_machine_builtin_image(self):
220+ machine = factory.make_Machine(
221+ status=NODE_STATUS.DEPLOYING,
222+ osystem="ubuntu",
223+ distro_series="focal",
224+ )
225+ configs = Config.objects.get_configs(
226+ [
227+ "commissioning_osystem",
228+ "commissioning_distro_series",
229+ "enable_third_party_drivers",
230+ "default_min_hwe_kernel",
231+ "default_osystem",
232+ "default_distro_series",
233+ "kernel_opts",
234+ "use_rack_proxy",
235+ "maas_internal_domain",
236+ "remote_syslog",
237+ "maas_syslog_port",
238+ ]
239+ )
240+
241+ osystem, series, config_arch = get_boot_config_for_machine(
242+ machine, configs, "xinstall"
243+ )
244+
245+ self.assertEqual(osystem, "ubuntu")
246+ self.assertEqual(series, "focal")
247+ self.assertEqual(config_arch, "generic")
248+
249+ def test_get_boot_config_for_machine_new_custom_image(self):
250+ arch = make_usable_architecture(self)
251+ boot_resource = factory.make_BootResource(
252+ base_image="ubuntu/jammy", architecture=arch
253+ )
254+ machine = factory.make_Machine(
255+ status=NODE_STATUS.DEPLOYING,
256+ osystem="custom",
257+ distro_series=boot_resource.name,
258+ )
259+ configs = Config.objects.get_configs(
260+ [
261+ "commissioning_osystem",
262+ "commissioning_distro_series",
263+ "enable_third_party_drivers",
264+ "default_min_hwe_kernel",
265+ "default_osystem",
266+ "default_distro_series",
267+ "kernel_opts",
268+ "use_rack_proxy",
269+ "maas_internal_domain",
270+ "remote_syslog",
271+ "maas_syslog_port",
272+ ]
273+ )
274+
275+ osystem, series, config_arch = get_boot_config_for_machine(
276+ machine, configs, "xinstall"
277+ )
278+
279+ self.assertEqual(osystem, "ubuntu")
280+ self.assertEqual(series, "jammy")
281+ self.assertEqual(config_arch, "generic")
282+
283+ def test_get_boot_config_for_machine_legacy_custom_image(self):
284+ arch = make_usable_architecture(self)
285+ boot_resource = factory.make_BootResource(
286+ base_image="", architecture=arch
287+ )
288+ machine = factory.make_Machine(
289+ status=NODE_STATUS.DEPLOYING,
290+ osystem="custom",
291+ distro_series=boot_resource.name,
292+ )
293+ configs = Config.objects.get_configs(
294+ [
295+ "commissioning_osystem",
296+ "commissioning_distro_series",
297+ "enable_third_party_drivers",
298+ "default_min_hwe_kernel",
299+ "default_osystem",
300+ "default_distro_series",
301+ "kernel_opts",
302+ "use_rack_proxy",
303+ "maas_internal_domain",
304+ "remote_syslog",
305+ "maas_syslog_port",
306+ ]
307+ )
308+
309+ osystem, series, config_arch = get_boot_config_for_machine(
310+ machine, configs, "xinstall"
311+ )
312+
313+ # legacy custom images should use the default commissioning image as a base image
314+ self.assertEqual(osystem, configs["commissioning_osystem"])
315+ self.assertEqual(series, configs["commissioning_distro_series"])
316+ self.assertEqual(config_arch, "generic")

Subscribers

People subscribed via source and target branches