diff --git a/src/cijoe/cli/cli.py b/src/cijoe/cli/cli.py index 0b9782d4..1ec32e8d 100644 --- a/src/cijoe/cli/cli.py +++ b/src/cijoe/cli/cli.py @@ -74,9 +74,28 @@ def search_for_file(path: Path) -> Optional[Path]: def create_combined_toml(configs: list[Path], path: Path): + def merge_dicts(a: dict, b: dict) -> dict: + """ + Recursively merge two dictionaries. + Values in `b` overwrite values in `a` unless both values are dicts. + """ + result = dict(a) + + for key, value in b.items(): + if ( + key in result + and isinstance(result[key], dict) + and isinstance(value, dict) + ): + result[key] = merge_dicts(result[key], value) + else: + result[key] = value + + return result + combined_config: dict[str, Any] = {} for config in configs: - combined_config |= dict_from_tomlfile(config) + combined_config = merge_dicts(combined_config, dict_from_tomlfile(config)) dict_to_tomlfile(combined_config, path) diff --git a/src/cijoe/core/templates/report-workflow.html.jinja2 b/src/cijoe/core/templates/report-workflow.html.jinja2 index d14ff16c..37e07ddd 100644 --- a/src/cijoe/core/templates/report-workflow.html.jinja2 +++ b/src/cijoe/core/templates/report-workflow.html.jinja2 @@ -349,6 +349,8 @@ function selectFilter(event) { {% endif %}  .output + diff --git a/src/cijoe/qemu/scripts/guest_wait_for_ssh.py b/src/cijoe/qemu/scripts/guest_wait_for_ssh.py new file mode 100644 index 00000000..4cbc4fea --- /dev/null +++ b/src/cijoe/qemu/scripts/guest_wait_for_ssh.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Wait for qemu guest SSH to be ready +=================================== + +Note: The script will not fail if the guest does not exist. +Note: This script does not itself start the qemu guest. + +Retargetable: False +------------------- +""" +import logging as log +import time +from argparse import ArgumentParser + + +def add_args(parser: ArgumentParser): + parser.add_argument( + "--transport_name", + type=str, + default=None, + help="Name of the transport to be used. If none given, it uses the first defined transport in the config.", + ) + parser.add_argument( + "--timeout", + type=int, + default=60, + help="Amount of seconds to wait for the qemu guest to terminate.", + ) + + +def main(args, cijoe): + """Wait for qemu guest to start""" + + began = time.time() + while True: + enter = time.time() + try: + err, state = cijoe.run( + "echo 'It is alive!'", + transport_name=args.transport_name, + ) + if not err and "It is alive!" in state.output(): + break + except Exception: + # do nothing + ... + + now = time.time() + elapsed_iter = now - enter + elapsed_total = now - began + + if elapsed_iter < 5.0: + time.sleep(5.0 - elapsed_iter) + if elapsed_total > args.timeout: + log.error(f"System did not come up within timeout({args.timeout}) seconds") + return False + + return 0 diff --git a/src/cijoe/qemu/workflows/example_workflow_guest_aarch64.yaml b/src/cijoe/qemu/workflows/example_workflow_guest_aarch64.yaml index a9c6de0a..051024fc 100644 --- a/src/cijoe/qemu/workflows/example_workflow_guest_aarch64.yaml +++ b/src/cijoe/qemu/workflows/example_workflow_guest_aarch64.yaml @@ -35,6 +35,12 @@ steps: with: guest_name: generic-uefi-tcg-aarch64 +- name: guest_wait_start + uses: qemu.guest_wait_for_ssh + with: + transport_name: qemu_guest + timeout: 120 + - name: guest_check run: | hostname diff --git a/src/cijoe/qemu/workflows/example_workflow_guest_x86_64.yaml b/src/cijoe/qemu/workflows/example_workflow_guest_x86_64.yaml index c3a4dffd..ba790b0a 100644 --- a/src/cijoe/qemu/workflows/example_workflow_guest_x86_64.yaml +++ b/src/cijoe/qemu/workflows/example_workflow_guest_x86_64.yaml @@ -35,6 +35,12 @@ steps: with: guest_name: generic-bios-kvm-x86_64 +- name: guest_wait_start + uses: qemu.guest_wait_for_ssh + with: + transport_name: qemu_guest + timeout: 120 + - name: guest_check run: | hostname diff --git a/src/cijoe/qemu/wrapper.py b/src/cijoe/qemu/wrapper.py index 6c0bb4fe..87b9a36b 100644 --- a/src/cijoe/qemu/wrapper.py +++ b/src/cijoe/qemu/wrapper.py @@ -218,7 +218,8 @@ def start(self, daemonize=True, extra_args=[]): "-blockdev", f"qcow2,node-name=boot,file.driver=file,file.filename={self.boot_img}", ] - args += ["-device", "virtio-blk-pci,drive=boot"] + boot_driver = self.guest_config.get("boot_driver", "virtio-blk-pci") + args += ["-device", f"{boot_driver},drive=boot"] # Process Management stuff args += ["-pidfile", str(self.pid)] diff --git a/src/cijoe/system_imaging/auxiliary/cloudinit-linux-common-userdata.user b/src/cijoe/system_imaging/auxiliary/cloudinit-linux-common-userdata.user index 8daac5b8..278e2bd7 100644 --- a/src/cijoe/system_imaging/auxiliary/cloudinit-linux-common-userdata.user +++ b/src/cijoe/system_imaging/auxiliary/cloudinit-linux-common-userdata.user @@ -25,7 +25,8 @@ write_files: runcmd: - systemctl restart ssh - +- touch /etc/cloud/cloud-init.disabled + final_message: "The system is up, after $UPTIME seconds" power_state: mode: poweroff