diff --git a/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/README.md b/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/README.md index f89ee6f9..0329a19a 100644 --- a/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/README.md +++ b/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/README.md @@ -2,10 +2,12 @@ Zapper-driven provisioning method that makes use of KVM assertions and actions. -## Ubuntu Desktop / Server + +## Autoinstall based Support for vanilla Ubuntu is provided by [autoinstall](https://canonical-subiquity.readthedocs-hosted.com/en/latest/intro-to-autoinstall.html). Supported Ubuntu versions are: +- Core24 (experimental) - Desktop >= 23.04 - Server >= 20.04 @@ -16,7 +18,7 @@ Unless specified via _autoinstall_ storage filter, the tool will select the larg - __url__: URL to the image to install - __username__: username to configure - __password__: password to configure -- **storage_layout**: can be either `lvm`, `direct`, `zfs` or `hybrid` (Desktop 23.10+) +- **storage_layout**: can be either `lvm`, `direct`, `zfs` or `hybrid` (Core, Desktop 23.10+) - **robot_tasks**: list of Zapper Robot tasks to run after a hard reset in order to follow the `autoinstall` installation - **cmdline_append** (optional): kernel parameters to append at the end of GRUB entry cmdline - **base_user_data** (optional): a custom base user-data file, it should be validated against [this schema](https://canonical-subiquity.readthedocs-hosted.com/en/latest/reference/autoinstall-schema.html) @@ -46,3 +48,13 @@ The tool will select the storage device with the following priority: ### Noble Ubuntu OEM 24.04 uses `autoinstall`. The procedure and the arguments are the same as _vanilla_ Ubuntu. + +## Live ISO + +Support for live ISOs is simply performed booting from an external storage device and returning right after KVM interactions. + +### Job parameters + +- __live_image__: Set to "true" to ensure that the Zapper considers the provision process complete at the end of KVM interactions defined by the specified `robot_tasks`, without needing to unplug the external media. +- __wait_until_ssh__: If set to "false", the Zapper will skip the SSH connection attempt, which is normally performed at the end of provisioning as a form of boot assertion. This is primarily useful in cases where the live ISO does not include an SSH server. + diff --git a/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/__init__.py b/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/__init__.py index b60777e6..9f74b814 100644 --- a/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/__init__.py +++ b/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/__init__.py @@ -17,7 +17,7 @@ import logging import os import subprocess -from typing import Any, Dict, Tuple +from typing import Any, Dict, Optional, Tuple from testflinger_device_connectors.devices import ProvisioningError from testflinger_device_connectors.devices.zapper import ZapperConnector @@ -36,14 +36,14 @@ class DeviceConnector(ZapperConnector): PROVISION_METHOD = "ProvisioningKVM" - def _get_autoinstall_conf(self) -> Dict[str, Any]: + def _get_autoinstall_conf(self) -> Optional[Dict[str, Any]]: """Prepare autoinstall-related configuration.""" provision = self.job_data["provision_data"] - autoinstall_conf = { - "storage_layout": provision.get("storage_layout", "lvm"), - } + if "storage_layout" not in provision: + return None + autoinstall_conf = {"storage_layout": provision["storage_layout"]} if "base_user_data" in provision: autoinstall_conf["base_user_data"] = provision["base_user_data"] @@ -81,19 +81,29 @@ def _validate_configuration( "url": url, "username": username, "password": password, + "robot_retries": retries, "autoinstall_conf": self._get_autoinstall_conf(), "reboot_script": self.config["reboot_script"], "device_ip": self.config["device_ip"], "robot_tasks": self.job_data["provision_data"]["robot_tasks"], - "robot_retries": retries, - "cmdline_append": self.job_data["provision_data"].get( - "cmdline_append", "" - ), - "skip_download": self.job_data["provision_data"].get( - "skip_download", False - ), } + # Let's handle defaults on the Zapper side adding only the explicitly + # specified keys to the `provision_data` dict. + optionals = [ + "cmdline_append", + "skip_download", + "wait_until_ssh", + "live_image", + ] + provisioning_data.update( + { + opt: self.job_data["provision_data"][opt] + for opt in optionals + if opt in self.job_data["provision_data"] + } + ) + return ((), provisioning_data) def _post_run_actions(self, args): diff --git a/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/tests/test_zapper_kvm.py b/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/tests/test_zapper_kvm.py index fbf67673..eae54a22 100644 --- a/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/tests/test_zapper_kvm.py +++ b/device-connectors/src/testflinger_device_connectors/devices/zapper_kvm/tests/test_zapper_kvm.py @@ -59,8 +59,6 @@ def test_validate_configuration(self): "device_ip": "1.1.1.1", "robot_tasks": ["job.robot", "another.robot"], "robot_retries": 1, - "cmdline_append": "", - "skip_download": False, } self.assertEqual(args, ()) self.assertDictEqual(kwargs, expected) @@ -90,6 +88,8 @@ def test_validate_configuration_w_opt(self): "robot_retries": 3, "cmdline_append": "more arguments", "skip_download": True, + "wait_until_ssh": True, + "live_image": False, }, "test_data": { "test_username": "username", @@ -111,6 +111,8 @@ def test_validate_configuration_w_opt(self): "robot_retries": 3, "cmdline_append": "more arguments", "skip_download": True, + "wait_until_ssh": True, + "live_image": False, } self.assertEqual(args, ()) self.assertDictEqual(kwargs, expected) @@ -159,12 +161,37 @@ def test_validate_configuration_alloem(self): "device_ip": "1.1.1.1", "robot_tasks": ["job.robot", "another.robot"], "robot_retries": 2, - "cmdline_append": "", - "skip_download": False, } self.assertEqual(args, ()) self.assertDictEqual(kwargs, expected) + def test_get_autoinstall_none(self): + """ + Test whether the get_autoinstall_conf function returns + None in case the storage_layout is not specified. + """ + + connector = DeviceConnector() + connector.job_data = { + "job_queue": "queue", + "provision_data": { + "url": "http://example.com/image.iso", + "robot_tasks": [ + "job.robot", + "another.robot", + ], + }, + "test_data": { + "test_username": "username", + "test_password": "password", + }, + } + + with patch("builtins.open", mock_open(read_data="mykey")): + conf = connector._get_autoinstall_conf() + + self.assertIsNone(conf) + def test_get_autoinstall_conf(self): """ Test whether the get_autoinstall_conf function returns diff --git a/docs/.wordlist.txt b/docs/.wordlist.txt index bf6f5044..33906e99 100644 --- a/docs/.wordlist.txt +++ b/docs/.wordlist.txt @@ -26,6 +26,7 @@ Git GitHub Grafana IAM +ISOs init installable Instantiation diff --git a/docs/reference/device-connector-types.rst b/docs/reference/device-connector-types.rst index 77281c52..b8828ed0 100644 --- a/docs/reference/device-connector-types.rst +++ b/docs/reference/device-connector-types.rst @@ -284,7 +284,7 @@ The ``zapper_kvm`` device connector, depending on the target image, supports the path from the ``robot/snippets`` path in the Zapper repository. * - ``storage_layout`` - When provisioning an image supporting *autoinstall*, the storage_layout can - be either ``lvm`` (default), ``direct``, ``zfs`` or ``hybrid`` (Desktop 23.10+) + be either ``lvm`` (default), ``direct``, ``zfs`` or ``hybrid`` (Core, Desktop 23.10+) * - ``cmdline_append`` - When provisioning an image supporting *autoinstall*, the cmdline_append can be used to append Kernel parameters to the standard GRUB entry. @@ -314,3 +314,20 @@ The ``zapper_kvm`` device connector, depending on the target image, supports the * - ``oem`` - Optional value to select the ``oemscript`` to run when specifying a ``url``, possible values are ``dell``, ``hp`` and ``lenovo``. + +.. list-table:: Supported ``provision_data`` keys for ``zapper_kvm`` with target any generic live ISOs + :header-rows: 1 + + * - Key + - Description + * - ``url`` + - URL to a disk image that is downloaded and flashed to a USB storage device, + which the DUT will then boot from. + * - ``robot_tasks`` + - List of Robot snippets to run in sequence after the USB storage device + is plugged into the DUT and the system restarted. The snippet ID is the relative + path from the ``robot/snippets`` path in the Zapper repository. + * - ``live_image`` + - Set to "true" to ensure that the Zapper considers the provision process complete at the end of KVM interactions defined by the specified `robot_tasks`, without needing to unplug the external media. + * - ``wait_until_ssh`` + - If set to "false", the Zapper will skip the SSH connection attempt, which is normally performed at the end of provisioning as a form of boot assertion. This is primarily useful in cases where the live ISO does not include an SSH server.