pixie-lab 0.3.1__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/.github/workflows/cicd.yaml +68 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/CHANGELOG.md +16 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/Makefile +10 -1
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/PKG-INFO +1 -1
- pixie_lab-0.3.2/cijoe/configs/test-usb-ventoy.toml +48 -0
- pixie_lab-0.3.2/cijoe/scripts/usb_iso_verify.py +92 -0
- pixie_lab-0.3.2/cijoe/scripts/usb_ventoy_guest_start.py +48 -0
- pixie_lab-0.3.2/cijoe/scripts/usb_ventoy_stage.py +231 -0
- pixie_lab-0.3.2/cijoe/tasks/test-usb-ventoy.yaml +119 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pyproject.toml +1 -1
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/.dockerignore +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/.github/workflows/docs.yml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/.gitignore +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/Containerfile +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/LICENSE +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/PLAN.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/README.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/netboot-pc.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/test-pxe-flash-always.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/test-pxe-flash.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/test-pxe-inventory.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/test-pxe-nbdboot.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/test-pxe-tui.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/test-pxe.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/configs/usbboot-pc.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/live_build.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/pixie_ipxe_build.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/pixie_wheel_stage.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/pxe_flash_stage.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/pxe_inventory_stage.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/pxe_prepare.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/pxe_run_chain_test.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/scripts/usb_iso_build.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/netboot-pc.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/test-pxe-flash-always.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/test-pxe-flash.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/test-pxe-inventory.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/test-pxe-nbdboot.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/test-pxe-tui.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/test-pxe.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/cijoe/tasks/usbboot-pc.yaml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/audit.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/src/boot-modes.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/src/conf.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/src/deployment.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/src/hardware-quirks.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/src/index.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/src/quickstart.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/tooling/README.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/tooling/pyproject.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/tooling/src/pixie_docs/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/docs/tooling/src/pixie_docs/cli.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/README.md +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/auxiliary/cloudinit-metadata.meta +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/auxiliary/ipxe-embed.ipxe +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/auxiliary/ipxe-local-general.h +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/auto/config +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/archives/backports.list.binary +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/archives/backports.list.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/bootloaders/grub-efi/splash.png +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/bootloaders/syslinux/splash.png +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/hooks/normal/0500-pixie-install.hook.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/hooks/normal/0500-pixie-skip-bootloader-menu.hook.binary +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/hooks/normal/0600-pixie-r8125-dkms.hook.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/hooks/normal/0700-pixie-clock-from-http.hook.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/hooks/normal/0800-pixie-ssh-live.hook.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/hooks/normal/0900-pixie-enable-services.hook.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/hooks/normal/0980-pixie-apt-validate.hook.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/issue +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/modprobe.d/zz-pixie-blacklist-nouveau.conf +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/motd +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/profile.d/pixie-version.sh +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/getty@.service.d/zzz-pixie-autologin-root.conf +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-early.service +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-late.service +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-mid.service +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-clock-from-http.service +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-images-discover.service +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-on-tty1.service +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-usb-grow.service +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/var-lib-pixie-images.mount +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/etc/udev/rules.d/70-pixie-realtek-2g5-offloads.rules +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/usr/lib/systemd/system-generators/pixie-skip-usb-only-units-on-netboot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-boot-banner +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-clock-from-http +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-images-discover +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-on-tty1 +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-trace +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-usb-grow +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/package-lists/pixie-base.list.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/package-lists/pixie-flash.list.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/preferences/backports.pref.binary +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/pixie-media/live-build/config/preferences/backports.pref.chroot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/_partition.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/_util.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/catalog/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/catalog/_fetcher.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/catalog/_live_env.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/catalog/_routes.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/catalog/_schema.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/catalog/_store.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/catalog/catalog.toml +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/deploy/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/deploy/_main.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/deploy/_templates.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/disks.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/events/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/events/_kinds.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/events/_log.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/events/_routes.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/exports/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/exports/_routes.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/exports/_store.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/exports/_supervisor.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/flash.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/images.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/machines/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/machines/_routes.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/machines/_store.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/oras.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/pivot/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/pivot/nbdboot +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/pxe/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/pxe/_renderer.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/pxe/_routes.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/tftp/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/tftp/_supervisor.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/tui/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/tui/_app.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/tui_catalog.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_auth.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_bind_preview.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_inventory.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_overlays.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_settings_store.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/.gitkeep +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/bootstrap-icons.min.css +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/bootstrap.min.css +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/fonts/bootstrap-icons.woff +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/fonts/bootstrap-icons.woff2 +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/htmx.min.js +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/pixie-favicon.png +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_static/sse.js +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_table_state.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/_partials/page_description.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/_partials/recent_events.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/_partials/table_helpers.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/catalog.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/catalog_detail.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/dashboard.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/events.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/ipxe/bootstrap.j2 +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/ipxe/exit.j2 +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/ipxe/nbdboot.j2 +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/ipxe/pixie-live-env.j2 +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/ipxe/unavailable.j2 +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/layout.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/live_env.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/login.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/machine_detail.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/machines.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/overlays.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/_templates/settings.html +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/src/pixie/web/main.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/conftest.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/__init__.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/conftest.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/test_catalog_fetch.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/test_deploy.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/test_exports.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/test_inventory.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/test_pxe.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/integration/test_tftp.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_auth.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_catalog_api.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_catalog_seed.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_deploy_unit.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_event_kinds.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_events_unit.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_exports_unit.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_fetch_progress.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_healthz.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_live_env_fetch.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_machines_unit.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_overlays_ui.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_pivot.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_pxe_plan_json.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_schema.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_settings.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_ui_pages.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_web_bind_preview.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/tests/test_web_inventory.py +0 -0
- {pixie_lab-0.3.1 → pixie_lab-0.3.2}/uv.lock +0 -0
|
@@ -10,6 +10,11 @@ name: cicd
|
|
|
10
10
|
# * ``build-media``: build the live-env media (netboot-pc + usbboot-pc
|
|
11
11
|
# variants) via the cijoe pipelines under cijoe/tasks/. Requires
|
|
12
12
|
# live-build + passwordless sudo; the runner has both.
|
|
13
|
+
# * ``verify-usbboot``: structurally verify the usbboot-pc .iso
|
|
14
|
+
# (hybrid ISO + El Torito BIOS & UEFI entries + sha256), then boot
|
|
15
|
+
# it via a real Ventoy stick in QEMU and assert the pixie live env
|
|
16
|
+
# comes up. Gates the publish jobs so a broken / non-Ventoy .iso
|
|
17
|
+
# never ships.
|
|
13
18
|
# * ``build-wheel``: sdist + wheel for PyPI. Runs on every push +
|
|
14
19
|
# PR so the packaging story is exercised regardless of tag state.
|
|
15
20
|
# * ``publish-pypi``: trusted publishing of the wheel + sdist,
|
|
@@ -556,6 +561,66 @@ jobs:
|
|
|
556
561
|
path: cijoe/_build/test-pxe
|
|
557
562
|
if-no-files-found: warn
|
|
558
563
|
|
|
564
|
+
verify-usbboot:
|
|
565
|
+
name: Verify usbboot .iso (structural + Ventoy boot in QEMU)
|
|
566
|
+
# Downloads the usbboot-pc bake, structurally verifies the hybrid
|
|
567
|
+
# ISO (ISO 9660 + isohybrid MBR + El Torito BIOS & UEFI entries +
|
|
568
|
+
# sha256), then boots it via a real Ventoy stick in QEMU and asserts
|
|
569
|
+
# the pixie live env comes up: pixie-usb-grow skips (no PIXIE_IMGS
|
|
570
|
+
# under a loop-mounted ISO), pixie-images-discover bind-mounts the
|
|
571
|
+
# operator drop, pixie-on-tty1 renders the CLI wizard. Gates the
|
|
572
|
+
# release -- a broken / non-bootable / non-Ventoy-compatible ISO
|
|
573
|
+
# must not ship.
|
|
574
|
+
needs: build-media
|
|
575
|
+
runs-on: ubuntu-latest
|
|
576
|
+
timeout-minutes: 40
|
|
577
|
+
steps:
|
|
578
|
+
- uses: actions/checkout@v6
|
|
579
|
+
|
|
580
|
+
- name: Install host tools (qemu, ovmf, exfat, xorriso)
|
|
581
|
+
run: |
|
|
582
|
+
set -eu
|
|
583
|
+
sudo apt-get update
|
|
584
|
+
sudo apt-get install -y --no-install-recommends \
|
|
585
|
+
qemu-system-x86 \
|
|
586
|
+
qemu-utils \
|
|
587
|
+
ovmf \
|
|
588
|
+
exfat-fuse \
|
|
589
|
+
exfatprogs \
|
|
590
|
+
xorriso \
|
|
591
|
+
file
|
|
592
|
+
sudo chmod a+rw /dev/kvm
|
|
593
|
+
ls -la /dev/kvm
|
|
594
|
+
|
|
595
|
+
- name: Install cijoe
|
|
596
|
+
run: pipx install --force cijoe
|
|
597
|
+
|
|
598
|
+
- name: Install uv
|
|
599
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
600
|
+
|
|
601
|
+
- name: Download usbboot-pc artifact
|
|
602
|
+
uses: actions/download-artifact@v7
|
|
603
|
+
with:
|
|
604
|
+
name: pixie-usbboot-pc-x86_64
|
|
605
|
+
path: ~/system_imaging/disk/
|
|
606
|
+
|
|
607
|
+
- name: Show downloaded artifact contents
|
|
608
|
+
run: |
|
|
609
|
+
set -eu
|
|
610
|
+
echo "--- ~/system_imaging/disk (recursive) ---"
|
|
611
|
+
find ~/system_imaging/disk -maxdepth 5 -print
|
|
612
|
+
|
|
613
|
+
- name: Verify + Ventoy-boot the usbboot .iso
|
|
614
|
+
run: make test-usb-ventoy
|
|
615
|
+
|
|
616
|
+
- name: Upload cijoe output
|
|
617
|
+
if: always()
|
|
618
|
+
uses: actions/upload-artifact@v7
|
|
619
|
+
with:
|
|
620
|
+
name: cijoe-output-test-usb-ventoy
|
|
621
|
+
path: cijoe/cijoe-output
|
|
622
|
+
if-no-files-found: warn
|
|
623
|
+
|
|
559
624
|
build-wheel:
|
|
560
625
|
name: Build wheel + sdist
|
|
561
626
|
needs: test
|
|
@@ -591,6 +656,7 @@ jobs:
|
|
|
591
656
|
- test-pxe-tui
|
|
592
657
|
- test-pxe-flash
|
|
593
658
|
- test-pxe-flash-always
|
|
659
|
+
- verify-usbboot
|
|
594
660
|
runs-on: ubuntu-latest
|
|
595
661
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
596
662
|
environment:
|
|
@@ -632,6 +698,7 @@ jobs:
|
|
|
632
698
|
- test-pxe-tui
|
|
633
699
|
- test-pxe-flash
|
|
634
700
|
- test-pxe-flash-always
|
|
701
|
+
- verify-usbboot
|
|
635
702
|
runs-on: ubuntu-latest
|
|
636
703
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
637
704
|
permissions:
|
|
@@ -676,6 +743,7 @@ jobs:
|
|
|
676
743
|
- test-pxe-tui
|
|
677
744
|
- test-pxe-flash
|
|
678
745
|
- test-pxe-flash-always
|
|
746
|
+
- verify-usbboot
|
|
679
747
|
runs-on: ubuntu-latest
|
|
680
748
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
681
749
|
permissions:
|
|
@@ -11,6 +11,22 @@ operator-facing summary.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.3.2] - 2026-07-24
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
**CI verifies the usbboot `.iso` boots via Ventoy.** A new
|
|
19
|
+
`verify-usbboot` job structurally checks the bake (valid ISO 9660 +
|
|
20
|
+
isohybrid MBR + El Torito BIOS *and* UEFI boot entries + sha256), then
|
|
21
|
+
installs Ventoy onto a QEMU disk, drops the `.iso` + a sentinel image
|
|
22
|
+
catalog onto it, and boots it -- asserting the pixie live env comes up
|
|
23
|
+
correctly on the Ventoy loop-boot path (`pixie-usb-grow` skips with no
|
|
24
|
+
`PIXIE_IMGS` partition, `pixie-images-discover` bind-mounts the
|
|
25
|
+
operator drop at `/var/lib/pixie/images`, and `pixie-on-tty1` renders
|
|
26
|
+
the CLI wizard). The job gates the publish jobs, so a broken,
|
|
27
|
+
non-bootable, or non-Ventoy-compatible ISO can never ship. Ported from
|
|
28
|
+
bty's `test-usb-ventoy`.
|
|
29
|
+
|
|
14
30
|
## [0.3.1] - 2026-07-24
|
|
15
31
|
|
|
16
32
|
### Fixed
|
|
@@ -23,7 +23,7 @@ endif
|
|
|
23
23
|
|
|
24
24
|
.PHONY: help \
|
|
25
25
|
deps test lint format format-check typecheck ci wheel \
|
|
26
|
-
media-deps build ipxe test-pxe \
|
|
26
|
+
media-deps build ipxe test-pxe test-usb-ventoy \
|
|
27
27
|
clean
|
|
28
28
|
|
|
29
29
|
help:
|
|
@@ -56,6 +56,8 @@ help:
|
|
|
56
56
|
@echo " (same deps as test-pxe-flash; asserts no /done flip)"
|
|
57
57
|
@echo " test-pxe-tui end-to-end PXE pixie-tui chain test"
|
|
58
58
|
@echo " (same deps as test-pxe-flash; asserts wizard entry)"
|
|
59
|
+
@echo " test-usb-ventoy structural + Ventoy-boot verify of the usbboot .iso"
|
|
60
|
+
@echo " (needs a prior VARIANT=usbboot-pc bake + qemu/KVM/OVMF + ventoy deps)"
|
|
59
61
|
@echo ""
|
|
60
62
|
@echo "Variant: $(VARIANT) (override with VARIANT=netboot-pc, ...)"
|
|
61
63
|
@echo " usbboot-pc - bootable USB live ISO via live-build (.iso, x86_64)"
|
|
@@ -166,6 +168,13 @@ test-pxe-flash-always:
|
|
|
166
168
|
test-pxe-tui:
|
|
167
169
|
cd cijoe && cijoe tasks/test-pxe-tui.yaml --monitor -c configs/test-pxe-tui.toml
|
|
168
170
|
|
|
171
|
+
# Structural + Ventoy-boot verification of the usbboot .iso. Needs a
|
|
172
|
+
# prior VARIANT=usbboot-pc bake staged under ~/system_imaging/disk/ (or
|
|
173
|
+
# a downloaded pixie-usbboot-pc-x86_64 CI artifact) + qemu/KVM/OVMF +
|
|
174
|
+
# ventoy tooling deps (losetup, exfat-fuse, passwordless sudo).
|
|
175
|
+
test-usb-ventoy:
|
|
176
|
+
cd cijoe && cijoe tasks/test-usb-ventoy.yaml --monitor -c configs/test-usb-ventoy.toml
|
|
177
|
+
|
|
169
178
|
# ---------- Cleanup ------------------------------------------------------
|
|
170
179
|
|
|
171
180
|
clean:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pixie-lab
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Bare-metal netboot appliance: catalog + fetch + NBD + PXE + TFTP + operator TUI in one container
|
|
5
5
|
Project-URL: Homepage, https://github.com/safl/pixie
|
|
6
6
|
Project-URL: Repository, https://github.com/safl/pixie
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# pixie USB Ventoy-boot test - cijoe config
|
|
2
|
+
#
|
|
3
|
+
# Verifies an operator's realistic Ventoy workflow:
|
|
4
|
+
# 1. Install Ventoy onto a USB stick.
|
|
5
|
+
# 2. Drop pixie-usbboot-pc-x86_64-v*.iso + image files + a catalog.toml
|
|
6
|
+
# onto the Ventoy data partition (under pixie-images/).
|
|
7
|
+
# 3. Boot the stick; Ventoy menu auto-selects the only ISO + chains
|
|
8
|
+
# into the pixie live env.
|
|
9
|
+
# 4. ``pixie-usb-grow.service`` skips correctly (no PIXIE_IMGS
|
|
10
|
+
# partition on the booted device -- the .iso is loop-mounted, not
|
|
11
|
+
# dd'd).
|
|
12
|
+
# 5. ``pixie-images-discover.service`` finds the operator's drop on the
|
|
13
|
+
# Ventoy data partition and bind-mounts ``pixie-images/`` at
|
|
14
|
+
# ``/var/lib/pixie/images``.
|
|
15
|
+
# 6. ``pixie-on-tty1.service`` renders the pixie CLI wizard on tty1.
|
|
16
|
+
|
|
17
|
+
[cijoe.task]
|
|
18
|
+
fail_fast = true
|
|
19
|
+
|
|
20
|
+
[qemu]
|
|
21
|
+
img_bin = "qemu-img"
|
|
22
|
+
|
|
23
|
+
[qemu.systems.x86_64]
|
|
24
|
+
bin = "qemu-system-x86_64"
|
|
25
|
+
|
|
26
|
+
[qemu.guests.usb-ventoy]
|
|
27
|
+
path = "{{ local.env.HOME }}/system_imaging/usb-ventoy"
|
|
28
|
+
system_label = "x86_64"
|
|
29
|
+
system_args.kwa = { cpu = "host", smp = 2, m = "1G", accel = "kvm" }
|
|
30
|
+
# IDE-hd: Ventoy's bootloader (a chainloaded GRUB) reads via BIOS
|
|
31
|
+
# INT13 -- virtio-blk is invisible to it. Present the Ventoy stick as
|
|
32
|
+
# an IDE hard disk so the BIOS sees a bootable disk. ``-no-reboot`` so
|
|
33
|
+
# a failed boot exits instead of looping.
|
|
34
|
+
system_args.raw = "-drive file={{ local.env.HOME }}/system_imaging/usb-ventoy/disk.img,format=raw,if=ide -no-reboot"
|
|
35
|
+
system_args.tcp_forward = { host = 12223, guest = 22 }
|
|
36
|
+
|
|
37
|
+
[cijoe.transport.usb_ventoy_guest]
|
|
38
|
+
username = "root"
|
|
39
|
+
password = "pixie"
|
|
40
|
+
hostname = "localhost"
|
|
41
|
+
port = 12223
|
|
42
|
+
|
|
43
|
+
[test.usb_ventoy]
|
|
44
|
+
iso_dir = "{{ local.env.HOME }}/system_imaging/disk"
|
|
45
|
+
# Pinned Ventoy version so the test is reproducible. Bump deliberately
|
|
46
|
+
# when validating a newer release; this is the Ventoy2Disk.sh version
|
|
47
|
+
# the test installs with, not what an operator must use in production.
|
|
48
|
+
ventoy_version = "1.1.05"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structurally verify the pixie usbboot .iso
|
|
3
|
+
===========================================
|
|
4
|
+
|
|
5
|
+
Fast, no-boot sanity checks on ``pixie-usbboot-pc-x86_64-v*.iso`` before
|
|
6
|
+
the Ventoy boot test spends ~10 min proving it actually boots. Catches a
|
|
7
|
+
broken / non-hybrid / single-firmware bake immediately:
|
|
8
|
+
|
|
9
|
+
1. ``file`` reports an ISO 9660 filesystem with a DOS/MBR boot sector
|
|
10
|
+
(the isohybrid MBR that makes ``dd`` to a USB stick BIOS-bootable).
|
|
11
|
+
2. The El Torito catalog carries BOTH a BIOS and a UEFI/EFI boot
|
|
12
|
+
image -- i.e. the ISO boots on legacy BIOS AND UEFI firmware.
|
|
13
|
+
3. The published ``.iso.sha256`` sidecar matches the image.
|
|
14
|
+
|
|
15
|
+
Retargetable: False (host-side; needs ``file`` + ``xorriso`` +
|
|
16
|
+
``sha256sum`` on the initiator, which the workflow installs).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import errno
|
|
22
|
+
import logging as log
|
|
23
|
+
from argparse import ArgumentParser
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
ISO_BASENAME_GLOB = "pixie-usbboot-pc-x86_64-v*.iso"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def add_args(parser: ArgumentParser):
|
|
30
|
+
del parser
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _out(res) -> str:
|
|
34
|
+
return res.output() if hasattr(res, "output") else str(res)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main(args, cijoe):
|
|
38
|
+
del args
|
|
39
|
+
cfg = cijoe.getconf("test.usb_ventoy", {})
|
|
40
|
+
iso_dir = Path(cfg.get("iso_dir") or (Path.home() / "system_imaging" / "disk"))
|
|
41
|
+
candidates = sorted(iso_dir.glob(ISO_BASENAME_GLOB))
|
|
42
|
+
if not candidates:
|
|
43
|
+
log.error(f"no {ISO_BASENAME_GLOB} found in {iso_dir} (did usbboot-pc build run?)")
|
|
44
|
+
return errno.ENOENT
|
|
45
|
+
iso = candidates[-1]
|
|
46
|
+
log.info(f"verifying {iso.name} ({iso.stat().st_size} bytes)")
|
|
47
|
+
|
|
48
|
+
# 1. Valid ISO 9660 + isohybrid MBR (BIOS-bootable when dd'd).
|
|
49
|
+
err, res = cijoe.run_local(f"file -- {iso}")
|
|
50
|
+
if err:
|
|
51
|
+
log.error("`file` failed")
|
|
52
|
+
return errno.EIO
|
|
53
|
+
file_out = _out(res)
|
|
54
|
+
if "ISO 9660" not in file_out:
|
|
55
|
+
log.error(f"not an ISO 9660 image: {file_out}")
|
|
56
|
+
return errno.EINVAL
|
|
57
|
+
if "DOS/MBR boot sector" not in file_out and "bootable" not in file_out.lower():
|
|
58
|
+
log.error(f"no isohybrid MBR boot sector (not USB-dd bootable?): {file_out}")
|
|
59
|
+
return errno.EINVAL
|
|
60
|
+
log.info("OK: ISO 9660 + DOS/MBR boot sector")
|
|
61
|
+
|
|
62
|
+
# 2. El Torito catalog has both a BIOS and a UEFI/EFI boot image.
|
|
63
|
+
err, res = cijoe.run_local(f"xorriso -indev {iso} -report_el_torito plain 2>&1")
|
|
64
|
+
if err:
|
|
65
|
+
log.error("`xorriso -report_el_torito` failed")
|
|
66
|
+
return errno.EIO
|
|
67
|
+
et = _out(res)
|
|
68
|
+
log.info(f"El Torito report:\n{et}")
|
|
69
|
+
boot_lines = [ln for ln in et.splitlines() if "boot img" in ln.lower()]
|
|
70
|
+
has_bios = any("BIOS" in ln for ln in boot_lines)
|
|
71
|
+
has_uefi = any(("UEFI" in ln) or ("EFI" in ln) for ln in boot_lines)
|
|
72
|
+
if not has_bios:
|
|
73
|
+
log.error("El Torito catalog has no BIOS boot image")
|
|
74
|
+
return errno.EINVAL
|
|
75
|
+
if not has_uefi:
|
|
76
|
+
log.error("El Torito catalog has no UEFI/EFI boot image")
|
|
77
|
+
return errno.EINVAL
|
|
78
|
+
log.info("OK: El Torito has both BIOS and UEFI boot images")
|
|
79
|
+
|
|
80
|
+
# 3. sha256 sidecar matches.
|
|
81
|
+
sha = iso.with_name(iso.name + ".sha256")
|
|
82
|
+
if not sha.is_file():
|
|
83
|
+
log.error(f"missing sha256 sidecar: {sha}")
|
|
84
|
+
return errno.ENOENT
|
|
85
|
+
err, _ = cijoe.run_local(f"sh -c 'cd {iso_dir} && sha256sum -c {sha.name}'")
|
|
86
|
+
if err:
|
|
87
|
+
log.error("sha256 mismatch")
|
|
88
|
+
return errno.EIO
|
|
89
|
+
log.info("OK: sha256 sidecar matches")
|
|
90
|
+
|
|
91
|
+
log.info(f"{iso.name}: structural verification passed")
|
|
92
|
+
return 0
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Start the Ventoy QEMU guest WITHOUT the is_up wait
|
|
3
|
+
==================================================
|
|
4
|
+
|
|
5
|
+
Thin wrapper around ``cijoe.qemu.wrapper.Guest.start()`` that returns
|
|
6
|
+
immediately after the guest is daemonised, skipping the upstream
|
|
7
|
+
``qemu.guest_start`` step's hardcoded 180-second ``is_up`` check
|
|
8
|
+
(which waits for the literal string ``login:`` on the serial console).
|
|
9
|
+
|
|
10
|
+
Why a local copy: Ventoy adds a chainload phase (menu -> grub ->
|
|
11
|
+
.iso -> initrd -> squashfs) on top of the pixie live env's normal
|
|
12
|
+
boot, which routinely pushes the time-to-login past the 180-second
|
|
13
|
+
ceiling baked into the upstream script. The YAML's
|
|
14
|
+
``core.wait_for_transport`` step already polls SSH readiness with
|
|
15
|
+
its own (configurable) timeout, so this script intentionally splits
|
|
16
|
+
"start the guest" from "wait for it to be reachable" -- the latter
|
|
17
|
+
is the only concern that needs a tunable timeout.
|
|
18
|
+
|
|
19
|
+
Retargetable: False (host-side; same constraints as the upstream
|
|
20
|
+
script).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import logging as log
|
|
26
|
+
from argparse import ArgumentParser
|
|
27
|
+
|
|
28
|
+
from cijoe.qemu.wrapper import Guest
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def add_args(parser: ArgumentParser):
|
|
32
|
+
parser.add_argument("--guest_name", type=str, help="Name of the qemu guest.")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main(args, cijoe):
|
|
36
|
+
if "guest_name" not in args:
|
|
37
|
+
log.error("missing argument: guest_name")
|
|
38
|
+
return 1
|
|
39
|
+
|
|
40
|
+
guest = Guest(cijoe, cijoe.config, args.guest_name)
|
|
41
|
+
|
|
42
|
+
err = guest.start()
|
|
43
|
+
if err:
|
|
44
|
+
log.error(f"guest.start() : err({err})")
|
|
45
|
+
return err
|
|
46
|
+
|
|
47
|
+
log.info(f"guest '{args.guest_name}' started; readiness handled by wait_for_transport")
|
|
48
|
+
return 0
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Stage a Ventoy disk for the pixie Ventoy-boot test
|
|
3
|
+
===================================================
|
|
4
|
+
|
|
5
|
+
Mirrors what an operator does in practice:
|
|
6
|
+
|
|
7
|
+
1. Download Ventoy.
|
|
8
|
+
2. Install Ventoy onto a USB stick (here: a 4 GiB sparse file
|
|
9
|
+
loop-attached so ``Ventoy2Disk.sh`` sees a block device).
|
|
10
|
+
3. Drop ``pixie-usbboot-pc-x86_64-v*.iso`` onto the Ventoy data
|
|
11
|
+
partition so the Ventoy menu offers it.
|
|
12
|
+
4. Drop a ``pixie-images/`` subdir with one sentinel image + a
|
|
13
|
+
``catalog.toml`` -- the operator's image catalog the live env
|
|
14
|
+
should discover via ``pixie-images-discover.service``.
|
|
15
|
+
5. Write a ``ventoy/ventoy.json`` with BOTH ``VTOY_MENU_TIMEOUT`` and
|
|
16
|
+
``VTOY_SECONDARY_TIMEOUT`` = 1 so the two Ventoy menus auto-boot
|
|
17
|
+
the only ISO without keyboard input (bty proved both are needed).
|
|
18
|
+
|
|
19
|
+
The catalog carries a single ``oras://`` entry (rolling nosi tag);
|
|
20
|
+
it exercises pixie's catalog parser as the realistic source operators
|
|
21
|
+
drop next to their images.
|
|
22
|
+
|
|
23
|
+
Retargetable: False (host-side staging on the cijoe initiator;
|
|
24
|
+
sudo needed for losetup + Ventoy2Disk.sh).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import errno
|
|
30
|
+
import logging as log
|
|
31
|
+
import urllib.request
|
|
32
|
+
from argparse import ArgumentParser
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
ISO_BASENAME_GLOB = "pixie-usbboot-pc-x86_64-v*.iso"
|
|
36
|
+
DISK_BYTES = 4 * 1024 * 1024 * 1024 # 4 GiB
|
|
37
|
+
|
|
38
|
+
# ``pixie-images-discover`` bind-mounts the first vfat/exfat partition
|
|
39
|
+
# whose ``pixie-images/`` subdir holds a usable image (``*.img*`` /
|
|
40
|
+
# ``*.qcow2``). A 1 MiB sparse ``.img.gz`` is enough to trip that; it is
|
|
41
|
+
# never flashed.
|
|
42
|
+
IMAGES_SUBDIR = "pixie-images"
|
|
43
|
+
SENTINEL_IMAGE_NAME = "demo-pixie-ventoy-test.img.gz"
|
|
44
|
+
SENTINEL_IMAGE_BYTES = 1 * 1024 * 1024
|
|
45
|
+
|
|
46
|
+
# Single-entry catalog an operator would realistically drop alongside
|
|
47
|
+
# their images: a remote ``oras://`` ref pixie's catalog parser reads.
|
|
48
|
+
CATALOG_TOML = """\
|
|
49
|
+
version = 1
|
|
50
|
+
|
|
51
|
+
[[images]]
|
|
52
|
+
name = "ventoy-test-fedora-44-headless"
|
|
53
|
+
src = "oras://ghcr.io/safl/nosi/fedora-44-headless:latest"
|
|
54
|
+
format = "img.gz"
|
|
55
|
+
arch = "x86_64"
|
|
56
|
+
description = "Sentinel remote entry for the Ventoy-boot test"
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def add_args(parser: ArgumentParser):
|
|
61
|
+
del parser
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def main(args, cijoe):
|
|
65
|
+
del args
|
|
66
|
+
cfg = cijoe.getconf("test.usb_ventoy", {})
|
|
67
|
+
iso_dir = Path(cfg.get("iso_dir") or (Path.home() / "system_imaging" / "disk"))
|
|
68
|
+
candidates = sorted(iso_dir.glob(ISO_BASENAME_GLOB))
|
|
69
|
+
if not candidates:
|
|
70
|
+
log.error(f"no {ISO_BASENAME_GLOB} found in {iso_dir} (did usbboot-pc build run?)")
|
|
71
|
+
return errno.ENOENT
|
|
72
|
+
src_iso = candidates[-1]
|
|
73
|
+
log.info(f"using {src_iso.name} ({src_iso.stat().st_size} bytes)")
|
|
74
|
+
|
|
75
|
+
ventoy_version = cfg.get("ventoy_version", "1.1.05")
|
|
76
|
+
ventoy_url = (
|
|
77
|
+
f"https://github.com/ventoy/Ventoy/releases/download/"
|
|
78
|
+
f"v{ventoy_version}/ventoy-{ventoy_version}-linux.tar.gz"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
guest_path_raw = cijoe.getconf("qemu.guests.usb-ventoy.path")
|
|
82
|
+
if not guest_path_raw:
|
|
83
|
+
log.error("missing qemu.guests.usb-ventoy.path in cijoe config")
|
|
84
|
+
return errno.EINVAL
|
|
85
|
+
guest_path = Path(guest_path_raw)
|
|
86
|
+
guest_path.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
|
|
88
|
+
disk = guest_path / "disk.img"
|
|
89
|
+
ventoy_tarball = guest_path / f"ventoy-{ventoy_version}-linux.tar.gz"
|
|
90
|
+
ventoy_dir = guest_path / f"ventoy-{ventoy_version}"
|
|
91
|
+
|
|
92
|
+
# 1. Download Ventoy (cached across runs).
|
|
93
|
+
if not ventoy_tarball.is_file():
|
|
94
|
+
log.info(f"downloading {ventoy_url}")
|
|
95
|
+
urllib.request.urlretrieve(ventoy_url, ventoy_tarball)
|
|
96
|
+
if not ventoy_dir.is_dir():
|
|
97
|
+
log.info(f"extracting {ventoy_tarball.name}")
|
|
98
|
+
err, _ = cijoe.run_local(f"tar -xzf {ventoy_tarball} -C {guest_path}")
|
|
99
|
+
if err:
|
|
100
|
+
log.error("tar -xzf failed")
|
|
101
|
+
return errno.EIO
|
|
102
|
+
|
|
103
|
+
# 2. Create the sparse 4 GiB disk (Ventoy will format it).
|
|
104
|
+
log.info(f"creating sparse disk: {disk} ({DISK_BYTES} bytes)")
|
|
105
|
+
with disk.open("wb") as fh:
|
|
106
|
+
fh.truncate(DISK_BYTES)
|
|
107
|
+
|
|
108
|
+
# 3. losetup + Ventoy2Disk.sh -I (force install).
|
|
109
|
+
log.info("losetup -fP --show <disk>")
|
|
110
|
+
err, out = cijoe.run_local(f"sudo losetup -fP --show {disk}")
|
|
111
|
+
if err:
|
|
112
|
+
log.error("losetup failed")
|
|
113
|
+
return errno.EIO
|
|
114
|
+
loop_dev = out.output().strip().splitlines()[-1].strip() if hasattr(out, "output") else ""
|
|
115
|
+
if not loop_dev.startswith("/dev/loop"):
|
|
116
|
+
log.error(f"unexpected losetup output: {out!r}")
|
|
117
|
+
return errno.EIO
|
|
118
|
+
log.info(f"loop device: {loop_dev}")
|
|
119
|
+
|
|
120
|
+
mount_dir = guest_path / "ventoy-mount"
|
|
121
|
+
mount_dir.mkdir(exist_ok=True)
|
|
122
|
+
sentinel_tmp = guest_path / "demo.tmp"
|
|
123
|
+
catalog_tmp = guest_path / "catalog.toml.tmp"
|
|
124
|
+
ventoy_json_tmp = guest_path / "ventoy.json.tmp"
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
# ``yes | Ventoy2Disk.sh -I <dev>``: force install over any
|
|
128
|
+
# existing partition table; ``yes`` answers the confirmation
|
|
129
|
+
# prompts (the disk is about to be erased).
|
|
130
|
+
log.info(f"Ventoy2Disk.sh -I {loop_dev}")
|
|
131
|
+
err, _ = cijoe.run_local(f"sudo sh -c 'yes | {ventoy_dir}/Ventoy2Disk.sh -I {loop_dev}'")
|
|
132
|
+
if err:
|
|
133
|
+
log.error("Ventoy2Disk.sh failed")
|
|
134
|
+
return errno.EIO
|
|
135
|
+
|
|
136
|
+
# Ventoy creates two partitions: p1 is the exFAT data partition
|
|
137
|
+
# (ISOs + operator files), p2 is the small VTOYEFI partition.
|
|
138
|
+
cijoe.run_local("sudo udevadm settle --timeout=10")
|
|
139
|
+
cijoe.run_local(f"sudo partprobe {loop_dev} || true")
|
|
140
|
+
cijoe.run_local("sudo udevadm settle --timeout=10")
|
|
141
|
+
cijoe.run_local(f"sudo lsblk -bno NAME,SIZE,TYPE,LABEL,FSTYPE {loop_dev}")
|
|
142
|
+
|
|
143
|
+
ventoy_data = f"{loop_dev}p1"
|
|
144
|
+
# Kernel exfat first (fast), fall back to mount.exfat-fuse.
|
|
145
|
+
# Ubuntu's ``mount -t exfat`` does NOT auto-fall-back to FUSE
|
|
146
|
+
# when the kernel module is missing; GHA's ubuntu-latest ships
|
|
147
|
+
# a stripped kernel without exfat, so the workflow installs
|
|
148
|
+
# ``exfat-fuse`` for the ``/sbin/mount.exfat-fuse`` userspace
|
|
149
|
+
# path.
|
|
150
|
+
log.info(f"mounting {ventoy_data}")
|
|
151
|
+
err, _ = cijoe.run_local(f"sudo mount -t exfat {ventoy_data} {mount_dir}")
|
|
152
|
+
if err:
|
|
153
|
+
log.info("kernel exfat unavailable; falling back to mount.exfat-fuse")
|
|
154
|
+
err, _ = cijoe.run_local(f"sudo mount.exfat-fuse {ventoy_data} {mount_dir}")
|
|
155
|
+
if err:
|
|
156
|
+
log.error(
|
|
157
|
+
f"both kernel and FUSE exfat mounts failed on {ventoy_data} "
|
|
158
|
+
f"(install ``exfat-fuse`` or ``linux-modules-extra-$(uname -r)``)"
|
|
159
|
+
)
|
|
160
|
+
return errno.EIO
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
# 4. Drop the .iso at the root of the Ventoy data partition.
|
|
164
|
+
log.info(f"copying {src_iso.name} -> Ventoy data partition")
|
|
165
|
+
err, _ = cijoe.run_local(f"sudo cp {src_iso} {mount_dir}/")
|
|
166
|
+
if err:
|
|
167
|
+
log.error("cp .iso failed")
|
|
168
|
+
return errno.EIO
|
|
169
|
+
|
|
170
|
+
# 5. pixie-images/ subdir with sentinel image + catalog.toml.
|
|
171
|
+
cijoe.run_local(f"sudo mkdir -p {mount_dir}/{IMAGES_SUBDIR}")
|
|
172
|
+
|
|
173
|
+
log.info(f"staging sentinel image ({SENTINEL_IMAGE_BYTES} bytes)")
|
|
174
|
+
with sentinel_tmp.open("wb") as fh:
|
|
175
|
+
fh.truncate(SENTINEL_IMAGE_BYTES)
|
|
176
|
+
err, _ = cijoe.run_local(
|
|
177
|
+
f"sudo cp {sentinel_tmp} {mount_dir}/{IMAGES_SUBDIR}/{SENTINEL_IMAGE_NAME}"
|
|
178
|
+
)
|
|
179
|
+
if err:
|
|
180
|
+
log.error("cp sentinel image failed")
|
|
181
|
+
return errno.EIO
|
|
182
|
+
|
|
183
|
+
log.info("staging catalog.toml")
|
|
184
|
+
catalog_tmp.write_text(CATALOG_TOML, encoding="utf-8")
|
|
185
|
+
err, _ = cijoe.run_local(
|
|
186
|
+
f"sudo cp {catalog_tmp} {mount_dir}/{IMAGES_SUBDIR}/catalog.toml"
|
|
187
|
+
)
|
|
188
|
+
if err:
|
|
189
|
+
log.error("cp catalog.toml failed")
|
|
190
|
+
return errno.EIO
|
|
191
|
+
|
|
192
|
+
# 6. ventoy/ventoy.json: fully-headless auto-boot. Ventoy
|
|
193
|
+
# shows TWO menus that BOTH need a timeout (bty verified via
|
|
194
|
+
# a stuck-guest screendump): the primary ISO list
|
|
195
|
+
# (VTOY_MENU_TIMEOUT) and the secondary boot-mode menu
|
|
196
|
+
# (VTOY_SECONDARY_TIMEOUT). The primary timeout does not
|
|
197
|
+
# cascade; without the secondary knob the guest sits at the
|
|
198
|
+
# boot-mode menu forever and no kernel ever boots.
|
|
199
|
+
cijoe.run_local(f"sudo mkdir -p {mount_dir}/ventoy")
|
|
200
|
+
ventoy_json_tmp.write_text(
|
|
201
|
+
"{\n"
|
|
202
|
+
' "control": [\n'
|
|
203
|
+
' {"VTOY_MENU_TIMEOUT": "1"},\n'
|
|
204
|
+
' {"VTOY_SECONDARY_TIMEOUT": "1"}\n'
|
|
205
|
+
" ]\n"
|
|
206
|
+
"}\n",
|
|
207
|
+
encoding="utf-8",
|
|
208
|
+
)
|
|
209
|
+
err, _ = cijoe.run_local(f"sudo cp {ventoy_json_tmp} {mount_dir}/ventoy/ventoy.json")
|
|
210
|
+
if err:
|
|
211
|
+
log.error("cp ventoy.json failed")
|
|
212
|
+
return errno.EIO
|
|
213
|
+
|
|
214
|
+
# Record what landed for the cijoe report.
|
|
215
|
+
cijoe.run_local(f"sudo ls -la {mount_dir}/")
|
|
216
|
+
cijoe.run_local(f"sudo ls -la {mount_dir}/{IMAGES_SUBDIR}/")
|
|
217
|
+
cijoe.run_local(f"sudo cat {mount_dir}/{IMAGES_SUBDIR}/catalog.toml")
|
|
218
|
+
cijoe.run_local(f"sudo cat {mount_dir}/ventoy/ventoy.json")
|
|
219
|
+
cijoe.run_local(f"sudo sync {mount_dir}")
|
|
220
|
+
finally:
|
|
221
|
+
cijoe.run_local(f"sudo umount {mount_dir}")
|
|
222
|
+
finally:
|
|
223
|
+
cijoe.run_local(f"sudo losetup -d {loop_dev}")
|
|
224
|
+
sentinel_tmp.unlink(missing_ok=True)
|
|
225
|
+
catalog_tmp.unlink(missing_ok=True)
|
|
226
|
+
ventoy_json_tmp.unlink(missing_ok=True)
|
|
227
|
+
if mount_dir.is_dir() and not any(mount_dir.iterdir()):
|
|
228
|
+
mount_dir.rmdir()
|
|
229
|
+
|
|
230
|
+
log.info(f"Ventoy disk ready at {disk}")
|
|
231
|
+
return 0
|