From 75254c3ef385401082446fe32662a3e9d94bd163 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 1 Jun 2026 21:01:06 +0200 Subject: Makefile: T8602: configtests always require vyos-configd There is no need in keeping the old --configd option when starting the smoketests. VyOS heavily relies on vyos-configd and derivates, so there is no need anymore to disable it for our smoketests. --- scripts/check-qemu-install | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'scripts') diff --git a/scripts/check-qemu-install b/scripts/check-qemu-install index c8675ffa..fd8cee6c 100755 --- a/scripts/check-qemu-install +++ b/scripts/check-qemu-install @@ -113,8 +113,6 @@ parser.add_argument('--match', help='Smoketests to run') parser.add_argument('--uefi', help='Boot using UEFI', action='store_true', default=False) parser.add_argument('--vnc', help='Enable VNC', action='store_true', default=False) parser.add_argument('--raid', help='Perform a RAID-1 install', action='store_true', default=False) -parser.add_argument('--configd', help='Execute testsuite with config daemon', action='store_true', - default=False) parser.add_argument('--no-interfaces', help='Execute testsuite without interface tests to save time', action='store_true', default=False) parser.add_argument('--smoketest', help='Execute script based CLI smoketests', @@ -850,15 +848,6 @@ try: c.sendline('touch /tmp/vyos.smoketests.hint') c.expect(op_mode_prompt) - ################################################# - # Start/stop config daemon - ################################################# - if args.configd: - c.sendline('sudo systemctl restart vyos-configd.service &> /dev/null') - else: - c.sendline('sudo systemctl stop vyos-configd.service &> /dev/null') - c.expect(op_mode_prompt) - ################################################# # Basic Configmode/Opmode switch ################################################# -- cgit v1.2.3 From 097a04e3bad808162cf348d3bc5bd1faf5127007 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 1 Jun 2026 21:05:22 +0200 Subject: Testsuite: T8602: increase Terminal width and length to 160/60 --- scripts/check-qemu-install | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'scripts') diff --git a/scripts/check-qemu-install b/scripts/check-qemu-install index fd8cee6c..9a0eb560 100755 --- a/scripts/check-qemu-install +++ b/scripts/check-qemu-install @@ -317,6 +317,10 @@ def loginVM(c, log): c.sendline(default_password) c.expect(op_mode_prompt) log.info('Logged in!') + c.sendline('set terminal width 160') + c.expect(op_mode_prompt) + c.sendline('set terminal length 60') + c.expect(op_mode_prompt) def clearTPM(): for f in glob(f'{tpm_folder}/*'): -- cgit v1.2.3 From 7df46ee92572273e8ab9bbc7b8ea738c1ad4e650 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 1 Jun 2026 21:07:11 +0200 Subject: Testsuite: T8602: stop "cat" abuse Abusing cats is a prevalent problem in the Linux user community. Users, ranging from know-nothing neophytes to all-knowing professionals, engage in cat abuse on a daily basis, whether in their scripts or simple terminal commands. https://rseragon.github.io/posts/cat-abuse/ --- scripts/check-qemu-install | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/check-qemu-install b/scripts/check-qemu-install index 9a0eb560..5ff77ca0 100755 --- a/scripts/check-qemu-install +++ b/scripts/check-qemu-install @@ -560,7 +560,8 @@ def basic_cli_tests(c): c.expect(op_mode_prompt) # Get serial console interface name from flavor definition - c.sendline('cat /usr/share/vyos/flavor.json | jq -r ".console_type"') + flavor_json = '/usr/share/vyos/flavor.json' + c.sendline(f'jq -r ".console_type" {flavor_json}') c.expect(op_mode_prompt) lines = [l.strip() for l in c.before.splitlines() if l.strip()] console_type = lines[-1].decode('utf-8').strip() # e.g. ttyS @@ -570,13 +571,13 @@ def basic_cli_tests(c): if console_type == 'tty': return - c.sendline('cat /usr/share/vyos/flavor.json | jq -r ".console_num"') + c.sendline(f'jq -r ".console_num" {flavor_json}') c.expect(op_mode_prompt) lines = [l.strip() for l in c.before.splitlines() if l.strip()] console_num = lines[-1].decode('utf-8').strip() # e.g. 0 # Get serial console speed from flavor definition - c.sendline('cat /usr/share/vyos/flavor.json | jq -r ".console_speed"') + c.sendline(f'jq -r ".console_speed" {flavor_json}') c.expect(op_mode_prompt) lines = [l.strip() for l in c.before.splitlines() if l.strip()] console_speed = lines[-1].decode('utf-8').strip() # e.g. 115200 -- cgit v1.2.3 From 28d7ceff4464597a4c0d6e48302506603bdde2e7 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 1 Jun 2026 19:18:25 +0000 Subject: Testsuite: T8602: check for mutually exclusive primary testcase selectors --- scripts/check-qemu-install | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'scripts') diff --git a/scripts/check-qemu-install b/scripts/check-qemu-install index 5ff77ca0..1d88ccb1 100755 --- a/scripts/check-qemu-install +++ b/scripts/check-qemu-install @@ -344,6 +344,35 @@ else: handler.setFormatter(formatter) log.addHandler(handler) +# Mutually exclusive primary testcase selectors (--no-interfaces is a +# smoketest modifier). +_primary_modes = [] +if args.cloud_init: + _primary_modes.append('--cloud-init') +if args.tpmtest: + _primary_modes.append('--tpmtest') +if args.raid: + _primary_modes.append('--raid') +if args.smoketest: + _primary_modes.append('--smoketest') +if args.configtest: + _primary_modes.append('--configtest') +if args.sbtest: + _primary_modes.append('--sbtest') +if len(_primary_modes) > 1: + log.error('Incompatible combination of testcase flags (%s): only one of ' + '--cloud-init, --tpmtest, --raid, --smoketest, ' + '--configtest, --sbtest may be set.', ', '.join(_primary_modes)) + sys.exit(1) + +if args.no_interfaces and not args.smoketest: + log.error('--no-interfaces requires --smoketest') + sys.exit(1) + +if args.sbtest and not args.uefi: + log.error('--sbtest requires --uefi') + sys.exit(1) + if args.logfile: filehandler = logging.FileHandler(args.logfile) filehandler.setLevel(logging.DEBUG) -- cgit v1.2.3 From c6dc300235b50d9a3f1d3ed5d9f887b67611e6c7 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 1 Jun 2026 19:41:46 +0000 Subject: Makefile: T8602: move validation if ISO exists into individual scripts Sometimes I do call scripts/check-qemu-install directly without the Makefile wrapper, thus it should be ensured that the ISO image exists also from within the test-case launcher, too. --- .gitignore | 1 + Makefile | 34 +++++++++++++--------------------- scripts/check-qemu-install | 5 +++-- scripts/iso-to-oci | 8 +++++++- 4 files changed, 24 insertions(+), 24 deletions(-) (limited to 'scripts') diff --git a/.gitignore b/.gitignore index 39e926ce..afecbb4d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ packer_build/* packer_cache/* key/* packages/* +rootfs/* !packages/*/ /testinstall*.raw /testinstall*.efivars diff --git a/Makefile b/Makefile index bbfd1032..e9e16fe1 100644 --- a/Makefile +++ b/Makefile @@ -12,62 +12,54 @@ all: %: ./build-vyos-image $* -.PHONY: checkiso -.ONESHELL: -checkiso: - if [[ ! -f $(ISO_PATH) ]]; then - echo "Could not find $(ISO_PATH)" - exit 1 - fi - .PHONY: test .ONESHELL: -test: checkiso +test: scripts/check-qemu-install --debug --match="$(MATCH)" --smoketest --uefi --cpu 4 --memory 8 --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: test-no-interfaces .ONESHELL: -test-no-interfaces: checkiso +test-no-interfaces: scripts/check-qemu-install --debug --smoketest --uefi --no-interfaces --cpu 4 --memory 8 --huge-page-size 2M --huge-page-count 1800 --isolate-cpus 2-3 --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: test-no-interfaces-no-vpp .ONESHELL: -test-no-interfaces-no-vpp: checkiso +test-no-interfaces-no-vpp: scripts/check-qemu-install --debug --smoketest --uefi --no-interfaces --no-vpp --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: test-interfaces .ONESHELL: -test-interfaces: checkiso +test-interfaces: scripts/check-qemu-install --debug --match="interfaces_" --smoketest --uefi --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: test-vpp .ONESHELL: -test-vpp: checkiso +test-vpp: scripts/check-qemu-install --debug --match="vpp" --smoketest --uefi --cpu 4 --memory 8 --huge-page-size 2M --huge-page-count 1800 --isolate-cpus 2-3 --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: testc .ONESHELL: -testc: checkiso +testc: scripts/check-qemu-install --debug --match="!vpp" --cpu 2 --memory 7 --configtest --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: testcvpp .ONESHELL: -testcvpp: checkiso +testcvpp: scripts/check-qemu-install --debug --match="vpp" --cpu 4 --memory 8 --huge-page-size 2M --huge-page-count 1800 --isolate-cpus 2-3 --configtest --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: testraid .ONESHELL: -testraid: checkiso +testraid: scripts/check-qemu-install --debug --raid --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: testsb .ONESHELL: -testsb: checkiso +testsb: scripts/check-qemu-install --debug --uefi --sbtest --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: testtpm .ONESHELL: -testtpm: checkiso +testtpm: scripts/check-qemu-install --debug --tpmtest --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: test-ci-qcow2 @@ -82,13 +74,13 @@ test-ci-qcow2: .PHONY: qemu-live .ONESHELL: -qemu-live: checkiso +qemu-live: scripts/check-qemu-install --qemu-cmd --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) .PHONY: oci .ONESHELL: -oci: checkiso - scripts/iso-to-oci $(ISO_PATH) +oci: + @scripts/iso-to-oci $(ISO_PATH) .PHONY: clean .ONESHELL: diff --git a/scripts/check-qemu-install b/scripts/check-qemu-install index 1d88ccb1..dbd0aa6f 100755 --- a/scripts/check-qemu-install +++ b/scripts/check-qemu-install @@ -396,8 +396,9 @@ if not args.disk: tmp_disk_random = "%04x" % random.randint(0,65535) args.disk = f'testinstall-{tmp_disk_time}-{tmp_disk_random}{DISK_IMAGE_EXTENSION}' -if not args.iso or not os.path.isfile(args.iso): - log.debug('Unable to find ISO image to install ...') +if args.iso and not os.path.isfile(args.iso): + log.error('Unable to find VyOS ISO image needed by testcases!') + sys.exit(1) OVMF_CODE = '/usr/share/OVMF/OVMF_CODE_4M.secboot.fd' OVMF_VARS_TMP = args.disk.replace(DISK_IMAGE_EXTENSION, '.efivars') diff --git a/scripts/iso-to-oci b/scripts/iso-to-oci index 3e463150..421a94ee 100755 --- a/scripts/iso-to-oci +++ b/scripts/iso-to-oci @@ -11,17 +11,23 @@ function cleanup() { if [[ $(/usr/bin/id -u) -ne 0 ]]; then echo "Not running as root" - exit + exit 1 fi if [ "$#" -ne 1 ]; then echo "Illegal number of parameters" + exit 1 fi ISO=$1 ROOTFS=rootfs UNSQUASHFS=unsquashfs +if [ ! -f "$ISO" ]; then + echo "E: Unable to find VyOS ISO image \"$ISO\" required for conversion" + exit 1 +fi + # ensure clean working directory cleanup -- cgit v1.2.3 From 2c5c8a4a89c7717b6d1953930ad983d96ece80cb Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Mon, 1 Jun 2026 22:44:41 +0200 Subject: Testsuite: T8602: add QEMU smoketest for add-system-image over HTTP (default + VRF) Extend check-qemu-install with --test-image-update: build a nested installer ISO as a second CD-ROM, serve the inner image over HTTP, and exercise add/delete system image in the default routing context and in a named VRF. Add make test-image-update and ignore/clean nested_installer_payload.iso artefacts. --- .gitignore | 1 + Makefile | 7 +- scripts/check-qemu-install | 215 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 214 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/.gitignore b/.gitignore index afecbb4d..3c372afe 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ rootfs/* /testinstall*.efivars /ci_data /ci_seed.iso +/nested_installer_payload.iso /*.qcow2 /*.tar .DS_Store diff --git a/Makefile b/Makefile index e9e16fe1..6763e580 100644 --- a/Makefile +++ b/Makefile @@ -72,6 +72,11 @@ test-ci-qcow2: rm -f cloud-init-image-$(ARCH).qcow2 ; cp $$(ls -t build/*.qcow2 | head -n 1) cloud-init-image-$(ARCH).qcow2 scripts/check-qemu-install --debug --cloud-init --disk cloud-init-image-$(ARCH).qcow2 $(filter-out $@,$(MAKECMDGOALS)) +.PHONY: test-image-update +.ONESHELL: +test-image-update: + scripts/check-qemu-install --debug --test-image-update --iso $(ISO_PATH) $(filter-out $@,$(MAKECMDGOALS)) + .PHONY: qemu-live .ONESHELL: qemu-live: @@ -105,7 +110,7 @@ clean: .PHONY: purge purge: - rm -rf build packer_build packer_cache testinstall-*.raw ci_data ci_seed.iso + rm -rf build packer_build packer_cache testinstall-*.raw ci_data ci_seed.iso nested_iso_data nested_installer_payload.iso .PHONY: ansible-install ansible-check ansible-clean .ONESHELL: diff --git a/scripts/check-qemu-install b/scripts/check-qemu-install index dbd0aa6f..643bceb2 100755 --- a/scripts/check-qemu-install +++ b/scripts/check-qemu-install @@ -54,6 +54,22 @@ import pexpect EXCEPTION = 0 DISK_IMAGE_EXTENSION = '.raw' + +# Nested installer-ISO QEMU test: mkisofs payload (see --nested-installer-iso-test) +NESTED_ISO_DATA_DIR = 'nested_iso_data' +NESTED_INSTALLER_PAYLOAD_ISO = 'nested_installer_payload.iso' +NESTED_INNER_ISO_NAME = 'vyos-installer.iso' + +# Local-only HTTP nested-ISO upgrade test: default VRF (main RIB) then named VRF purple +NESTED_HTTP_SERV_PORT = 18088 +NESTED_HTTP_IMAGE_NAME = 'test-image-update' +NESTED_HTTP_VRF_NAME = 'purple' +NESTED_HTTP_VRF_DUMMY = 'dum8000' +NESTED_HTTP_VRF_TABLE = '42042' +NESTED_HTTP_VRF_ADDR = '198.51.100.99' # RFC 5737 TEST-NET-3: bind address for http.server inside VRF +NESTED_HTTP_MOUNT_POINT = '/mnt/nested_installer_iso' + +# Cloud-Init data CI_CONFIG_ARGS = { 'hostname': 'vyos-CLOUD-INIT', 'eth0_ipv4': '192.0.2.1/25', @@ -125,6 +141,8 @@ parser.add_argument('--sbtest', help='Execute Secure Boot tests', action='store_true', default=False) parser.add_argument('--cloud-init', help='Execute cloud-init tests', action='store_true', default=False) +parser.add_argument('--test-image-update', help='Test ISO image update in default VRF and custom VRF', + action='store_true', default=False) parser.add_argument('--qemu-cmd', help='Only generate QEMU launch command', action='store_true', default=False) parser.add_argument('--cpu', help='Set QEMU CPU', type=int, default=2) @@ -188,7 +206,8 @@ class EarlyExit(Exception): def _kvm_exists(): return os.path.exists("/dev/kvm") -def get_qemu_cmd(name, enable_uefi, disk_img, raid=None, iso_img=None, tpm=False, vnc_enabled=False, secure_boot=False): +def get_qemu_cmd(name, enable_uefi, disk_img, raid=None, iso_img=None, tpm=False, + vnc_enabled=False, secure_boot=False, nested_cdrom_iso=None): uefi = "" uuid = "f48b60b2-e6ad-49ef-9d09-4245d0585e52" accel = ',accel=kvm' if _kvm_exists() else '' @@ -227,12 +246,27 @@ def get_qemu_cmd(name, enable_uefi, disk_img, raid=None, iso_img=None, tpm=False vga = '-vga virtio' cdrom = "" + nested_cdrom = "" if iso_img: + bootindex = '10' cdrom = f' -drive file={iso_img},format=raw,if=none,media=cdrom,id=drive-cd1,readonly=on' if architecture == 'arm64': - cdrom = f' {cdrom} -device scsi-cd,bus=scsi0.0,drive=drive-cd1,id=cd1,bootindex=10' + cdrom = f' {cdrom} -device scsi-cd,bus=scsi0.0,drive=drive-cd1,id=cd1,bootindex={bootindex}' + else: + cdrom = f' {cdrom} -device ahci,id=achi0 -device ide-cd,bus=achi0.0,drive=drive-cd1,id=cd1,bootindex={bootindex}' + + if nested_cdrom_iso: + drive_settings = 'drive=drive-cd2,id=cd2,bootindex=11' + nested_cdrom = f' -drive file={nested_cdrom_iso},format=raw,if=none,media=cdrom,id=drive-cd2,readonly=on' + if architecture == 'arm64': + nested_cdrom = f' {nested_cdrom} -device scsi-cd,bus=scsi0.0,{drive_settings}' else: - cdrom = f' {cdrom} -device ahci,id=achi0 -device ide-cd,bus=achi0.0,drive=drive-cd1,id=cd1,bootindex=10' + if not iso_img: + # Second IDE CD on its own AHCI controller (no installer CD in this command) + nested_cdrom = f'{nested_cdrom} -device ahci,id=nestedahci' \ + f' -device ide-cd,bus=nestedahci.0,{drive_settings}' + else: + nested_cdrom = f'{nested_cdrom} -device ide-cd,bus=achi0.1,{drive_settings}' # RFC7042 section 2.1.2 MAC addresses used for documentation macbase = '00:00:5E:00:53' @@ -261,7 +295,7 @@ def get_qemu_cmd(name, enable_uefi, disk_img, raid=None, iso_img=None, tpm=False -netdev user,id=n6 -device virtio-net-pci,netdev=n6,mac={macbase}:06,romfile="" \ -netdev user,id=n7 -device virtio-net-pci,netdev=n7,mac={macbase}:07,romfile="" \ -device virtio-scsi-pci,id=scsi0 \ - {cdrom} \ + {cdrom}{nested_cdrom} \ -drive format={disk_format},file={disk_img},if=none,media=disk,id=drive-hd1,readonly=off \ -device scsi-hd,bus=scsi0.0,drive=drive-hd1,id=hd1,bootindex=1' @@ -349,6 +383,8 @@ log.addHandler(handler) _primary_modes = [] if args.cloud_init: _primary_modes.append('--cloud-init') +if args.test_image_update: + _primary_modes.append('--test-image-update') if args.tpmtest: _primary_modes.append('--tpmtest') if args.raid: @@ -361,7 +397,7 @@ if args.sbtest: _primary_modes.append('--sbtest') if len(_primary_modes) > 1: log.error('Incompatible combination of testcase flags (%s): only one of ' - '--cloud-init, --tpmtest, --raid, --smoketest, ' + '--cloud-init, --test-image-update, --tpmtest, --raid, --smoketest, ' '--configtest, --sbtest may be set.', ', '.join(_primary_modes)) sys.exit(1) @@ -400,6 +436,10 @@ if args.iso and not os.path.isfile(args.iso): log.error('Unable to find VyOS ISO image needed by testcases!') sys.exit(1) +if args.test_image_update and not args.iso: + log.error('--test-image-update requires --iso file!') + sys.exit(1) + OVMF_CODE = '/usr/share/OVMF/OVMF_CODE_4M.secboot.fd' OVMF_VARS_TMP = args.disk.replace(DISK_IMAGE_EXTENSION, '.efivars') if args.sbtest: @@ -410,7 +450,7 @@ diskname_raid = None def gen_disk(name): if not os.path.isfile(name): log.info(f'Creating Disk image {name}') - c = subprocess.check_output(['qemu-img', 'create', name, '2G']) + c = subprocess.check_output(['qemu-img', 'create', name, '5G']) log.debug(c.decode()) else: log.info(f'Re-using already existing disk image "{name}".') @@ -425,6 +465,22 @@ if args.raid: # must be called after the raid disk as args.disk name is altered in the RAID path gen_disk(args.disk) +if args.test_image_update: + if os.path.isdir(NESTED_ISO_DATA_DIR): + shutil.rmtree(NESTED_ISO_DATA_DIR) + os.makedirs(NESTED_ISO_DATA_DIR) + shutil.copy2(args.iso, os.path.join(NESTED_ISO_DATA_DIR, NESTED_INNER_ISO_NAME)) + if os.path.isfile(NESTED_INSTALLER_PAYLOAD_ISO): + os.unlink(NESTED_INSTALLER_PAYLOAD_ISO) + log.info('Assembling nested installer payload ISO (second CD-ROM, drive-cd2)') + subprocess.check_call([ + 'mkisofs', '-joliet', '-rock', '-volid', 'VYOSNESTED', + '-output', NESTED_INSTALLER_PAYLOAD_ISO, NESTED_ISO_DATA_DIR, + ]) + nested_payload_iso_path = os.path.abspath(NESTED_INSTALLER_PAYLOAD_ISO) + log.info('Removing nested ISO staging directory %s', NESTED_ISO_DATA_DIR) + shutil.rmtree(NESTED_ISO_DATA_DIR) + # Create software emulated TPM - clear existing TPM data first - might be a # leftover from a previous run clearTPM() @@ -622,8 +678,116 @@ def basic_cli_tests(c): c.expect(f'set console_type="{console_type}"') c.expect(op_mode_prompt) +def _image_update_cli_sequence(c, log, new_image_name, server_bind_host='127.0.0.1', use_vrf=False): + """One add-system-image/delete cycle for nested ISO over HTTP (optional Linux VRF + VyOS vrf arg).""" + url = f'http://{server_bind_host}:{NESTED_HTTP_SERV_PORT}/{NESTED_INNER_ISO_NAME}' + + vrf_exec = '' + if use_vrf: + log.info(f'Configure VRF ({NESTED_HTTP_VRF_NAME}) for HTTP server - used for image update ') + vrf_exec = f'ip vrf exec {NESTED_HTTP_VRF_NAME} ' + c.sendline('configure') + c.expect(cfg_mode_prompt) + c.sendline(f'set vrf name {NESTED_HTTP_VRF_NAME} table {NESTED_HTTP_VRF_TABLE}') + c.expect(cfg_mode_prompt) + c.sendline(f'set interfaces dummy {NESTED_HTTP_VRF_DUMMY} address {server_bind_host}/32') + c.expect(cfg_mode_prompt) + c.sendline(f'set interfaces dummy {NESTED_HTTP_VRF_DUMMY} vrf {NESTED_HTTP_VRF_NAME}') + c.expect(cfg_mode_prompt) + c.sendline('commit') + c.expect(cfg_mode_prompt) + c.sendline('exit') + c.expect(op_mode_prompt) + + c.sendline( + f'sudo bash -c \'{vrf_exec}python3 -m http.server {NESTED_HTTP_SERV_PORT} --bind {server_bind_host} ' + f'--directory {NESTED_HTTP_MOUNT_POINT} /tmp/nested_http.log 2>&1 &\'' + ) + c.expect(op_mode_prompt) + + time.sleep(5) # Wait for HTTP server to start + + update_cmd_cli = f'TERM=dumb add system image {url}' + if use_vrf: + update_cmd_cli = f'{update_cmd_cli} vrf {NESTED_HTTP_VRF_NAME}' + c.sendline(update_cmd_cli) + + timeout = 600 + deadline = time.time() + timeout + while True: + if time.time() > deadline: + raise Exception(f'add system image timed out after {timeout}s') + i = c.expect([ + 'What would you like to name this image', + 'Would you like to set the new image as the default one for boot', + 'An active configuration was found. Would you like to copy it to the new image', + 'Would you like to copy SSH host keys', + 'Would you like to save the SSH known hosts (fingerprints)', + 'Signature is not available. Do you want to continue with installation', + 'There are unsaved changes to the configuration', + 'Would you like to continue', + 'Unable to', + 'Error:', + op_mode_prompt, + ], timeout=300) + if i == 0: + c.sendline(new_image_name) + elif i == 1: + c.sendline('n') + elif i == 2: + c.sendline('y') + elif i == 3: + c.sendline('y') + elif i == 4: + c.sendline('y') + elif i == 5: + c.sendline('y') + elif i == 6: + c.sendline('y') + elif i == 7: + c.sendline('y') + elif i == 8 or i == 9: + raise Exception('add system image reported an error') + elif i == 10: + log.info('add system image completed') + break + + c.sendline('show system image') + c.expect(new_image_name) + c.expect(op_mode_prompt) + + c.sendline(f'TERM=dumb delete system image {new_image_name}') + deadline = time.time() + timeout + while time.time() < deadline: + j = c.expect([ + 'Are you sure you want to delete', + 'Do you really want to delete the image', + 'Cannot ', + 'Error:', + 'Unable to ', + op_mode_prompt, + ], timeout=300) + if j == 0 or j == 1: + c.sendline('y') + elif j == 2 or j == 3 or j == 4: + raise Exception('delete system image failed') + elif j == 5: + break + else: + raise Exception('delete system image timed out') + + c.sendline('show system image') + c.expect(op_mode_prompt) + if new_image_name in c.before.decode(errors='replace'): + raise Exception(f'Image {new_image_name!r} still listed after delete') + + c.sendline(f'sudo fuser -k {NESTED_HTTP_SERV_PORT}/tcp 2>/dev/null || true') + c.expect(op_mode_prompt) + if args.qemu_cmd: - tmp = get_qemu_cmd(qemu_name, args.uefi, args.disk, raid=diskname_raid, iso_img=args.iso, vnc_enabled=args.vnc, secure_boot=args.sbtest) + tmp = get_qemu_cmd(qemu_name, args.uefi, args.disk, raid=diskname_raid, + iso_img=args.iso, vnc_enabled=args.vnc, secure_boot=args.sbtest, + nested_cdrom_iso=nested_payload_iso_path) os.system(tmp) exit(0) @@ -680,7 +844,9 @@ try: # Installing image to disk ################################################# log.info('Installing system') - cmd = get_qemu_cmd(qemu_name, args.uefi, args.disk, raid=diskname_raid, tpm=args.tpmtest, iso_img=args.iso, vnc_enabled=args.vnc, secure_boot=args.sbtest) + cmd = get_qemu_cmd(qemu_name, args.uefi, args.disk, raid=diskname_raid, + tpm=args.tpmtest, iso_img=args.iso, vnc_enabled=args.vnc, + secure_boot=args.sbtest, nested_cdrom_iso=nested_payload_iso_path) log.debug(f'Executing command: {cmd}') c = pexpect.spawn(cmd, logfile=stl, timeout=60) @@ -1272,6 +1438,29 @@ try: tmp = 'Configtest failed :/ - check debug output' log.error(tmp) raise Exception(tmp) + elif args.test_image_update: + log.info(f'Nested installer ISO: mount second CD-ROM ({NESTED_INNER_ISO_NAME})') + c.sendline(f'sudo mkdir -p {NESTED_HTTP_MOUNT_POINT}') + c.expect(op_mode_prompt) + + # After drive-cd1 eject the first SCSI CD node is often an empty tray; real + # media may appear on sr1+. Try every optical device until one mounts. + c.sendline( + f'for d in /dev/sr*; do sudo mount -o ro "$d" {NESTED_HTTP_MOUNT_POINT} 2>/dev/null ' + '&& echo NESTED_ISO_MOUNT_OK && break; done; ' + f'mountpoint -q {NESTED_HTTP_MOUNT_POINT} || echo NESTED_ISO_MOUNT_FAIL' + ) + if c.expect(['NESTED_ISO_MOUNT_OK', 'NESTED_ISO_MOUNT_FAIL']) != 0: + raise Exception('Failed to mount nested installer payload ISO (no usable /dev/sr* media)') + c.expect(op_mode_prompt) + + _image_update_cli_sequence(c, log, NESTED_HTTP_IMAGE_NAME) + _image_update_cli_sequence(c, log, f'{NESTED_HTTP_IMAGE_NAME}-{NESTED_HTTP_VRF_NAME}', NESTED_HTTP_VRF_ADDR, use_vrf=True) + + log.info('Unmount nested installer ISO') + c.sendline(f'sudo umount {NESTED_HTTP_MOUNT_POINT}') + c.expect(op_mode_prompt) + log.info('Nested installer ISO testcase complete') elif args.sbtest: c.sendline('show secure-boot') c.expect('SecureBoot enabled') @@ -1310,6 +1499,16 @@ if tpm_process: tpm_process.terminate() tpm_process.join() +if args.test_image_update: + try: + if os.path.isdir(NESTED_ISO_DATA_DIR): + shutil.rmtree(NESTED_ISO_DATA_DIR) + if nested_payload_iso_path and os.path.isfile(nested_payload_iso_path) \ + and not args.keep: + os.remove(nested_payload_iso_path) + except OSError: + log.warning('Could not remove nested installer ISO artefacts', exc_info=True) + if not args.keep: log.info(f'Removing disk file: {args.disk}') try: -- cgit v1.2.3