pixie-lab 0.3.2__tar.gz → 0.4.1__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.
Files changed (206) hide show
  1. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/CHANGELOG.md +168 -0
  2. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/PKG-INFO +1 -1
  3. pixie_lab-0.4.1/docs/src/boot-modes.md +173 -0
  4. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/src/deployment.md +11 -3
  5. pixie_lab-0.4.1/docs/src/quickstart.md +97 -0
  6. pixie_lab-0.4.1/pixie-media/live-build/config/includes.chroot/etc/initramfs-tools/conf.d/pixie-modules-most +22 -0
  7. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pyproject.toml +1 -1
  8. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/deploy/_main.py +25 -3
  9. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/events/_kinds.py +21 -19
  10. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/exports/_store.py +118 -80
  11. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/machines/_routes.py +31 -13
  12. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/machines/_store.py +85 -34
  13. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/pxe/_renderer.py +62 -82
  14. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/pxe/_routes.py +44 -2
  15. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_bind_preview.py +13 -9
  16. pixie_lab-0.4.1/src/pixie/web/_fsutil.py +36 -0
  17. pixie_lab-0.4.1/src/pixie/web/_images.py +194 -0
  18. pixie_lab-0.4.1/src/pixie/web/_overlay_bind.py +101 -0
  19. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_overlays.py +68 -50
  20. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_settings_store.py +30 -11
  21. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/catalog.html +21 -57
  22. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/catalog_detail.html +1 -1
  23. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/dashboard.html +66 -76
  24. pixie_lab-0.4.1/src/pixie/web/_templates/image_detail.html +108 -0
  25. pixie_lab-0.4.1/src/pixie/web/_templates/images.html +139 -0
  26. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/layout.html +3 -0
  27. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/machine_detail.html +88 -69
  28. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/machines.html +16 -5
  29. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/overlays.html +36 -29
  30. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/main.py +333 -114
  31. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_deploy_unit.py +36 -0
  32. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_exports_unit.py +88 -33
  33. pixie_lab-0.4.1/tests/test_images_ui.py +223 -0
  34. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_machines_unit.py +188 -24
  35. pixie_lab-0.4.1/tests/test_overlays_ui.py +361 -0
  36. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_pxe_plan_json.py +46 -0
  37. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_settings.py +24 -1
  38. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_ui_pages.py +44 -6
  39. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_web_bind_preview.py +11 -7
  40. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/uv.lock +1 -1
  41. pixie_lab-0.3.2/docs/src/boot-modes.md +0 -119
  42. pixie_lab-0.3.2/docs/src/quickstart.md +0 -66
  43. pixie_lab-0.3.2/tests/test_overlays_ui.py +0 -255
  44. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/.dockerignore +0 -0
  45. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/.github/workflows/cicd.yaml +0 -0
  46. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/.github/workflows/docs.yml +0 -0
  47. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/.gitignore +0 -0
  48. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/Containerfile +0 -0
  49. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/LICENSE +0 -0
  50. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/Makefile +0 -0
  51. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/PLAN.md +0 -0
  52. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/README.md +0 -0
  53. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/netboot-pc.toml +0 -0
  54. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/test-pxe-flash-always.toml +0 -0
  55. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/test-pxe-flash.toml +0 -0
  56. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/test-pxe-inventory.toml +0 -0
  57. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/test-pxe-nbdboot.toml +0 -0
  58. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/test-pxe-tui.toml +0 -0
  59. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/test-pxe.toml +0 -0
  60. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/test-usb-ventoy.toml +0 -0
  61. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/configs/usbboot-pc.toml +0 -0
  62. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/live_build.py +0 -0
  63. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/pixie_ipxe_build.py +0 -0
  64. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/pixie_wheel_stage.py +0 -0
  65. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/pxe_flash_stage.py +0 -0
  66. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/pxe_inventory_stage.py +0 -0
  67. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/pxe_prepare.py +0 -0
  68. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/pxe_run_chain_test.py +0 -0
  69. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/usb_iso_build.py +0 -0
  70. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/usb_iso_verify.py +0 -0
  71. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/usb_ventoy_guest_start.py +0 -0
  72. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/scripts/usb_ventoy_stage.py +0 -0
  73. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/netboot-pc.yaml +0 -0
  74. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/test-pxe-flash-always.yaml +0 -0
  75. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/test-pxe-flash.yaml +0 -0
  76. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/test-pxe-inventory.yaml +0 -0
  77. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/test-pxe-nbdboot.yaml +0 -0
  78. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/test-pxe-tui.yaml +0 -0
  79. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/test-pxe.yaml +0 -0
  80. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/test-usb-ventoy.yaml +0 -0
  81. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/cijoe/tasks/usbboot-pc.yaml +0 -0
  82. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/audit.md +0 -0
  83. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/src/conf.py +0 -0
  84. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/src/hardware-quirks.md +0 -0
  85. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/src/index.md +0 -0
  86. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/tooling/README.md +0 -0
  87. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/tooling/pyproject.toml +0 -0
  88. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/tooling/src/pixie_docs/__init__.py +0 -0
  89. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/docs/tooling/src/pixie_docs/cli.py +0 -0
  90. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/README.md +0 -0
  91. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/auxiliary/cloudinit-metadata.meta +0 -0
  92. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/auxiliary/ipxe-embed.ipxe +0 -0
  93. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/auxiliary/ipxe-local-general.h +0 -0
  94. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/auto/config +0 -0
  95. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/archives/backports.list.binary +0 -0
  96. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/archives/backports.list.chroot +0 -0
  97. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/bootloaders/grub-efi/splash.png +0 -0
  98. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/bootloaders/syslinux/splash.png +0 -0
  99. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/hooks/normal/0500-pixie-install.hook.chroot +0 -0
  100. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/hooks/normal/0500-pixie-skip-bootloader-menu.hook.binary +0 -0
  101. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/hooks/normal/0600-pixie-r8125-dkms.hook.chroot +0 -0
  102. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/hooks/normal/0700-pixie-clock-from-http.hook.chroot +0 -0
  103. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/hooks/normal/0800-pixie-ssh-live.hook.chroot +0 -0
  104. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/hooks/normal/0900-pixie-enable-services.hook.chroot +0 -0
  105. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/hooks/normal/0980-pixie-apt-validate.hook.chroot +0 -0
  106. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/issue +0 -0
  107. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/modprobe.d/zz-pixie-blacklist-nouveau.conf +0 -0
  108. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/motd +0 -0
  109. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/profile.d/pixie-version.sh +0 -0
  110. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/getty@.service.d/zzz-pixie-autologin-root.conf +0 -0
  111. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-early.service +0 -0
  112. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-late.service +0 -0
  113. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-mid.service +0 -0
  114. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-clock-from-http.service +0 -0
  115. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-images-discover.service +0 -0
  116. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-on-tty1.service +0 -0
  117. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-usb-grow.service +0 -0
  118. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/var-lib-pixie-images.mount +0 -0
  119. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/etc/udev/rules.d/70-pixie-realtek-2g5-offloads.rules +0 -0
  120. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/usr/lib/systemd/system-generators/pixie-skip-usb-only-units-on-netboot +0 -0
  121. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-boot-banner +0 -0
  122. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-clock-from-http +0 -0
  123. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-images-discover +0 -0
  124. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-on-tty1 +0 -0
  125. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-trace +0 -0
  126. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-usb-grow +0 -0
  127. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/package-lists/pixie-base.list.chroot +0 -0
  128. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/package-lists/pixie-flash.list.chroot +0 -0
  129. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/preferences/backports.pref.binary +0 -0
  130. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/pixie-media/live-build/config/preferences/backports.pref.chroot +0 -0
  131. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/__init__.py +0 -0
  132. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/_partition.py +0 -0
  133. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/_util.py +0 -0
  134. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/catalog/__init__.py +0 -0
  135. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/catalog/_fetcher.py +0 -0
  136. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/catalog/_live_env.py +0 -0
  137. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/catalog/_routes.py +0 -0
  138. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/catalog/_schema.py +0 -0
  139. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/catalog/_store.py +0 -0
  140. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/catalog/catalog.toml +0 -0
  141. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/deploy/__init__.py +0 -0
  142. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/deploy/_templates.py +0 -0
  143. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/disks.py +0 -0
  144. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/events/__init__.py +0 -0
  145. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/events/_log.py +0 -0
  146. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/events/_routes.py +0 -0
  147. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/exports/__init__.py +0 -0
  148. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/exports/_routes.py +0 -0
  149. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/exports/_supervisor.py +0 -0
  150. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/flash.py +0 -0
  151. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/images.py +0 -0
  152. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/machines/__init__.py +0 -0
  153. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/oras.py +0 -0
  154. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/pivot/__init__.py +0 -0
  155. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/pivot/nbdboot +0 -0
  156. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/pxe/__init__.py +0 -0
  157. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/tftp/__init__.py +0 -0
  158. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/tftp/_supervisor.py +0 -0
  159. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/tui/__init__.py +0 -0
  160. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/tui/_app.py +0 -0
  161. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/tui_catalog.py +0 -0
  162. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/__init__.py +0 -0
  163. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_auth.py +0 -0
  164. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_inventory.py +0 -0
  165. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/.gitkeep +0 -0
  166. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/bootstrap-icons.min.css +0 -0
  167. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/bootstrap.min.css +0 -0
  168. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/fonts/bootstrap-icons.woff +0 -0
  169. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/fonts/bootstrap-icons.woff2 +0 -0
  170. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/htmx.min.js +0 -0
  171. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/pixie-favicon.png +0 -0
  172. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_static/sse.js +0 -0
  173. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_table_state.py +0 -0
  174. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/_partials/page_description.html +0 -0
  175. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/_partials/recent_events.html +0 -0
  176. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/_partials/table_helpers.html +0 -0
  177. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/events.html +0 -0
  178. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/ipxe/bootstrap.j2 +0 -0
  179. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/ipxe/exit.j2 +0 -0
  180. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/ipxe/nbdboot.j2 +0 -0
  181. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/ipxe/pixie-live-env.j2 +0 -0
  182. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/ipxe/unavailable.j2 +0 -0
  183. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/live_env.html +0 -0
  184. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/login.html +0 -0
  185. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/src/pixie/web/_templates/settings.html +0 -0
  186. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/__init__.py +0 -0
  187. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/conftest.py +0 -0
  188. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/__init__.py +0 -0
  189. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/conftest.py +0 -0
  190. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/test_catalog_fetch.py +0 -0
  191. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/test_deploy.py +0 -0
  192. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/test_exports.py +0 -0
  193. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/test_inventory.py +0 -0
  194. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/test_pxe.py +0 -0
  195. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/integration/test_tftp.py +0 -0
  196. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_auth.py +0 -0
  197. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_catalog_api.py +0 -0
  198. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_catalog_seed.py +0 -0
  199. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_event_kinds.py +0 -0
  200. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_events_unit.py +0 -0
  201. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_fetch_progress.py +0 -0
  202. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_healthz.py +0 -0
  203. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_live_env_fetch.py +0 -0
  204. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_pivot.py +0 -0
  205. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_schema.py +0 -0
  206. {pixie_lab-0.3.2 → pixie_lab-0.4.1}/tests/test_web_inventory.py +0 -0
@@ -11,6 +11,174 @@ operator-facing summary.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.4.1] - 2026-07-25
15
+
16
+ ### Changed
17
+
18
+ **New machines default to `pixie-inventory`, one-shot.** A
19
+ freshly-discovered MAC now auto-registers with `pixie-inventory`
20
+ instead of `ipxe-exit`: non-destructive (boots the live env, collects
21
+ lshw + disks, posts them, exits to firmware) and immediately useful --
22
+ every new machine's hardware shows up and the flash modes (which
23
+ require inventory) become available without a manual pass. It is
24
+ one-shot: the first inventory POST flips the binding to `ipxe-exit`, so
25
+ a PXE-first box inventories itself exactly once and never boot-loops.
26
+ With no live env staged the plan degrades to exit, so a bare deploy
27
+ behaves like before until the operator fetches the live env. Override
28
+ per deploy with `PIXIE_DEFAULT_BOOT_MODE` (e.g. `ipxe-exit` for the old
29
+ behaviour).
30
+
31
+ ### Fixed
32
+
33
+ **Live env boots on arbitrary NICs (netboot-pc initramfs is now
34
+ `MODULES=most`).** The live env fetches its squashfs over HTTP from the
35
+ initramfs before pivot-root, so the target's NIC must be driven from
36
+ inside the initrd. It was built the mkinitramfs default `MODULES=dep` --
37
+ only the build host's drivers (plus the DKMS'd r8125) -- so a target
38
+ with a different NIC came up to `initrd` and then stalled forever at the
39
+ squashfs fetch, silently breaking `pixie-inventory` / `-tui` / `-flash`
40
+ on that box (observed on an ASRock Rack board). The netboot-pc bake now
41
+ sets `MODULES=most`, baking the broad Debian-installer driver set so an
42
+ arbitrary lab machine's NIC (and storage) is alive before the fetch.
43
+ Costs some initramfs size; worth it for an appliance whose job is
44
+ booting unknown hardware.
45
+
46
+ **No more `${PIXIE_LIVE_ENV_EXTRA_CMDLINE:-}` literal on the kernel
47
+ cmdline.** On a deploy whose compose runner doesn't expand `${VAR:-}`
48
+ (podman-compose passes the unset default through verbatim), the
49
+ container env held the literal string `${PIXIE_LIVE_ENV_EXTRA_CMDLINE:-}`,
50
+ which pixie then appended to every live-env / nbdboot kernel cmdline.
51
+ Harmless (the kernel ignores the unknown token) but wrong. The
52
+ extra-cmdline resolver now treats a value that is nothing but an
53
+ unexpanded `${...}` placeholder as unset, so it can't leak -- fixed for
54
+ any deploy without regenerating compose.
55
+
56
+ ## [0.4.0] - 2026-07-24
57
+
58
+ ### Changed
59
+
60
+ **Machines list Image column reads like the new model.** The bound
61
+ image now shows the catalog name linked to its Images page (short sha
62
+ for an orphan blob whose entry was deleted), instead of a bare hash,
63
+ and is blank for boot modes that don't consume an image (ipxe-exit /
64
+ inventory / tui) rather than showing a stale sha.
65
+
66
+ **Catalog table is leaner.** The sources listing drops the inline
67
+ content sha + byte size (image facts that live on the linked Image
68
+ page); the fetched/error/in-flight readiness state stays.
69
+
70
+ **Default timestamp format drops the timezone suffix.** Operator-facing
71
+ timestamps now default to `%Y-%m-%d %H:%M:%S` (24-hour, no ` %Z`); the
72
+ display timezone is still an operator setting, just not repeated on
73
+ every row. Set `PIXIE_DATETIME_FORMAT` or the Settings override to add
74
+ it back.
75
+
76
+ **Overlays are now globally-named single-writer volumes.** A persistent
77
+ nbdboot overlay is no longer a per-`(machine, image, profile)` triple; it
78
+ is a globally-unique named writable volume (`alias`) over ONE base image,
79
+ and the base image is implied by the alias. At most one machine may hold
80
+ an alias at a time: attaching one already held by a different machine is
81
+ rejected in the app ("held by <mac>; detach first"), with qemu-nbd's
82
+ qcow2 image-lock as the backstop. On a machine's detail page the picker
83
+ now offers the aliases that are free (or already held here) plus a
84
+ create-new flow; attaching an existing alias binds the machine to that
85
+ alias's base image. The Overlays page keys on the alias, shows an
86
+ **Attached to** column (a MAC or "free"), and classifies each row as
87
+ serving / held / free / orphaned / missing; Prune still reclaims only the
88
+ orphaned + file-missing rows and leaves a free alias (a deliberate keep)
89
+ alone. On-disk overlays move from
90
+ `overlays/<mac>/<image_sha>/<profile>.qcow2` to a flat
91
+ `overlays/<alias>.qcow2`. Existing state.db rows migrate in place on
92
+ first start: each old overlay becomes `alias = <profile>-<mac_slug>`
93
+ holding its original MAC, its qcow2 path is kept as-is (no large-file
94
+ move), and each machine's binding is rewritten to the same derived alias.
95
+
96
+ **Dashboard speaks the new model.** The tiles now mirror the
97
+ storage/lifecycle vocabulary + the nav: **Machines**, **Catalog**
98
+ (sources you can fetch, with a not-yet-fetched count), **Images** (the
99
+ materialised entities, with on-disk footprint + how many are
100
+ reclaimable), **Overlays** (alias-keyed writable volumes, with disk-used
101
+ + serving), and **Events** -- instead of the old catalog-images /
102
+ NBD-exports framing. The Acknowledge button was removed from the
103
+ dashboard (it's an action; the dashboard is output-only -- ack lives on
104
+ the Events page).
105
+
106
+ **Catalog is sources-only now.** With Images owning the materialised
107
+ view, the Catalog page drops the NBD-serving column and the
108
+ Stop-export / blob-delete actions (those live on Images). It keeps
109
+ source management -- add, import, Fetch, delete-entry -- and a fetched
110
+ disk-image row now links to its **Image** for footprint, live NBD /
111
+ overlay usage, and GC.
112
+
113
+ **Legacy `/ui/exports` lands on Images.** The kept-alive redirect for
114
+ the old exports URL (and the Stop-export action) now points at the
115
+ Images view, where NBD export usage is surfaced per content sha, rather
116
+ than at Catalog.
117
+
118
+ **Overlay picker shows aliases held elsewhere, disabled.** The
119
+ machine-detail overlay dropdown used to hide any alias held by another
120
+ machine, so an operator searching for it assumed it didn't exist and
121
+ tried to create a duplicate (which the single-writer bind then
122
+ rejected). Held-elsewhere aliases are now shown as a disabled option
123
+ labelled with the holder MAC ("held by `<mac>`") -- visible, but not
124
+ selectable.
125
+
126
+ **Overlays distinguish "pending" from "file missing".** A reserved
127
+ overlay alias whose qcow2 hasn't been lazy-created yet (never booted)
128
+ previously read as *file missing* -- the same alarming state as a
129
+ booted-then-lost overlay -- and Prune would reclaim it as junk. Such an
130
+ overlay is now classified **pending** (benign, awaiting its first
131
+ nbdboot) and is left alone by Prune; *missing* is reserved for an
132
+ overlay whose qcow2 vanished after a prior boot (real data loss). The
133
+ `last_boot_at` timestamp is the discriminator.
134
+
135
+ ### Added
136
+
137
+ **First-contact machines emit a `machine.discovered` audit event.** The
138
+ first time an unknown MAC hits `GET /pxe/<mac>` and is auto-registered,
139
+ pixie now records one `machine.discovered` event (carrying the
140
+ auto-registered boot mode + the client IP); repeat contacts do not
141
+ re-emit. The kind was reserved but never fired before.
142
+
143
+ **Images: the materialised content behind Catalog sources.** A new
144
+ **Images** page (`/ui/images`) separates the *source* (a Catalog entry,
145
+ a URL you can fetch) from the *entity* it produces once fetched (an
146
+ image, identity = the disk content sha). Machines, NBD exports, and
147
+ overlays all key off that sha, so each image rolls up its on-disk
148
+ footprint (raw disk + rootfs + boot bundle + overlays) and every live
149
+ usage -- machines bound, the ephemeral nbdkit export, and per-machine
150
+ qemu-nbd overlays -- with counts that link into the per-usage admin
151
+ surfaces. An **image detail hub** (`/ui/images/<sha>`) lays out the
152
+ artifacts + every usage inline and offers a **guarded delete / blob GC**:
153
+ allowed only when the usage count (the refcount) is zero, it removes the
154
+ whole `blobs/<sha>/` dir (raw disk **and** the `rootfs.raw` the old
155
+ per-entry delete leaked) and clears the sha off every entry that
156
+ resolved to it. The list also surfaces **orphan blobs** -- sha dirs on
157
+ disk with no catalog entry -- which is where the un-GC'd disk pressure
158
+ actually hides; they delete straight through. Catalog is unchanged for
159
+ now.
160
+
161
+ ### Fixed
162
+
163
+ **Live-env artifacts are served after a post-startup fetch, no restart
164
+ needed.** The `/boot/pixie-live-env/` mount was created at app startup
165
+ only if the directory already existed, so on a fresh deploy -- where the
166
+ operator stages the netboot-pc bake later via "Fetch live env" -- the
167
+ plan pointed targets at `/boot/pixie-live-env/vmlinuz` and friends that
168
+ then 404'd until pixie was restarted, silently breaking every
169
+ `pixie-inventory` / `-tui` / `-flash` boot. The mount is now created
170
+ unconditionally (dir ensured, `check_dir=False`), so a fetched live-env
171
+ is served immediately.
172
+
173
+ **`pixie-lab purge --all` no longer tracebacks on a root-owned parent.**
174
+ When the deploy dir lives under a directory the operator can't write
175
+ (the common `/opt/pixie` case), purge could empty the dir but not
176
+ remove the dir itself, and surfaced the failure as a raw Python
177
+ traceback. It now clears the state, prints a plain one-line caveat
178
+ ("emptied `<dir>`, but could not remove the directory itself ..."), and
179
+ exits 0 -- the purge did its job; only the empty-dir removal was blocked
180
+ by the parent's permissions.
181
+
14
182
  ## [0.3.2] - 2026-07-24
15
183
 
16
184
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixie-lab
3
- Version: 0.3.2
3
+ Version: 0.4.1
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,173 @@
1
+ # Boot modes
2
+
3
+ Every machine pixie has seen is bound to exactly one of six boot
4
+ modes. On the target's next PXE request, pixie renders an iPXE plan
5
+ whose shape depends on the mode. The bind form on the machine detail
6
+ page picks the mode by clicking a card; the JSON API accepts the same
7
+ tokens.
8
+
9
+ ## The six modes
10
+
11
+ ### `ipxe-exit`
12
+
13
+ Pixie exits the iPXE chain. The target's BIOS boot order picks the
14
+ next bootable device. A pure no-op: pixie won't reflash a machine or
15
+ netboot it. This is where `pixie-inventory` self-terminates to (see
16
+ below), and where an operator parks a machine pixie should leave alone.
17
+
18
+ ### `pixie-inventory` (default)
19
+
20
+ The target boots pixie's own live env. The `pixie` CLI collects
21
+ disk + NIC + lshw output and POSTs it to `/pxe/<mac>/inventory`,
22
+ then the target reboots to firmware. Prerequisite for
23
+ `pixie-flash-*`: without inventory, pixie doesn't know which disk
24
+ serial to hand the flash pipeline.
25
+
26
+ This is the **default a freshly-discovered MAC auto-registers with**:
27
+ it's non-destructive (touches no disk) and immediately useful (every
28
+ new machine's hardware shows up on `/ui/machines`, and the flash modes
29
+ become available without a manual pass). It is **one-shot** -- on the
30
+ first successful inventory POST pixie flips the binding to `ipxe-exit`,
31
+ so a PXE-first machine inventories itself exactly once and then stops
32
+ touching the boot (no re-inventory loop). Rebinding to
33
+ `pixie-inventory` buys one more pass. If no live env is staged the plan
34
+ degrades to `unavailable`/exit, so a bare deploy behaves like the old
35
+ exit-by-default until the operator fetches the live env. Set
36
+ `PIXIE_DEFAULT_BOOT_MODE=ipxe-exit` to restore the old exit-by-default.
37
+
38
+ ### `pixie-tui`
39
+
40
+ The target boots pixie's live env into an interactive TUI. An
41
+ operator drives the wizard on the target's own console (or IP-KVM /
42
+ serial-over-LAN). Useful for one-off image picks that don't need
43
+ recording on pixie's side.
44
+
45
+ ### `pixie-flash-once`
46
+
47
+ The target boots pixie's live env, which writes the bound image to
48
+ the picked disk serial, then reboots to firmware. Pixie flips the
49
+ binding to `ipxe-exit` after a successful flash so the next PXE
50
+ lands the target on its freshly-flashed local disk. Requires
51
+ inventory + a target disk serial.
52
+
53
+ ### `pixie-flash-always`
54
+
55
+ Same as `pixie-flash-once` but pixie leaves the binding on
56
+ `pixie-flash-always` after the flash. Every PXE re-flashes the
57
+ image; any local changes between reboots are discarded. Useful
58
+ for CI reflash loops where the target is expected to boot from a
59
+ known-clean image each cycle.
60
+
61
+ ### `nbdboot`
62
+
63
+ The target boots the image's OWN kernel (extracted from the sibling
64
+ netboot bundle) and mounts the image over NBD. By default, root is an
65
+ overlay-on-tmpfs: writes go to RAM, nothing propagates back to the
66
+ source blob. Multiple targets can nbdboot the same image
67
+ simultaneously because they each get their own overlay.
68
+
69
+ **Persistent overlays** flip a target from ephemeral to dev mode
70
+ without changing anything else about the bind. On the machine detail
71
+ page, the `Overlay alias` field is blank by default (ephemeral,
72
+ unchanged behaviour) or names a persistent overlay volume (e.g.
73
+ `simon`, `karl`, `ci-with-nvme-tools`). A non-blank alias attaches a
74
+ qcow2 volume with the image's base blob as `backing_file`, served by
75
+ `qemu-nbd` at a dedicated port. The target mounts the NBD device
76
+ read-write; system-level changes (apt-installed packages,
77
+ hardware-specific config, kernel modules) land on the qcow2 and
78
+ survive reboots.
79
+
80
+ An overlay is a **globally-unique named volume over one base image**,
81
+ not a per-machine file. See [](#overlays-are-volumes-not-per-machine-files)
82
+ below for how the alias, its base image, and single-writer access
83
+ work.
84
+
85
+ **Kexec into a locally-installed kernel.** The netboot bundle owns
86
+ the kernel and initrd pixie serves. Installing `linux-image-*` on the
87
+ target's persistent overlay writes files to `/boot` but the next
88
+ power-cycle refetches pixie's kernel and those files sit unused.
89
+ `kexec` bridges that gap: the netboot kernel comes up, then the
90
+ operator runs
91
+ `kexec -l /boot/vmlinuz-<v> --initrd=/boot/initrd.img-<v>
92
+ --reuse-cmdline && systemctl kexec` to switch to the local kernel
93
+ without going through firmware. Every netboot-shipping nosi variant
94
+ now bakes `kexec-tools` in; see nosi's [Custom kernel under netboot
95
+ (kexec)](https://safl.github.io/nosi/kexec.html) for the full
96
+ workflow. Recovery from a bad kernel is a power-cycle back to
97
+ pixie's kernel; nothing kexec's automatically, so a broken install
98
+ never becomes a boot loop.
99
+
100
+ ## Overlays are volumes, not per-machine files
101
+
102
+ An overlay is a **globally-unique named writable volume** layered over
103
+ exactly one base image. The alias is the identity, not the machine:
104
+ `data/overlays/<alias>.qcow2`, a single qcow2 whose `backing_file` is
105
+ the image's base blob. There is no `(mac, image, profile)` key and no
106
+ per-machine directory; two machines cannot mint independent files
107
+ under the same name, because the name is the volume.
108
+
109
+ **The alias implies its image.** Attaching an *existing* alias binds
110
+ the machine to that overlay's base image; you don't re-pick the image
111
+ on the bind form, the volume already knows it. Attaching a *new* alias
112
+ takes the image you selected and lays a fresh qcow2 over it. Because
113
+ the alias carries the image, moving a volume between machines is just
114
+ re-binding the alias; the base image follows.
115
+
116
+ **Single-writer, enforced in the app.** At most one machine holds an
117
+ alias at a time. Binding an alias already attached to another machine
118
+ is refused ("overlay `<alias>` held by `<mac>`; detach first") on both
119
+ the operator form and the `PUT /machines/{mac}` API, before any qcow2
120
+ is touched. The qemu qcow2 lock is the backstop; the app-level check
121
+ is the operator-facing guard. Rebinding a machine to ephemeral, to a
122
+ different alias, or to a non-`nbdboot` mode releases the hold it had.
123
+
124
+ **Managing volumes.** The Overlays page (`/ui/overlays`) lists every
125
+ volume with its state: *serving* (a live machine is bound to it and
126
+ `qemu-nbd` is up), *held* (attached to a machine, nothing serving),
127
+ *free* (unattached, kept for a future bind), *orphaned* (attached to a
128
+ MAC with no machine row), or *missing* (the qcow2 is gone). **Reset**
129
+ tears down `qemu-nbd` and unlinks one volume's qcow2 so the next boot
130
+ lazy-creates it fresh from the base; **Prune** reclaims the junk
131
+ states (orphaned + missing) in bulk and deliberately leaves free
132
+ volumes alone.
133
+
134
+ ## Bindings
135
+
136
+ A binding is what pixie serves a MAC next time it PXEs: a boot mode,
137
+ plus (for the modes that need one) an image, plus (for `nbdboot`) an
138
+ optional overlay alias. A fresh MAC auto-registers with
139
+ `PIXIE_DEFAULT_BOOT_MODE` (default `pixie-inventory`, which
140
+ self-terminates to `ipxe-exit` after one inventory pass) and no image
141
+ on first contact; the machine detail page is where an operator promotes
142
+ it. Saving the bind form persists all three fields
143
+ together, and the plan renderer reads them per PXE request: it resolves
144
+ the image's netboot bundle, resolves the alias to its overlay (and
145
+ base image), ensures the NBD export is up, and renders the plan. A
146
+ binding that can't be satisfied (no image bound, bundle not fetched,
147
+ alias held elsewhere) degrades to an `unavailable` plan whose comment
148
+ names the reason, rather than serving a broken boot.
149
+
150
+ ## Prerequisites at a glance
151
+
152
+ | Mode | Needs image | Needs inventory | Needs target disk |
153
+ |------|-------------|-----------------|-------------------|
154
+ | `ipxe-exit` | no | no | no |
155
+ | `pixie-inventory` | no | no | no |
156
+ | `pixie-tui` | no | no | no |
157
+ | `pixie-flash-once` | yes | yes | yes |
158
+ | `pixie-flash-always` | yes | yes | yes |
159
+ | `nbdboot` | yes (with sibling netboot bundle) | no | no |
160
+
161
+ The bind form on the machine detail page enforces these prerequisites
162
+ client-side; the server enforces them again with a 422 on the
163
+ `PUT /machines/{mac}` endpoint.
164
+
165
+ ## Extra kernel cmdline
166
+
167
+ Every mode that boots pixie's live env or the nbdboot chain accepts
168
+ extra kernel-cmdline tokens. Deploy-wide default:
169
+ `PIXIE_LIVE_ENV_EXTRA_CMDLINE` in the container env. Per-machine
170
+ override on the bind form: the `Extra kernel cmdline` text field.
171
+ A non-blank per-machine value fully overrides the deploy-wide default
172
+ for that ONE machine, so hardware quirks stay scoped to the target
173
+ that needs them. See [](hardware-quirks.md) for known-good tokens.
@@ -69,7 +69,7 @@ unattended). Flags choose how far the wipe goes:
69
69
 
70
70
  - `--data` - also delete `data/`: `state.db` (catalog, machines,
71
71
  events, exports, overlays, settings), every fetched blob and unpacked
72
- artifact, all per-machine overlays, and the staged `live-env/`.
72
+ artifact, all overlay volumes, and the staged `live-env/`.
73
73
  `data/` is a bind mount (`./data:/var/lib/pixie`), not a named
74
74
  volume, so purge unlinks it directly - `podman compose down -v`
75
75
  alone would not touch it.
@@ -135,10 +135,18 @@ if HTTP is proxied but NBD is direct.
135
135
  to every pixie-live-env chain. Common values are hardware-quirk
136
136
  workarounds; see [](hardware-quirks.md).
137
137
 
138
+ **`PIXIE_DEFAULT_BOOT_MODE`** (default: `pixie-inventory`) - boot mode a
139
+ freshly-discovered MAC auto-registers with. The default is
140
+ non-destructive (collect + post inventory, then exit) and one-shot (the
141
+ first inventory POST flips it to `ipxe-exit`, so a PXE-first box does
142
+ not re-inventory). Set to `ipxe-exit` for the old exit-by-default
143
+ behaviour. See [](boot-modes.md).
144
+
138
145
  **`PIXIE_DATA_DIR`** (default: `/var/lib/pixie`) - state root. Holds
139
146
  `state.db`, `blobs/` (fetched image bytes), `artifacts/` (extracted
140
- netboot bundles), `overlays/<mac>/<image_sha>/<profile>.qcow2`
141
- (per-machine writable overlays for nbdboot; see [](boot-modes.md)),
147
+ netboot bundles), `overlays/<alias>.qcow2`
148
+ (globally-named single-writer overlay volumes for nbdboot; see
149
+ [](boot-modes.md)),
142
150
  `live-env/` (staged pixie live env).
143
151
 
144
152
  **`PIXIE_LIVE_ENV_DIR`** (default: `$PIXIE_DATA_DIR/live-env`) - the
@@ -0,0 +1,97 @@
1
+ # Quickstart
2
+
3
+ This walks through a first pixie deploy: install, bring the container
4
+ up, point DHCP at it, and boot a first target.
5
+
6
+ ## Run the deploy generator (no install)
7
+
8
+ The `pixie-lab` commands (`init` / `deploy` / `purge`) are one-off
9
+ admin tasks, so run them ephemerally with `uv` instead of installing
10
+ anything:
11
+
12
+ ```
13
+ uv tool run pixie-lab init
14
+ ```
15
+
16
+ `uv tool run pixie-lab` fetches the distribution into a temporary
17
+ isolated environment, runs the command, and leaves nothing behind: no
18
+ system Python pollution, nothing to uninstall. Every command below is
19
+ invoked the same way. (If you would rather keep it around, `uv tool
20
+ install pixie-lab` installs the `pixie-lab` command persistently.)
21
+
22
+ The distribution ships two console-scripts: `pixie-lab` (the deploy
23
+ generator you run here) and `pixie` (the operator TUI baked into the
24
+ pixie live env; targets run it after booting the live env, operators
25
+ don't call it on their workstation).
26
+
27
+ ## Generate the deploy bundle
28
+
29
+ ```
30
+ mkdir /opt/pixie && cd /opt/pixie
31
+ uv tool run pixie-lab init
32
+ ${EDITOR:-vi} envvars # set PIXIE_HOST_ADDR + PIXIE_ADMIN_PASSWORD
33
+ ```
34
+
35
+ `uv tool run pixie-lab init` writes `compose.yml`, `envvars` (with placeholder
36
+ values), and `envvars.example` into the current directory. Edit
37
+ `envvars` to point pixie at your LAN address and set a real admin
38
+ password.
39
+
40
+ ## Bring pixie up
41
+
42
+ ```
43
+ podman compose --env-file envvars up -d
44
+ curl http://<PIXIE_HOST_ADDR>:8080/healthz
45
+ ```
46
+
47
+ The compose file runs pixie under `network_mode: host` so udp/69
48
+ (TFTP) plus the NBD port range plus tcp/8080 (HTTP) all reach the
49
+ LAN without publish gymnastics.
50
+
51
+ ## Point DHCP at pixie
52
+
53
+ Set your DHCP server to chain PXE targets through pixie's TFTP and
54
+ iPXE bootstrap. See [](deployment.md#dhcp-handoff) for BIOS + UEFI
55
+ dnsmasq recipes.
56
+
57
+ ## Add a first catalog entry
58
+
59
+ Open `http://<PIXIE_HOST_ADDR>:8080/ui/catalog` in a browser, log in
60
+ with your admin password, and add a catalog entry with a source URL.
61
+ Hit Fetch. Pixie pulls the bytes to disk (and for `img.gz` / `img.zst`
62
+ inputs, decompresses on the way in). The row flips to `fetched` when
63
+ the pipeline lands.
64
+
65
+ ## Bind a machine
66
+
67
+ Once a target has PXEd at least once, it appears on `/ui/machines`.
68
+ Click its MAC to open the machine detail page. Pick a boot mode from
69
+ the card grid, pick an image, and Save. On the target's next PXE,
70
+ pixie serves the plan you bound.
71
+
72
+ See [](boot-modes.md) for what each mode does and how bindings pin a
73
+ machine to an image and, for `nbdboot`, an overlay volume.
74
+
75
+ ## Tear it down
76
+
77
+ `uv tool run pixie-lab purge` is the inverse of the deploy. Run it from
78
+ the deploy directory (or pass the path):
79
+
80
+ ```
81
+ uv tool run pixie-lab purge # stop + remove the container only
82
+ uv tool run pixie-lab purge --data # also DELETE data/ (state.db, blobs, artifacts, overlays, live-env)
83
+ uv tool run pixie-lab purge --images # also remove the pixie container image
84
+ uv tool run pixie-lab purge --all # container + image + data/ + the deploy directory itself
85
+ ```
86
+
87
+ Purge prints its plan and gates every variant behind a `y/N`
88
+ confirmation; pass `--yes` to skip the prompt for scripted teardown.
89
+ Even the plain stop is impactful: it drops every nbdkit / qemu-nbd
90
+ export, so any target currently booted `nbdboot` off pixie loses its
91
+ NBD root at that moment. The `--data` and `--all` deletions are
92
+ irreversible, so the confirmation is deliberate.
93
+
94
+ The single-shot `uv tool run pixie-lab deploy` is the mirror image of purge: it
95
+ runs `init`, auto-fills `envvars`, brings the stack up, and waits on
96
+ `/healthz` in one command, for when you don't need to hand-edit the
97
+ generated files first.
@@ -0,0 +1,22 @@
1
+ # pixie netboot-pc live env: bake a BROAD driver set into the initramfs.
2
+ #
3
+ # The live env's very first action after the kernel hands off is to
4
+ # fetch its squashfs over HTTP from the kernel cmdline `fetch=` param
5
+ # (Debian live-boot). That means the target's NIC must be driven from
6
+ # INSIDE the initramfs, before pivot-root -- iPXE loaded vmlinuz+initrd
7
+ # over the firmware's network stack, but from Linux on it's the kernel
8
+ # module that matters.
9
+ #
10
+ # mkinitramfs' default is MODULES=dep: only the drivers the BUILD HOST
11
+ # needs. So the netboot-pc initrd shipped drivers for the builder's NIC
12
+ # (plus the r8125 DKMS hook) and nothing else -- a target with a
13
+ # different NIC (e.g. matx / ASRock Rack) came up to `initrd` and then
14
+ # stalled forever at the squashfs fetch, because its NIC had no driver.
15
+ #
16
+ # MODULES=most bakes the broad Debian-installer driver set, so an
17
+ # arbitrary lab box's NIC (and storage) is alive before the fetch.
18
+ # Costs initramfs size (~2x); acceptable for a netboot appliance whose
19
+ # whole job is booting unknown hardware. Narrow to an explicit NIC list
20
+ # in /etc/initramfs-tools/modules later if size becomes a problem and
21
+ # the target NICs are known.
22
+ MODULES=most
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pixie-lab"
3
- version = "0.3.2"
3
+ version = "0.4.1"
4
4
  description = "Bare-metal netboot appliance: catalog + fetch + NBD + PXE + TFTP + operator TUI in one container"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -311,10 +311,32 @@ def _cmd_purge(args: argparse.Namespace) -> int:
311
311
  shutil.rmtree(data_dir)
312
312
  print(f"pixie-lab purge: removed {data_dir}", file=sys.stderr)
313
313
 
314
- # 4. Delete the deploy directory itself.
314
+ # 4. Delete the deploy directory itself. ``dest`` often sits under a
315
+ # root-owned parent (e.g. /opt), where an unprivileged operator can
316
+ # empty the dir but can't rmdir it -- rmtree clears the contents,
317
+ # then raises on the final rmdir. Report that plainly instead of
318
+ # dumping a traceback; the state is gone either way, so it's a
319
+ # caveat, not a failure.
315
320
  if remove_dir and dest.exists():
316
- shutil.rmtree(dest)
317
- print(f"pixie-lab purge: removed {dest}", file=sys.stderr)
321
+ try:
322
+ shutil.rmtree(dest)
323
+ print(f"pixie-lab purge: removed {dest}", file=sys.stderr)
324
+ except OSError as exc:
325
+ leftover = sorted(p.name for p in dest.iterdir()) if dest.is_dir() else []
326
+ if not leftover:
327
+ print(
328
+ f"pixie-lab purge: emptied {dest}, but could not remove the "
329
+ f"directory itself ({exc.strerror or exc}) -- its parent is not "
330
+ f"writable (a root-owned /opt, say). The empty dir is harmless; "
331
+ f"you can redeploy into it or 'sudo rmdir' it.",
332
+ file=sys.stderr,
333
+ )
334
+ else:
335
+ print(
336
+ f"pixie-lab purge: could not fully remove {dest} "
337
+ f"({exc.strerror or exc}); leftover: {', '.join(leftover)}",
338
+ file=sys.stderr,
339
+ )
318
340
 
319
341
  return 0
320
342
 
@@ -21,7 +21,9 @@ from __future__ import annotations
21
21
  # ---------- catalog + fetch pipeline ---------------------------------
22
22
  #
23
23
  # Every catalog-store mutation emits one of these. ``subject_kind`` is
24
- # always ``"entry"``; ``subject_id`` is the catalog entry name.
24
+ # ``"entry"`` (``subject_id`` = the catalog entry name) for the per-row
25
+ # kinds; the ``catalog.import.*`` pair is catalog-scoped instead
26
+ # (``subject_kind = "catalog"``) since one import touches many entries.
25
27
 
26
28
  CATALOG_ENTRY_ADDED = "catalog.entry.added"
27
29
  """A new row landed via POST /catalog/entries, POST /ui/catalog/add,
@@ -95,36 +97,37 @@ EXPORT_NBDKIT_EXITED = "export.nbdkit.exited"
95
97
  (``_refresh_row`` noticed no live proc for a ``running`` row).
96
98
  ``details.previous_port`` + ``details.error`` explain."""
97
99
 
98
- # ---------- persistent overlays (per-machine qcow2) -----------------
100
+ # ---------- persistent overlays (alias-keyed volumes) ---------------
99
101
  #
100
- # ``subject_kind`` is always ``"machine"``; ``subject_id`` is the
101
- # canonical MAC. ``details`` names the (image_sha, profile) triple.
102
+ # ``subject_kind`` is always ``"overlay"``; ``subject_id`` is the
103
+ # globally-unique ``alias`` (``""`` for a fleet-wide bulk action). An
104
+ # overlay is a single-writer qcow2 volume over one base image, not a
105
+ # per-machine file; ``details`` carries ``alias`` / ``image_sha`` /
106
+ # ``qcow2_path`` as relevant.
102
107
 
103
108
  OVERLAY_CREATED = "overlay.created"
104
- """A per-machine qcow2 overlay was materialised for the first time
105
- for a ``(mac, image_sha, profile)`` triple. ``details`` carries
106
- ``image_sha``, ``profile``, and ``qcow2_path``."""
109
+ """An alias-keyed qcow2 overlay was materialised for the first time
110
+ (lazy-created on the first nbdboot render, or after a Reset).
111
+ ``details`` carries ``alias``, ``image_sha``, and ``qcow2_path``."""
107
112
 
108
113
  OVERLAY_RESET = "overlay.reset"
109
- """Operator hit Reset on the machine detail page: the qemu-nbd was
110
- terminated, the qcow2 was unlinked, the overlays row was deleted.
114
+ """Operator hit Reset (one alias) or Prune (bulk) on the Overlays
115
+ page: the qemu-nbd was terminated, the qcow2 unlinked, the overlays
116
+ row deleted. ``subject_id`` is the alias for a single Reset, ``""``
117
+ for a bulk Prune (``details.pruned`` counts the reclaimed volumes).
111
118
  Next plan render lazily creates a fresh overlay from the base."""
112
119
 
113
- OVERLAY_BOOTED = "overlay.booted"
114
- """Plan render for an already-existing overlay row (heartbeat).
115
- Emitted on every persistent nbdboot render so an operator can grep
116
- the events log for "which overlays am I actually using?"."""
117
-
118
120
  # ---------- machines + PXE ------------------------------------------
119
121
  #
120
122
  # ``subject_kind`` is always ``"machine"``; ``subject_id`` is the
121
123
  # canonical MAC address.
122
124
 
123
125
  MACHINE_DISCOVERED = "machine.discovered"
124
- """First-ever GET /pxe/<mac> for a MAC pixie has not seen before.
125
- Emitted from ``machines_store.touch_seen`` when a new row is
126
- created; subsequent hits do NOT emit (they update ``last_seen_at``
127
- via the ``pxe.plan.rendered`` event instead)."""
126
+ """First-ever GET /pxe/<mac> for a MAC pixie has not seen before. The
127
+ ``/pxe/<mac>`` route detects the absent row before ``touch_seen``
128
+ auto-registers it and emits this once; subsequent hits do NOT emit
129
+ (they update ``last_seen_at`` via the ``pxe.plan.rendered`` event
130
+ instead). ``details.boot_mode`` is the auto-registered default."""
128
131
 
129
132
  MACHINE_BOUND = "machine.bound"
130
133
  """First bind for a MAC: PUT /machines/<mac> or POST
@@ -232,7 +235,6 @@ KNOWN_EVENT_KINDS: frozenset[str] = frozenset(
232
235
  EXPORT_NBDKIT_EXITED,
233
236
  OVERLAY_CREATED,
234
237
  OVERLAY_RESET,
235
- OVERLAY_BOOTED,
236
238
  MACHINE_DISCOVERED,
237
239
  MACHINE_BOUND,
238
240
  MACHINE_BINDING_CHANGED,