Merge ~skatsaounis/maas-ci/+git/system-tests:download-boot-images into ~maas-committers/maas-ci/+git/system-tests:master

Proposed by Stamatis Katsaounis
Status: Merged
Merge reported by: Stamatis Katsaounis
Merged at revision: 40f5ee8ece9b0d1684205574b1f536fd7d811ebb
Proposed branch: ~skatsaounis/maas-ci/+git/system-tests:download-boot-images
Merge into: ~maas-committers/maas-ci/+git/system-tests:master
Diff against target: 355 lines (+137/-10)
8 files modified
pyproject.toml (+2/-1)
systemtests/api.py (+53/-1)
systemtests/conftest.py (+2/-0)
systemtests/fixtures.py (+11/-0)
systemtests/machine_config.py (+18/-0)
systemtests/state.py (+35/-4)
systemtests/tests_per_machine/test_machine.py (+3/-2)
utils/gen_config.py (+13/-2)
Reviewer Review Type Date Requested Status
Jack Lloyd-Walters Approve
MAAS Lander Approve
Review via email: mp+454705@code.launchpad.net

Commit message

Add oseries support

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

UNIT TESTS
-b download-boot-images lp:~skatsaounis/maas-ci/+git/system-tests into -b master lp:~maas-committers/maas-ci/+git/system-tests

STATUS: SUCCESS
COMMIT: 215a3c407e1f9c712d32ad211f3590d69104ca8f

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

UNIT TESTS
-b download-boot-images lp:~skatsaounis/maas-ci/+git/system-tests into -b master lp:~maas-committers/maas-ci/+git/system-tests

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/system-tests-tester/759/console
COMMIT: a2399387bfe20d5c2d198c4ed2944956b8447202

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

UNIT TESTS
-b download-boot-images lp:~skatsaounis/maas-ci/+git/system-tests into -b master lp:~maas-committers/maas-ci/+git/system-tests

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/system-tests-tester/760/console
COMMIT: c5372e8511c8780d2fbaa919e44b47d253501f07

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

UNIT TESTS
-b download-boot-images lp:~skatsaounis/maas-ci/+git/system-tests into -b master lp:~maas-committers/maas-ci/+git/system-tests

STATUS: SUCCESS
COMMIT: deb013d04f91a14a5066fd5c27fb36027c1d0931

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

UNIT TESTS
-b download-boot-images lp:~skatsaounis/maas-ci/+git/system-tests into -b master lp:~maas-committers/maas-ci/+git/system-tests

STATUS: SUCCESS
COMMIT: 40f5ee8ece9b0d1684205574b1f536fd7d811ebb

review: Approve
Revision history for this message
Jack Lloyd-Walters (lloydwaltersj) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/pyproject.toml b/pyproject.toml
2index 4a38e7a..a3b6850 100644
3--- a/pyproject.toml
4+++ b/pyproject.toml
5@@ -5,7 +5,8 @@ markers = [
6 "skip_if_dns_unconfigured", # skips tests if DNS configuration isn't present
7 "skip_if_installed_from_snap", # Skips tests if MAAS is installed as a snap.
8 "skip_if_installed_from_deb_package", # Skips tests if MAAS is installed from package.
9- "skip_if_packer_unconfigured" # Skips tests if Packer-MAAS is not configured.
10+ "skip_if_packer_unconfigured", # Skips tests if Packer-MAAS is not configured.
11+ "skip_if_os_not_supported" # Skips tests if the OS chosen is not compatible with the machine architecture
12 ]
13 log_level = "INFO"
14 log_file = "systemtests.log"
15diff --git a/systemtests/api.py b/systemtests/api.py
16index 4abf1c2..83163d1 100644
17--- a/systemtests/api.py
18+++ b/systemtests/api.py
19@@ -6,7 +6,7 @@ from subprocess import CalledProcessError
20 from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, TypedDict, Union
21
22 from .tls import MAAS_CONTAINER_CERTS_PATH
23-from .utils import wait_for_machine
24+from .utils import retries, wait_for_machine
25
26 if TYPE_CHECKING:
27 from logging import Logger
28@@ -32,6 +32,16 @@ class BootImages(TypedDict):
29 status: str
30
31
32+class BootResource(TypedDict):
33+ id: int
34+ type: str
35+ name: str
36+ architecture: str
37+ resource_uri: str
38+ last_deployed: str | None
39+ subarches: str
40+
41+
42 class Version(TypedDict):
43 capabilities: list[str]
44 version: str
45@@ -276,6 +286,10 @@ class AuthenticatedAPIClient:
46 result: str = self.execute(["boot-resources", "import"], json_output=False)
47 return result
48
49+ def list_boot_resources(self) -> list[BootResource]:
50+ result: list[BootResource] = self.execute(["boot-resources", "read"])
51+ return result
52+
53 def stop_importing_boot_resources(self) -> str:
54 result: str = self.execute(["boot-resources", "stop-import"], json_output=False)
55 return result
56@@ -284,6 +298,20 @@ class AuthenticatedAPIClient:
57 result: bool = self.execute(["boot-resources", "is-importing"])
58 return result
59
60+ def wait_for_images(self) -> None:
61+ for retry_info in retries(timeout=20 * 60, delay=10):
62+ if not self.is_importing_boot_resources():
63+ break
64+ else:
65+ raise AssertionError(
66+ f"Image couldn't be imported after {retry_info.attempt} attempts"
67+ f" over {retry_info.elapsed} seconds."
68+ )
69+
70+ def import_and_wait_for_images(self) -> None:
71+ self.import_boot_resources()
72+ self.wait_for_images()
73+
74 def list_machines(self, **kwargs: str) -> list[Machine]:
75 """
76 machines read -h to know parameters available for kwargs
77@@ -336,6 +364,8 @@ class AuthenticatedAPIClient:
78 assert result["status_name"] == "Deploying"
79 if expected_osystem := kwargs.get("osystem"):
80 assert result["osystem"] == expected_osystem
81+ if expected_distro_series := kwargs.get("distro_series"):
82+ assert result["distro_series"] == expected_distro_series
83 return result
84
85 def create_ssh_key(self, public_key: str) -> SSHKey:
86@@ -390,6 +420,28 @@ class AuthenticatedAPIClient:
87 ]
88 )
89
90+ def create_boot_source_selection(
91+ self,
92+ architectures: Iterable[str],
93+ osystem: str = "ubuntu",
94+ oseries: str = "lunar",
95+ ) -> None:
96+ arches = [f"arches={arch}" for arch in architectures]
97+ boot_sources = self.execute(["boot-sources", "read"])
98+ for boot_source in boot_sources:
99+ self.execute(
100+ [
101+ "boot-source-selections",
102+ "create",
103+ str(boot_source["id"]),
104+ f"os={osystem}",
105+ f"release={oseries}",
106+ *arches,
107+ "subarches=*",
108+ "labels=*",
109+ ]
110+ )
111+
112 def rescue_machine(self, machine: Machine) -> None:
113 result = self.execute(["machine", "rescue-mode", machine["system_id"]])
114 assert result["status_name"] == "Entering rescue mode"
115diff --git a/systemtests/conftest.py b/systemtests/conftest.py
116index 60dfa4a..72957ee 100644
117--- a/systemtests/conftest.py
118+++ b/systemtests/conftest.py
119@@ -50,6 +50,7 @@ from .fixtures import (
120 skip_if_dns_unconfigured,
121 skip_if_installed_from_deb_package,
122 skip_if_installed_from_snap,
123+ skip_if_os_not_supported,
124 ssh_key,
125 tag_all,
126 testlog,
127@@ -102,6 +103,7 @@ __all__ = [
128 "skip_if_dns_unconfigured",
129 "skip_if_installed_from_deb_package",
130 "skip_if_installed_from_snap",
131+ "skip_if_os_not_supported",
132 "ssh_key",
133 "tag_all",
134 "testlog",
135diff --git a/systemtests/fixtures.py b/systemtests/fixtures.py
136index e53ba45..1046980 100644
137--- a/systemtests/fixtures.py
138+++ b/systemtests/fixtures.py
139@@ -17,6 +17,7 @@ from .api import AuthenticatedAPIClient, UnauthenticatedMAASAPIClient
140 from .config import ADMIN_EMAIL, ADMIN_PASSWORD, ADMIN_USER
141 from .dnstester import DNSTester
142 from .lxd import Instance, get_lxd
143+from .machine_config import MachineConfig
144 from .o11y import is_o11y_enabled, setup_o11y
145 from .packer import PackerMain
146 from .region import MAASRegion
147@@ -634,6 +635,16 @@ def skip_if_ansible_playbooks_unconfigured(
148
149
150 @pytest.fixture(autouse=True)
151+def skip_if_os_not_supported(request: Any) -> None:
152+ if marker := request.node.get_closest_marker("skip_if_os_not_supported"):
153+ # Avoid instantiating machine configurations unless we have the marker applied
154+ machine_config: MachineConfig = request.getfixturevalue("machine_config")
155+ reason = marker.args[0]
156+ if machine_config.not_supported:
157+ pytest.skip(reason)
158+
159+
160+@pytest.fixture(autouse=True)
161 def skip_if_packer_unconfigured(request: Any, config: dict[str, Any]) -> None:
162 """Skip tests that require Packer configuration."""
163 marker = request.node.get_closest_marker("skip_if_packer_unconfigured")
164diff --git a/systemtests/machine_config.py b/systemtests/machine_config.py
165index e567d12..31c249b 100644
166--- a/systemtests/machine_config.py
167+++ b/systemtests/machine_config.py
168@@ -54,6 +54,8 @@ class MachineConfig:
169 devices_config: tuple[DeviceConfig, ...] = field(compare=False)
170 architecture: str = "amd64"
171 osystem: str = "ubuntu"
172+ oseries: Optional[str] = None
173+ cpu_model: Optional[str] = None
174 lxd_profile: Optional[str] = None
175
176 def __str__(self) -> str:
177@@ -86,6 +88,22 @@ class MachineConfig:
178 )
179
180 @property
181+ def not_supported(self) -> bool:
182+ # XXX config.yaml doesn't contain a CPU model yet
183+ # we need to update that for this to be useful
184+ return (
185+ (
186+ (self.architecture, self.osystem)
187+ == (
188+ "ppc64el",
189+ "ubuntu",
190+ )
191+ )
192+ and isinstance(self.oseries, str)
193+ and self.oseries >= "jammy"
194+ )
195+
196+ @property
197 def maas_power_cmd_power_parameters(self) -> list[str]:
198 return maas_power_cmd_power_parameters(self.power_type, self.power_parameters)
199
200diff --git a/systemtests/state.py b/systemtests/state.py
201index 7ca5be8..0af9641 100644
202--- a/systemtests/state.py
203+++ b/systemtests/state.py
204@@ -65,9 +65,15 @@ def import_images_and_wait_until_synced(
205 ) -> None:
206 architectures = set()
207 osystems = set()
208+ oseriess = set()
209 for machine, power_config in config.get("machines", {}).get("hardware", {}).items():
210 architectures.add(power_config.get("architecture", "amd64"))
211- osystems.add(power_config.get("osystem", "ubuntu"))
212+ osystem = power_config.get("osystem", "ubuntu")
213+ osystems.add(osystem)
214+ if osystem == "ubuntu":
215+ oseries = power_config.get("oseries")
216+ if oseries:
217+ oseriess.add(oseries)
218
219 started_importing_regex = "^Started importing of boot images from"
220 # Sometimes the region has already started importing images, we
221@@ -77,12 +83,15 @@ def import_images_and_wait_until_synced(
222 # TODO Arguably MAAS should do this for us!
223 if authenticated_admin.is_importing_boot_resources():
224 authenticated_admin.stop_importing_boot_resources()
225+ LOG.info("Aborted importing of unconfigured boot resources.")
226 if stream_url := config.get("images", {}).get("stream-url"):
227 LOG.info(f"Setting image source to {stream_url}")
228 boot_sources = authenticated_admin.list_boot_sources()
229 # XXX: Why do we have >1 boot source?!
230 for boot_source in boot_sources:
231 authenticated_admin.update_boot_source_url(boot_source["id"], stream_url)
232+ # wait for the image stream to switch
233+ authenticated_admin.import_and_wait_for_images()
234 with waits_for_event_after(
235 authenticated_admin,
236 event_type="Region import info",
237@@ -91,6 +100,20 @@ def import_images_and_wait_until_synced(
238 authenticated_admin.update_architectures_in_boot_source_selections(
239 architectures
240 )
241+ for oseries in oseriess:
242+ authenticated_admin.create_boot_source_selection(
243+ architectures, oseries=oseries
244+ )
245+ boot_sources = authenticated_admin.list_boot_sources()
246+ boot_resources = ", ".join(
247+ sorted(
248+ set(
249+ resource["name"]
250+ for resource in authenticated_admin.list_boot_resources()
251+ )
252+ )
253+ )
254+ LOG.info(f"Available boot resources: {boot_resources}")
255 authenticated_admin.import_boot_resources()
256 windows_path = None
257 if "windows" in osystems:
258@@ -115,9 +138,16 @@ def import_images_and_wait_until_synced(
259 LOG.error(f"Unsupported file-store scheme '{scheme}'")
260
261 region_start_point = time.time()
262- while authenticated_admin.is_importing_boot_resources():
263- LOG.debug("Sleeping for 10s for region image import")
264- time.sleep(10)
265+ authenticated_admin.import_and_wait_for_images()
266+ boot_resources = ", ".join(
267+ sorted(
268+ set(
269+ resource["name"]
270+ for resource in authenticated_admin.list_boot_resources()
271+ )
272+ )
273+ )
274+ LOG.info(f"available images: {boot_resources}")
275
276 region_time_taken = time.time() - region_start_point
277 LOG.info(
278@@ -136,6 +166,7 @@ def import_images_and_wait_until_synced(
279 )
280 windows_time_taken = time.time() - windows_start_point
281 LOG.info(f"Took {windows_time_taken:0.1f}s to upload Windows")
282+ # testing of custom (packer) images
283 for img in custom_images:
284 custom_image_start_point = time.time()
285 img_params = {
286diff --git a/systemtests/tests_per_machine/test_machine.py b/systemtests/tests_per_machine/test_machine.py
287index e244b17..0ae7bdc 100644
288--- a/systemtests/tests_per_machine/test_machine.py
289+++ b/systemtests/tests_per_machine/test_machine.py
290@@ -28,6 +28,7 @@ if TYPE_CHECKING:
291 from ..machine_config import MachineConfig
292
293
294+@pytest.mark.skip_if_os_not_supported("Machine hardware is not supported by OS")
295 @test_steps("enlist", "metadata", "commission", "deploy", "test_image", "rescue")
296 def test_full_circle(
297 maas_api_client: AuthenticatedAPIClient,
298@@ -116,7 +117,7 @@ def test_full_circle(
299 if image_to_test is None:
300 deploy_osystem, deploy_oseries = (
301 machine_config.osystem,
302- None,
303+ machine_config.oseries,
304 )
305 ssh_username = "ubuntu"
306 else:
307@@ -133,7 +134,7 @@ def test_full_circle(
308 status="Deployed",
309 abort_status="Failed deployment",
310 machine_id=machine_config.name,
311- timeout=TIMEOUT,
312+ timeout=TIMEOUT * 2,
313 )
314
315 assert machine["ip_addresses"][0] not in dynamic_range
316diff --git a/utils/gen_config.py b/utils/gen_config.py
317index 041b1af..17d84c8 100755
318--- a/utils/gen_config.py
319+++ b/utils/gen_config.py
320@@ -193,6 +193,12 @@ def main(argv: list[str]) -> int:
321 metavar="URL",
322 help="Configure MAAS to consume images from this URL",
323 )
324+ parser.add_argument(
325+ "--oseries",
326+ type=str,
327+ metavar="SERIES",
328+ help="Deploy and test this SERIES",
329+ )
330
331 args = parser.parse_args(argv)
332
333@@ -208,8 +214,8 @@ def main(argv: list[str]) -> int:
334 "or in --containers-image option"
335 )
336
337- if args.image_stream_url:
338- config.setdefault("images", {})["stream-url"] = args.image_stream_url
339+ if stream_url := args.image_stream_url:
340+ config.setdefault("images", {})["stream-url"] = stream_url.strip()
341 else:
342 if "images" not in config or "stream-url" not in config["images"]:
343 parser.error(
344@@ -339,6 +345,11 @@ def main(argv: list[str]) -> int:
345 for name, details in hardware.items()
346 if name in args.machine
347 }
348+ if oseries := args.oseries:
349+ for details in machines["hardware"].values():
350+ details["oseries"] = oseries
351+ if details.get("osystem", ""):
352+ details["osystem"] = "ubuntu"
353
354 if vms:
355 # Filter out VMs with name not listed in specified vm_machines

Subscribers

People subscribed via source and target branches