pixie-lab 0.3.2__tar.gz → 0.4.0__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 (204) hide show
  1. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/CHANGELOG.md +126 -0
  2. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/PKG-INFO +1 -1
  3. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/src/boot-modes.md +63 -23
  4. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/src/deployment.md +4 -3
  5. pixie_lab-0.4.0/docs/src/quickstart.md +97 -0
  6. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pyproject.toml +1 -1
  7. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/deploy/_main.py +25 -3
  8. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/events/_kinds.py +21 -19
  9. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/exports/_store.py +118 -80
  10. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/machines/_routes.py +23 -7
  11. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/machines/_store.py +56 -30
  12. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/pxe/_renderer.py +62 -82
  13. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/pxe/_routes.py +13 -0
  14. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_bind_preview.py +13 -9
  15. pixie_lab-0.4.0/src/pixie/web/_fsutil.py +36 -0
  16. pixie_lab-0.4.0/src/pixie/web/_images.py +194 -0
  17. pixie_lab-0.4.0/src/pixie/web/_overlay_bind.py +101 -0
  18. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_overlays.py +68 -50
  19. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_settings_store.py +7 -5
  20. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/catalog.html +21 -57
  21. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/catalog_detail.html +1 -1
  22. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/dashboard.html +66 -76
  23. pixie_lab-0.4.0/src/pixie/web/_templates/image_detail.html +108 -0
  24. pixie_lab-0.4.0/src/pixie/web/_templates/images.html +139 -0
  25. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/layout.html +3 -0
  26. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/machine_detail.html +88 -69
  27. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/machines.html +16 -5
  28. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/overlays.html +36 -29
  29. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/main.py +303 -114
  30. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_deploy_unit.py +36 -0
  31. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_exports_unit.py +88 -33
  32. pixie_lab-0.4.0/tests/test_images_ui.py +223 -0
  33. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_machines_unit.py +130 -24
  34. pixie_lab-0.4.0/tests/test_overlays_ui.py +361 -0
  35. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_pxe_plan_json.py +44 -0
  36. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_settings.py +4 -1
  37. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_ui_pages.py +44 -6
  38. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_web_bind_preview.py +11 -7
  39. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/uv.lock +1 -1
  40. pixie_lab-0.3.2/docs/src/quickstart.md +0 -66
  41. pixie_lab-0.3.2/tests/test_overlays_ui.py +0 -255
  42. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/.dockerignore +0 -0
  43. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/.github/workflows/cicd.yaml +0 -0
  44. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/.github/workflows/docs.yml +0 -0
  45. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/.gitignore +0 -0
  46. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/Containerfile +0 -0
  47. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/LICENSE +0 -0
  48. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/Makefile +0 -0
  49. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/PLAN.md +0 -0
  50. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/README.md +0 -0
  51. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/netboot-pc.toml +0 -0
  52. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/test-pxe-flash-always.toml +0 -0
  53. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/test-pxe-flash.toml +0 -0
  54. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/test-pxe-inventory.toml +0 -0
  55. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/test-pxe-nbdboot.toml +0 -0
  56. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/test-pxe-tui.toml +0 -0
  57. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/test-pxe.toml +0 -0
  58. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/test-usb-ventoy.toml +0 -0
  59. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/configs/usbboot-pc.toml +0 -0
  60. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/live_build.py +0 -0
  61. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/pixie_ipxe_build.py +0 -0
  62. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/pixie_wheel_stage.py +0 -0
  63. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/pxe_flash_stage.py +0 -0
  64. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/pxe_inventory_stage.py +0 -0
  65. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/pxe_prepare.py +0 -0
  66. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/pxe_run_chain_test.py +0 -0
  67. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/usb_iso_build.py +0 -0
  68. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/usb_iso_verify.py +0 -0
  69. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/usb_ventoy_guest_start.py +0 -0
  70. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/scripts/usb_ventoy_stage.py +0 -0
  71. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/netboot-pc.yaml +0 -0
  72. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/test-pxe-flash-always.yaml +0 -0
  73. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/test-pxe-flash.yaml +0 -0
  74. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/test-pxe-inventory.yaml +0 -0
  75. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/test-pxe-nbdboot.yaml +0 -0
  76. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/test-pxe-tui.yaml +0 -0
  77. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/test-pxe.yaml +0 -0
  78. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/test-usb-ventoy.yaml +0 -0
  79. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/cijoe/tasks/usbboot-pc.yaml +0 -0
  80. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/audit.md +0 -0
  81. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/src/conf.py +0 -0
  82. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/src/hardware-quirks.md +0 -0
  83. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/src/index.md +0 -0
  84. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/tooling/README.md +0 -0
  85. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/tooling/pyproject.toml +0 -0
  86. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/tooling/src/pixie_docs/__init__.py +0 -0
  87. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/docs/tooling/src/pixie_docs/cli.py +0 -0
  88. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/README.md +0 -0
  89. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/auxiliary/cloudinit-metadata.meta +0 -0
  90. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/auxiliary/ipxe-embed.ipxe +0 -0
  91. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/auxiliary/ipxe-local-general.h +0 -0
  92. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/auto/config +0 -0
  93. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/archives/backports.list.binary +0 -0
  94. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/archives/backports.list.chroot +0 -0
  95. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/bootloaders/grub-efi/splash.png +0 -0
  96. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/bootloaders/syslinux/splash.png +0 -0
  97. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/hooks/normal/0500-pixie-install.hook.chroot +0 -0
  98. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/hooks/normal/0500-pixie-skip-bootloader-menu.hook.binary +0 -0
  99. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/hooks/normal/0600-pixie-r8125-dkms.hook.chroot +0 -0
  100. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/hooks/normal/0700-pixie-clock-from-http.hook.chroot +0 -0
  101. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/hooks/normal/0800-pixie-ssh-live.hook.chroot +0 -0
  102. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/hooks/normal/0900-pixie-enable-services.hook.chroot +0 -0
  103. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/hooks/normal/0980-pixie-apt-validate.hook.chroot +0 -0
  104. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/issue +0 -0
  105. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/modprobe.d/zz-pixie-blacklist-nouveau.conf +0 -0
  106. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/motd +0 -0
  107. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/profile.d/pixie-version.sh +0 -0
  108. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/getty@.service.d/zzz-pixie-autologin-root.conf +0 -0
  109. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-early.service +0 -0
  110. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-late.service +0 -0
  111. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-mid.service +0 -0
  112. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-clock-from-http.service +0 -0
  113. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-images-discover.service +0 -0
  114. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-on-tty1.service +0 -0
  115. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-usb-grow.service +0 -0
  116. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/var-lib-pixie-images.mount +0 -0
  117. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/etc/udev/rules.d/70-pixie-realtek-2g5-offloads.rules +0 -0
  118. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/usr/lib/systemd/system-generators/pixie-skip-usb-only-units-on-netboot +0 -0
  119. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-boot-banner +0 -0
  120. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-clock-from-http +0 -0
  121. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-images-discover +0 -0
  122. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-on-tty1 +0 -0
  123. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-trace +0 -0
  124. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-usb-grow +0 -0
  125. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/package-lists/pixie-base.list.chroot +0 -0
  126. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/package-lists/pixie-flash.list.chroot +0 -0
  127. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/preferences/backports.pref.binary +0 -0
  128. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/pixie-media/live-build/config/preferences/backports.pref.chroot +0 -0
  129. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/__init__.py +0 -0
  130. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/_partition.py +0 -0
  131. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/_util.py +0 -0
  132. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/catalog/__init__.py +0 -0
  133. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/catalog/_fetcher.py +0 -0
  134. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/catalog/_live_env.py +0 -0
  135. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/catalog/_routes.py +0 -0
  136. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/catalog/_schema.py +0 -0
  137. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/catalog/_store.py +0 -0
  138. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/catalog/catalog.toml +0 -0
  139. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/deploy/__init__.py +0 -0
  140. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/deploy/_templates.py +0 -0
  141. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/disks.py +0 -0
  142. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/events/__init__.py +0 -0
  143. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/events/_log.py +0 -0
  144. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/events/_routes.py +0 -0
  145. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/exports/__init__.py +0 -0
  146. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/exports/_routes.py +0 -0
  147. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/exports/_supervisor.py +0 -0
  148. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/flash.py +0 -0
  149. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/images.py +0 -0
  150. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/machines/__init__.py +0 -0
  151. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/oras.py +0 -0
  152. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/pivot/__init__.py +0 -0
  153. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/pivot/nbdboot +0 -0
  154. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/pxe/__init__.py +0 -0
  155. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/tftp/__init__.py +0 -0
  156. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/tftp/_supervisor.py +0 -0
  157. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/tui/__init__.py +0 -0
  158. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/tui/_app.py +0 -0
  159. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/tui_catalog.py +0 -0
  160. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/__init__.py +0 -0
  161. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_auth.py +0 -0
  162. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_inventory.py +0 -0
  163. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/.gitkeep +0 -0
  164. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/bootstrap-icons.min.css +0 -0
  165. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/bootstrap.min.css +0 -0
  166. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/fonts/bootstrap-icons.woff +0 -0
  167. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/fonts/bootstrap-icons.woff2 +0 -0
  168. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/htmx.min.js +0 -0
  169. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/pixie-favicon.png +0 -0
  170. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_static/sse.js +0 -0
  171. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_table_state.py +0 -0
  172. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/_partials/page_description.html +0 -0
  173. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/_partials/recent_events.html +0 -0
  174. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/_partials/table_helpers.html +0 -0
  175. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/events.html +0 -0
  176. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/ipxe/bootstrap.j2 +0 -0
  177. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/ipxe/exit.j2 +0 -0
  178. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/ipxe/nbdboot.j2 +0 -0
  179. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/ipxe/pixie-live-env.j2 +0 -0
  180. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/ipxe/unavailable.j2 +0 -0
  181. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/live_env.html +0 -0
  182. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/login.html +0 -0
  183. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/src/pixie/web/_templates/settings.html +0 -0
  184. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/__init__.py +0 -0
  185. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/conftest.py +0 -0
  186. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/__init__.py +0 -0
  187. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/conftest.py +0 -0
  188. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/test_catalog_fetch.py +0 -0
  189. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/test_deploy.py +0 -0
  190. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/test_exports.py +0 -0
  191. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/test_inventory.py +0 -0
  192. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/test_pxe.py +0 -0
  193. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/integration/test_tftp.py +0 -0
  194. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_auth.py +0 -0
  195. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_catalog_api.py +0 -0
  196. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_catalog_seed.py +0 -0
  197. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_event_kinds.py +0 -0
  198. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_events_unit.py +0 -0
  199. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_fetch_progress.py +0 -0
  200. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_healthz.py +0 -0
  201. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_live_env_fetch.py +0 -0
  202. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_pivot.py +0 -0
  203. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_schema.py +0 -0
  204. {pixie_lab-0.3.2 → pixie_lab-0.4.0}/tests/test_web_inventory.py +0 -0
@@ -11,6 +11,132 @@ operator-facing summary.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.4.0] - 2026-07-24
15
+
16
+ ### Changed
17
+
18
+ **Machines list Image column reads like the new model.** The bound
19
+ image now shows the catalog name linked to its Images page (short sha
20
+ for an orphan blob whose entry was deleted), instead of a bare hash,
21
+ and is blank for boot modes that don't consume an image (ipxe-exit /
22
+ inventory / tui) rather than showing a stale sha.
23
+
24
+ **Catalog table is leaner.** The sources listing drops the inline
25
+ content sha + byte size (image facts that live on the linked Image
26
+ page); the fetched/error/in-flight readiness state stays.
27
+
28
+ **Default timestamp format drops the timezone suffix.** Operator-facing
29
+ timestamps now default to `%Y-%m-%d %H:%M:%S` (24-hour, no ` %Z`); the
30
+ display timezone is still an operator setting, just not repeated on
31
+ every row. Set `PIXIE_DATETIME_FORMAT` or the Settings override to add
32
+ it back.
33
+
34
+ **Overlays are now globally-named single-writer volumes.** A persistent
35
+ nbdboot overlay is no longer a per-`(machine, image, profile)` triple; it
36
+ is a globally-unique named writable volume (`alias`) over ONE base image,
37
+ and the base image is implied by the alias. At most one machine may hold
38
+ an alias at a time: attaching one already held by a different machine is
39
+ rejected in the app ("held by <mac>; detach first"), with qemu-nbd's
40
+ qcow2 image-lock as the backstop. On a machine's detail page the picker
41
+ now offers the aliases that are free (or already held here) plus a
42
+ create-new flow; attaching an existing alias binds the machine to that
43
+ alias's base image. The Overlays page keys on the alias, shows an
44
+ **Attached to** column (a MAC or "free"), and classifies each row as
45
+ serving / held / free / orphaned / missing; Prune still reclaims only the
46
+ orphaned + file-missing rows and leaves a free alias (a deliberate keep)
47
+ alone. On-disk overlays move from
48
+ `overlays/<mac>/<image_sha>/<profile>.qcow2` to a flat
49
+ `overlays/<alias>.qcow2`. Existing state.db rows migrate in place on
50
+ first start: each old overlay becomes `alias = <profile>-<mac_slug>`
51
+ holding its original MAC, its qcow2 path is kept as-is (no large-file
52
+ move), and each machine's binding is rewritten to the same derived alias.
53
+
54
+ **Dashboard speaks the new model.** The tiles now mirror the
55
+ storage/lifecycle vocabulary + the nav: **Machines**, **Catalog**
56
+ (sources you can fetch, with a not-yet-fetched count), **Images** (the
57
+ materialised entities, with on-disk footprint + how many are
58
+ reclaimable), **Overlays** (alias-keyed writable volumes, with disk-used
59
+ + serving), and **Events** -- instead of the old catalog-images /
60
+ NBD-exports framing. The Acknowledge button was removed from the
61
+ dashboard (it's an action; the dashboard is output-only -- ack lives on
62
+ the Events page).
63
+
64
+ **Catalog is sources-only now.** With Images owning the materialised
65
+ view, the Catalog page drops the NBD-serving column and the
66
+ Stop-export / blob-delete actions (those live on Images). It keeps
67
+ source management -- add, import, Fetch, delete-entry -- and a fetched
68
+ disk-image row now links to its **Image** for footprint, live NBD /
69
+ overlay usage, and GC.
70
+
71
+ **Legacy `/ui/exports` lands on Images.** The kept-alive redirect for
72
+ the old exports URL (and the Stop-export action) now points at the
73
+ Images view, where NBD export usage is surfaced per content sha, rather
74
+ than at Catalog.
75
+
76
+ **Overlay picker shows aliases held elsewhere, disabled.** The
77
+ machine-detail overlay dropdown used to hide any alias held by another
78
+ machine, so an operator searching for it assumed it didn't exist and
79
+ tried to create a duplicate (which the single-writer bind then
80
+ rejected). Held-elsewhere aliases are now shown as a disabled option
81
+ labelled with the holder MAC ("held by `<mac>`") -- visible, but not
82
+ selectable.
83
+
84
+ **Overlays distinguish "pending" from "file missing".** A reserved
85
+ overlay alias whose qcow2 hasn't been lazy-created yet (never booted)
86
+ previously read as *file missing* -- the same alarming state as a
87
+ booted-then-lost overlay -- and Prune would reclaim it as junk. Such an
88
+ overlay is now classified **pending** (benign, awaiting its first
89
+ nbdboot) and is left alone by Prune; *missing* is reserved for an
90
+ overlay whose qcow2 vanished after a prior boot (real data loss). The
91
+ `last_boot_at` timestamp is the discriminator.
92
+
93
+ ### Added
94
+
95
+ **First-contact machines emit a `machine.discovered` audit event.** The
96
+ first time an unknown MAC hits `GET /pxe/<mac>` and is auto-registered,
97
+ pixie now records one `machine.discovered` event (carrying the
98
+ auto-registered boot mode + the client IP); repeat contacts do not
99
+ re-emit. The kind was reserved but never fired before.
100
+
101
+ **Images: the materialised content behind Catalog sources.** A new
102
+ **Images** page (`/ui/images`) separates the *source* (a Catalog entry,
103
+ a URL you can fetch) from the *entity* it produces once fetched (an
104
+ image, identity = the disk content sha). Machines, NBD exports, and
105
+ overlays all key off that sha, so each image rolls up its on-disk
106
+ footprint (raw disk + rootfs + boot bundle + overlays) and every live
107
+ usage -- machines bound, the ephemeral nbdkit export, and per-machine
108
+ qemu-nbd overlays -- with counts that link into the per-usage admin
109
+ surfaces. An **image detail hub** (`/ui/images/<sha>`) lays out the
110
+ artifacts + every usage inline and offers a **guarded delete / blob GC**:
111
+ allowed only when the usage count (the refcount) is zero, it removes the
112
+ whole `blobs/<sha>/` dir (raw disk **and** the `rootfs.raw` the old
113
+ per-entry delete leaked) and clears the sha off every entry that
114
+ resolved to it. The list also surfaces **orphan blobs** -- sha dirs on
115
+ disk with no catalog entry -- which is where the un-GC'd disk pressure
116
+ actually hides; they delete straight through. Catalog is unchanged for
117
+ now.
118
+
119
+ ### Fixed
120
+
121
+ **Live-env artifacts are served after a post-startup fetch, no restart
122
+ needed.** The `/boot/pixie-live-env/` mount was created at app startup
123
+ only if the directory already existed, so on a fresh deploy -- where the
124
+ operator stages the netboot-pc bake later via "Fetch live env" -- the
125
+ plan pointed targets at `/boot/pixie-live-env/vmlinuz` and friends that
126
+ then 404'd until pixie was restarted, silently breaking every
127
+ `pixie-inventory` / `-tui` / `-flash` boot. The mount is now created
128
+ unconditionally (dir ensured, `check_dir=False`), so a fetched live-env
129
+ is served immediately.
130
+
131
+ **`pixie-lab purge --all` no longer tracebacks on a root-owned parent.**
132
+ When the deploy dir lives under a directory the operator can't write
133
+ (the common `/opt/pixie` case), purge could empty the dir but not
134
+ remove the dir itself, and surfaced the failure as a raw Python
135
+ traceback. It now clears the state, prints a plain one-line caveat
136
+ ("emptied `<dir>`, but could not remove the directory itself ..."), and
137
+ exits 0 -- the purge did its job; only the empty-dir removal was blocked
138
+ by the parent's permissions.
139
+
14
140
  ## [0.3.2] - 2026-07-24
15
141
 
16
142
  ### 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.0
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
@@ -54,29 +54,21 @@ overlay-on-tmpfs: writes go to RAM, nothing propagates back to the
54
54
  source blob. Multiple targets can nbdboot the same image
55
55
  simultaneously because they each get their own overlay.
56
56
 
57
- **Persistent overlays** flip a single target from ephemeral to dev
58
- mode without changing anything else about the bind. On the machine
59
- detail page, the `Overlay profile` field is blank by default
60
- (ephemeral, unchanged behaviour) or names a per-machine profile
61
- (e.g. `simon`, `karl`, `ci-with-nvme-tools`). A non-blank profile
62
- maps to a per-machine qcow2 file with the image's base blob as
63
- `backing_file`, served by `qemu-nbd` at a dedicated port. The target
64
- mounts the NBD device read-write; system-level changes (apt-installed
65
- packages, hardware-specific config, kernel modules) land on the qcow2
66
- and survive reboots.
67
-
68
- Overlays are keyed by `(mac, image_content_sha256, profile)`:
69
- different machines have fully independent files even under the same
70
- profile name, and rebinding a machine to a different image leaves the
71
- old image's overlays on disk (a rebind back resumes them). Storage is
72
- `data/overlays/<mac>/<image_sha>/<profile>.qcow2`. The **Reset**
73
- button on the machine detail page tears down `qemu-nbd`, unlinks the
74
- qcow2, and lets the next boot lazy-create a fresh overlay from the
75
- base.
76
-
77
- Concurrency is by construction: a MAC boots one target at a time, so
78
- two machines can never contend for the same qcow2. There is no
79
- holder-tracking or force-reclaim.
57
+ **Persistent overlays** flip a target from ephemeral to dev mode
58
+ without changing anything else about the bind. On the machine detail
59
+ page, the `Overlay alias` field is blank by default (ephemeral,
60
+ unchanged behaviour) or names a persistent overlay volume (e.g.
61
+ `simon`, `karl`, `ci-with-nvme-tools`). A non-blank alias attaches a
62
+ qcow2 volume with the image's base blob as `backing_file`, served by
63
+ `qemu-nbd` at a dedicated port. The target mounts the NBD device
64
+ read-write; system-level changes (apt-installed packages,
65
+ hardware-specific config, kernel modules) land on the qcow2 and
66
+ survive reboots.
67
+
68
+ An overlay is a **globally-unique named volume over one base image**,
69
+ not a per-machine file. See [](#overlays-are-volumes-not-per-machine-files)
70
+ below for how the alias, its base image, and single-writer access
71
+ work.
80
72
 
81
73
  **Kexec into a locally-installed kernel.** The netboot bundle owns
82
74
  the kernel and initrd pixie serves. Installing `linux-image-*` on the
@@ -93,6 +85,54 @@ workflow. Recovery from a bad kernel is a power-cycle back to
93
85
  pixie's kernel; nothing kexec's automatically, so a broken install
94
86
  never becomes a boot loop.
95
87
 
88
+ ## Overlays are volumes, not per-machine files
89
+
90
+ An overlay is a **globally-unique named writable volume** layered over
91
+ exactly one base image. The alias is the identity, not the machine:
92
+ `data/overlays/<alias>.qcow2`, a single qcow2 whose `backing_file` is
93
+ the image's base blob. There is no `(mac, image, profile)` key and no
94
+ per-machine directory; two machines cannot mint independent files
95
+ under the same name, because the name is the volume.
96
+
97
+ **The alias implies its image.** Attaching an *existing* alias binds
98
+ the machine to that overlay's base image; you don't re-pick the image
99
+ on the bind form, the volume already knows it. Attaching a *new* alias
100
+ takes the image you selected and lays a fresh qcow2 over it. Because
101
+ the alias carries the image, moving a volume between machines is just
102
+ re-binding the alias; the base image follows.
103
+
104
+ **Single-writer, enforced in the app.** At most one machine holds an
105
+ alias at a time. Binding an alias already attached to another machine
106
+ is refused ("overlay `<alias>` held by `<mac>`; detach first") on both
107
+ the operator form and the `PUT /machines/{mac}` API, before any qcow2
108
+ is touched. The qemu qcow2 lock is the backstop; the app-level check
109
+ is the operator-facing guard. Rebinding a machine to ephemeral, to a
110
+ different alias, or to a non-`nbdboot` mode releases the hold it had.
111
+
112
+ **Managing volumes.** The Overlays page (`/ui/overlays`) lists every
113
+ volume with its state: *serving* (a live machine is bound to it and
114
+ `qemu-nbd` is up), *held* (attached to a machine, nothing serving),
115
+ *free* (unattached, kept for a future bind), *orphaned* (attached to a
116
+ MAC with no machine row), or *missing* (the qcow2 is gone). **Reset**
117
+ tears down `qemu-nbd` and unlinks one volume's qcow2 so the next boot
118
+ lazy-creates it fresh from the base; **Prune** reclaims the junk
119
+ states (orphaned + missing) in bulk and deliberately leaves free
120
+ volumes alone.
121
+
122
+ ## Bindings
123
+
124
+ A binding is what pixie serves a MAC next time it PXEs: a boot mode,
125
+ plus (for the modes that need one) an image, plus (for `nbdboot`) an
126
+ optional overlay alias. A fresh MAC auto-registers as `ipxe-exit` with
127
+ no image on first contact; the machine detail page is where an
128
+ operator promotes it. Saving the bind form persists all three fields
129
+ together, and the plan renderer reads them per PXE request: it resolves
130
+ the image's netboot bundle, resolves the alias to its overlay (and
131
+ base image), ensures the NBD export is up, and renders the plan. A
132
+ binding that can't be satisfied (no image bound, bundle not fetched,
133
+ alias held elsewhere) degrades to an `unavailable` plan whose comment
134
+ names the reason, rather than serving a broken boot.
135
+
96
136
  ## Prerequisites at a glance
97
137
 
98
138
  | Mode | Needs image | Needs inventory | Needs target disk |
@@ -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.
@@ -137,8 +137,9 @@ workarounds; see [](hardware-quirks.md).
137
137
 
138
138
  **`PIXIE_DATA_DIR`** (default: `/var/lib/pixie`) - state root. Holds
139
139
  `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)),
140
+ netboot bundles), `overlays/<alias>.qcow2`
141
+ (globally-named single-writer overlay volumes for nbdboot; see
142
+ [](boot-modes.md)),
142
143
  `live-env/` (staged pixie live env).
143
144
 
144
145
  **`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.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pixie-lab"
3
- version = "0.3.2"
3
+ version = "0.4.0"
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,