pixie-lab 0.1.0__tar.gz → 0.2.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 (186) hide show
  1. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/CHANGELOG.md +73 -4
  2. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/Containerfile +4 -0
  3. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/PKG-INFO +8 -6
  4. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/README.md +7 -5
  5. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/test-pxe-nbdboot.toml +59 -0
  6. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/pxe_run_chain_test.py +41 -5
  7. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/src/boot-modes.md +40 -1
  8. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/src/deployment.md +77 -3
  9. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/src/hardware-quirks.md +32 -0
  10. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/src/quickstart.md +3 -3
  11. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pyproject.toml +1 -1
  12. pixie_lab-0.2.0/src/pixie/_partition.py +152 -0
  13. pixie_lab-0.2.0/src/pixie/_util.py +17 -0
  14. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/catalog/_fetcher.py +231 -68
  15. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/catalog/_routes.py +5 -6
  16. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/catalog/_store.py +10 -9
  17. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/deploy/_main.py +122 -26
  18. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/events/_kinds.py +44 -1
  19. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/events/_log.py +53 -1
  20. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/exports/__init__.py +0 -5
  21. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/exports/_routes.py +3 -2
  22. pixie_lab-0.2.0/src/pixie/exports/_store.py +347 -0
  23. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/exports/_supervisor.py +143 -7
  24. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/flash.py +4 -6
  25. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/images.py +8 -9
  26. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/machines/_routes.py +10 -0
  27. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/machines/_store.py +52 -9
  28. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/pivot/nbdboot +116 -55
  29. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/pxe/_renderer.py +127 -4
  30. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/pxe/_routes.py +2 -2
  31. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/tui/__init__.py +7 -9
  32. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/tui/_app.py +5 -4
  33. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/tui_catalog.py +16 -22
  34. pixie_lab-0.2.0/src/pixie/web/__init__.py +9 -0
  35. pixie_lab-0.2.0/src/pixie/web/_bind_preview.py +84 -0
  36. pixie_lab-0.2.0/src/pixie/web/_inventory.py +275 -0
  37. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/dashboard.html +74 -3
  38. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/events.html +17 -0
  39. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/ipxe/nbdboot.j2 +10 -1
  40. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/machine_detail.html +227 -43
  41. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/main.py +250 -2
  42. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/conftest.py +11 -0
  43. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_deploy_unit.py +71 -0
  44. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_events_unit.py +33 -0
  45. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_exports_unit.py +57 -0
  46. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_fetch_progress.py +69 -2
  47. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_machines_unit.py +73 -0
  48. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_ui_pages.py +108 -9
  49. pixie_lab-0.2.0/tests/test_web_bind_preview.py +89 -0
  50. pixie_lab-0.2.0/tests/test_web_inventory.py +232 -0
  51. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/uv.lock +1 -1
  52. pixie_lab-0.1.0/src/pixie/_util.py +0 -38
  53. pixie_lab-0.1.0/src/pixie/exports/_store.py +0 -156
  54. pixie_lab-0.1.0/src/pixie/web/__init__.py +0 -6
  55. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/.dockerignore +0 -0
  56. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/.github/workflows/cicd.yaml +0 -0
  57. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/.github/workflows/docs.yml +0 -0
  58. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/.gitignore +0 -0
  59. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/LICENSE +0 -0
  60. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/Makefile +0 -0
  61. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/PLAN.md +0 -0
  62. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/netboot-pc.toml +0 -0
  63. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/test-pxe-flash-always.toml +0 -0
  64. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/test-pxe-flash.toml +0 -0
  65. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/test-pxe-inventory.toml +0 -0
  66. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/test-pxe-tui.toml +0 -0
  67. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/test-pxe.toml +0 -0
  68. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/configs/usbboot-pc.toml +0 -0
  69. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/live_build.py +0 -0
  70. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/pixie_ipxe_build.py +0 -0
  71. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/pixie_wheel_stage.py +0 -0
  72. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/pxe_flash_stage.py +0 -0
  73. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/pxe_inventory_stage.py +0 -0
  74. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/pxe_prepare.py +0 -0
  75. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/scripts/usb_iso_build.py +0 -0
  76. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/netboot-pc.yaml +0 -0
  77. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/test-pxe-flash-always.yaml +0 -0
  78. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/test-pxe-flash.yaml +0 -0
  79. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/test-pxe-inventory.yaml +0 -0
  80. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/test-pxe-nbdboot.yaml +0 -0
  81. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/test-pxe-tui.yaml +0 -0
  82. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/test-pxe.yaml +0 -0
  83. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/cijoe/tasks/usbboot-pc.yaml +0 -0
  84. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/audit.md +0 -0
  85. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/src/conf.py +0 -0
  86. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/src/index.md +0 -0
  87. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/tooling/README.md +0 -0
  88. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/tooling/pyproject.toml +0 -0
  89. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/tooling/src/pixie_docs/__init__.py +0 -0
  90. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/docs/tooling/src/pixie_docs/cli.py +0 -0
  91. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/README.md +0 -0
  92. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/auxiliary/cloudinit-metadata.meta +0 -0
  93. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/auxiliary/ipxe-embed.ipxe +0 -0
  94. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/auxiliary/ipxe-local-general.h +0 -0
  95. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/auto/config +0 -0
  96. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/archives/backports.list.binary +0 -0
  97. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/archives/backports.list.chroot +0 -0
  98. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/bootloaders/grub-efi/splash.png +0 -0
  99. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/bootloaders/syslinux/splash.png +0 -0
  100. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/hooks/normal/0500-pixie-install.hook.chroot +0 -0
  101. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/hooks/normal/0500-pixie-skip-bootloader-menu.hook.binary +0 -0
  102. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/hooks/normal/0600-pixie-r8125-dkms.hook.chroot +0 -0
  103. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/hooks/normal/0700-pixie-clock-from-http.hook.chroot +0 -0
  104. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/hooks/normal/0800-pixie-ssh-live.hook.chroot +0 -0
  105. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/hooks/normal/0900-pixie-enable-services.hook.chroot +0 -0
  106. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/hooks/normal/0980-pixie-apt-validate.hook.chroot +0 -0
  107. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/issue +0 -0
  108. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/modprobe.d/zz-pixie-blacklist-nouveau.conf +0 -0
  109. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/motd +0 -0
  110. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/profile.d/pixie-version.sh +0 -0
  111. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/getty@.service.d/zzz-pixie-autologin-root.conf +0 -0
  112. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-early.service +0 -0
  113. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-late.service +0 -0
  114. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-banner-mid.service +0 -0
  115. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-clock-from-http.service +0 -0
  116. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-images-discover.service +0 -0
  117. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-on-tty1.service +0 -0
  118. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/pixie-usb-grow.service +0 -0
  119. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/systemd/system/var-lib-pixie-images.mount +0 -0
  120. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/etc/udev/rules.d/70-pixie-realtek-2g5-offloads.rules +0 -0
  121. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/usr/lib/systemd/system-generators/pixie-skip-usb-only-units-on-netboot +0 -0
  122. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-boot-banner +0 -0
  123. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-clock-from-http +0 -0
  124. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-images-discover +0 -0
  125. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-on-tty1 +0 -0
  126. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-trace +0 -0
  127. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/includes.chroot/usr/local/sbin/pixie-usb-grow +0 -0
  128. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/package-lists/pixie-base.list.chroot +0 -0
  129. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/package-lists/pixie-flash.list.chroot +0 -0
  130. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/preferences/backports.pref.binary +0 -0
  131. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/pixie-media/live-build/config/preferences/backports.pref.chroot +0 -0
  132. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/__init__.py +0 -0
  133. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/catalog/__init__.py +0 -0
  134. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/catalog/_schema.py +0 -0
  135. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/deploy/__init__.py +0 -0
  136. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/deploy/_templates.py +0 -0
  137. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/disks.py +0 -0
  138. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/events/__init__.py +0 -0
  139. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/events/_routes.py +0 -0
  140. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/machines/__init__.py +0 -0
  141. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/oras.py +0 -0
  142. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/pivot/__init__.py +0 -0
  143. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/pxe/__init__.py +0 -0
  144. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/tftp/__init__.py +0 -0
  145. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/tftp/_supervisor.py +0 -0
  146. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_auth.py +0 -0
  147. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_settings_store.py +0 -0
  148. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/.gitkeep +0 -0
  149. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/bootstrap-icons.min.css +0 -0
  150. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/bootstrap.min.css +0 -0
  151. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/fonts/bootstrap-icons.woff +0 -0
  152. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/fonts/bootstrap-icons.woff2 +0 -0
  153. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/htmx.min.js +0 -0
  154. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/pixie-favicon.png +0 -0
  155. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_static/sse.js +0 -0
  156. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_table_state.py +0 -0
  157. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/_partials/page_description.html +0 -0
  158. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/_partials/recent_events.html +0 -0
  159. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/_partials/table_helpers.html +0 -0
  160. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/catalog.html +0 -0
  161. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/catalog_detail.html +0 -0
  162. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/ipxe/bootstrap.j2 +0 -0
  163. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/ipxe/exit.j2 +0 -0
  164. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/ipxe/pixie-live-env.j2 +0 -0
  165. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/ipxe/unavailable.j2 +0 -0
  166. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/layout.html +0 -0
  167. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/login.html +0 -0
  168. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/machines.html +0 -0
  169. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/src/pixie/web/_templates/settings.html +0 -0
  170. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/__init__.py +0 -0
  171. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/__init__.py +0 -0
  172. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/conftest.py +0 -0
  173. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/test_catalog_fetch.py +0 -0
  174. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/test_deploy.py +0 -0
  175. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/test_exports.py +0 -0
  176. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/test_inventory.py +0 -0
  177. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/test_pxe.py +0 -0
  178. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/integration/test_tftp.py +0 -0
  179. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_auth.py +0 -0
  180. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_catalog_api.py +0 -0
  181. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_event_kinds.py +0 -0
  182. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_healthz.py +0 -0
  183. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_pivot.py +0 -0
  184. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_pxe_plan_json.py +0 -0
  185. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_schema.py +0 -0
  186. {pixie_lab-0.1.0 → pixie_lab-0.2.0}/tests/test_settings.py +0 -0
@@ -7,15 +7,84 @@ perceives, defaults that survived a `pip install -U`, and gates that
7
7
  landed in CI.
8
8
 
9
9
  Per-release commit history lives in `git log`; this file is the
10
- operator-facing summary. Nothing between the 0.1.0 skeleton and the
11
- next real release has been tagged: the intermediate work all lands
12
- under `[Unreleased]` until an operator can drive the full flow end-
13
- to-end on real hardware.
10
+ operator-facing summary.
14
11
 
15
12
  ## [Unreleased]
16
13
 
14
+ ## [0.2.0] - 2026-07-22
15
+
16
+ First real release after the 0.1.0 skeleton: nbdboot (ephemeral +
17
+ persistent per-machine overlays) validated end-to-end on real
18
+ hardware, plus a bty-lab-shaped deploy CLI and an operator-managed
19
+ event log.
20
+
17
21
  ### Added
18
22
 
23
+ **Event-log Acknowledge + Clear actions.** The `/ui/events` page grows
24
+ two bulk actions: **Acknowledge** advances the ack cursor so the
25
+ dashboard's unacknowledged-error count zeros without touching the log,
26
+ and **Clear** wipes the whole log (behind a confirm) and drops one
27
+ `events.cleared` marker so the reset itself stays on the record.
28
+ Mirrors the ack/clear affordances bty's event log carried.
29
+
30
+ **`pixie-lab purge` reworked to match `bty-lab`.** `purge` now prints
31
+ a plan and gates the destructive parts behind a `y/N` confirmation
32
+ (`-y`/`--yes` to skip; a non-TTY refuses without `--yes`). Flags:
33
+ `--data` deletes the on-disk state (previously `--wipe-data`, which
34
+ never actually removed the bind-mounted `data/`), `--images` removes
35
+ the container image, and `--all` also removes the deploy directory
36
+ (implies `--data`).
37
+
38
+ **Per-machine persistent qcow2 overlays under `nbdboot`.** A new
39
+ `overlay_profile` field on the machine binding flips one target from
40
+ the default ephemeral-tmpfs behaviour to a per-machine writable
41
+ overlay without changing anything else about the bind. A non-blank
42
+ profile maps to a `data/overlays/<mac>/<image_sha>/<profile>.qcow2`
43
+ file with the image's base blob as `backing_file`, served over NBD by
44
+ `qemu-nbd`; the target mounts it read-write and system changes (apt
45
+ installs, kernel modules, hardware-specific config) survive reboots.
46
+ Overlays are keyed by `(mac, image_sha, profile)`, so different
47
+ machines have fully independent files under the same profile name and
48
+ rebinding to a different image leaves the old image's overlays on
49
+ disk for a later resume. A Reset button on the machine detail page
50
+ tears down `qemu-nbd`, unlinks the qcow2, and lets the next boot
51
+ lazy-create a fresh overlay from the base. New `overlays` table on
52
+ state.db (idempotent additive migration), new `overlay.created`,
53
+ `overlay.reset`, `overlay.booted` events. Concurrency is by
54
+ construction (a MAC boots one target at a time), so there is no
55
+ holder tracking or force-reclaim.
56
+
57
+ **Slick Inventory card viz.** The machine detail Inventory card was
58
+ rewritten to consume a normalised view of the stored `lshw -json`
59
+ blob. CPU renders one stat-block per socket with the model as
60
+ headline, an architecture badge, and Bootstrap `display-6` big-type
61
+ numbers for cores over threads plus max clock. Memory shows a total
62
+ headline plus a per-DIMM slot-fill row (filled blocks for populated
63
+ SMBIOS type-17 bank records, outlined blocks for empty slots,
64
+ hover-title tooltip per slot showing size / speed / type), with a
65
+ total-only fallback for firmwares that skimp on bank records. The
66
+ extractor lives in `pixie.web._inventory.normalise_inventory` and
67
+ runs at render time, so a wire-format change or a new lshw quirk
68
+ touches one function. Two new Jinja filters: `humanize_bytes` and
69
+ `humanize_hz`.
70
+
71
+ ### Fixed
72
+
73
+ **Truncated `.img.gz` fetches now fail at the download stage.**
74
+ Operators saw "decompress img.gz failed: Compressed file ended before
75
+ the end-of-stream marker was reached" on the catalog page when a
76
+ ghcr download was interrupted mid-transfer. Root cause: the fetch
77
+ pipeline's byte-copy loop treated `resp.read()` returning an empty
78
+ chunk as "done" but urllib does not raise when a peer closes the
79
+ connection early, so a short body was accepted and the gzip trailer
80
+ check surfaced the truncation several minutes downstream of the
81
+ actual failure. `_stream_to_tmpfile` now cross-checks bytes-written
82
+ against `Content-Length` and raises a clear `download truncated for
83
+ <url>: got X of Y bytes` `FetchError` at the point of cause. A
84
+ related cleanup leak (the `finally` block only unlinked `.inflight`
85
+ files for `tar.gz`, so failing `img.gz` fetches left multi-GB
86
+ orphans in `data/tmp/` forever) is fixed as part of the same change.
87
+
19
88
  **Settings pane with per-operator display picks.** New top-nav
20
89
  pill `/ui/settings` with two knobs: display timezone (IANA zone
21
90
  name) and datetime format (strftime pattern). Both resolve override
@@ -44,6 +44,9 @@ LABEL org.opencontainers.image.title="pixie" \
44
44
  # /usr/lib/ipxe/, which we copy into pixie's
45
45
  # TFTP root so the daemon can serve them.
46
46
  # qemu-utils: qemu-img info + qemu-nbd for qcow2 handling.
47
+ # fdisk: sfdisk for pixie._partition partition-offset lookup
48
+ # on persistent-overlay renders. On Ubuntu 26.04
49
+ # sfdisk moved out of util-linux into this package.
47
50
  # ca-certificates: HTTPS fetch of ORAS + release assets.
48
51
  #
49
52
  # Verified against ubuntu:26.04's package list in CI on every build;
@@ -64,6 +67,7 @@ RUN apt-get update \
64
67
  tftpd-hpa \
65
68
  ipxe \
66
69
  qemu-utils \
70
+ fdisk \
67
71
  && rm -rf /var/lib/apt/lists/*
68
72
 
69
73
  # App under /app with editable-style install from the source we copy in.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixie-lab
3
- Version: 0.1.0
3
+ Version: 0.2.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
@@ -81,11 +81,13 @@ top nav has Machines, Catalog, Events, and Settings.
81
81
 
82
82
  Everything writable lives under `PIXIE_DATA_DIR` (default
83
83
  `/var/lib/pixie`). `state.db` holds catalog rows, machine rows,
84
- event log, settings, and NBD-export records. `blobs/<sha>/blob`
85
- holds fetched disk images. `artifacts/<sha>/{vmlinuz,initrd,
86
- manifest.json}` holds unpacked netboot bundles. `live-env/` holds
87
- the pixie live env kernel + initrd + squashfs the netboot-pc bake
88
- produced.
84
+ event log, settings, NBD-export records, and persistent-overlay
85
+ records. `blobs/<sha>/blob` holds fetched disk images.
86
+ `artifacts/<sha>/{vmlinuz,initrd,manifest.json}` holds unpacked
87
+ netboot bundles. `overlays/<mac>/<image_sha>/<profile>.qcow2` holds
88
+ per-machine writable overlays for the `nbdboot` boot mode.
89
+ `live-env/` holds the pixie live env kernel + initrd + squashfs the
90
+ netboot-pc bake produced.
89
91
 
90
92
  Both admin password and display timezone / strftime pattern have
91
93
  env-var overrides plus DB overrides via `/ui/settings`; env wins
@@ -53,11 +53,13 @@ top nav has Machines, Catalog, Events, and Settings.
53
53
 
54
54
  Everything writable lives under `PIXIE_DATA_DIR` (default
55
55
  `/var/lib/pixie`). `state.db` holds catalog rows, machine rows,
56
- event log, settings, and NBD-export records. `blobs/<sha>/blob`
57
- holds fetched disk images. `artifacts/<sha>/{vmlinuz,initrd,
58
- manifest.json}` holds unpacked netboot bundles. `live-env/` holds
59
- the pixie live env kernel + initrd + squashfs the netboot-pc bake
60
- produced.
56
+ event log, settings, NBD-export records, and persistent-overlay
57
+ records. `blobs/<sha>/blob` holds fetched disk images.
58
+ `artifacts/<sha>/{vmlinuz,initrd,manifest.json}` holds unpacked
59
+ netboot bundles. `overlays/<mac>/<image_sha>/<profile>.qcow2` holds
60
+ per-machine writable overlays for the `nbdboot` boot mode.
61
+ `live-env/` holds the pixie live env kernel + initrd + squashfs the
62
+ netboot-pc bake produced.
61
63
 
62
64
  Both admin password and display timezone / strftime pattern have
63
65
  env-var overrides plus DB overrides via `/ui/settings`; env wins
@@ -88,3 +88,62 @@ needle = "systemd["
88
88
  [[test.pxe.chain_markers]]
89
89
  key = "debian-userspace"
90
90
  needle = "Debian"
91
+
92
+ # ---- Positive userland proof ---------------------------------------
93
+ #
94
+ # Getty printed the login prompt on the serial console: this is what
95
+ # "operator-usable userland" actually looks like from outside the VM.
96
+ # Getty runs as part of getty.target, which is pulled in by
97
+ # multi-user.target, so a login prompt on serial is unambiguous
98
+ # proof that multi-user.target was reached. Chosen over the
99
+ # "Reached target multi-user.target" systemd banner because Debian
100
+ # 13 boots in ``quiet`` mode by default and does not print that
101
+ # banner on the console -- getty's login prompt is always printed.
102
+ # Catches the "boots to systemd but a hanging unit blocks userspace"
103
+ # shape where the earlier markers (systemd-started, debian-userspace)
104
+ # fire but the target never reaches operator-usable userland.
105
+ [[test.pxe.chain_markers]]
106
+ key = "login-prompt"
107
+ needle = "login:"
108
+
109
+ # ---- Forbidden markers ---------------------------------------------
110
+ #
111
+ # Anything below here MUST NOT appear on the client serial log or
112
+ # in pixie's container logs. The check runs alongside the positive-
113
+ # marker poll and fails the whole test the moment any needle hits.
114
+ # Kept scoped to failure signals that would leave a booted-looking
115
+ # target with a broken userland (booted markers OK, but the console
116
+ # spells out the bad news).
117
+
118
+ # systemd-resolved / networkd starvation with no fallback resolv.conf.
119
+ # The mount hook's mask of systemd-networkd + NetworkManager killed
120
+ # both DHCP daemons on the pivoted rootfs; if the initrd's
121
+ # /etc/resolv.conf write did not stick, apt / curl on the target log
122
+ # this literal string on serial. Nosi 2026.W30's mount hook unlinks
123
+ # the /etc/resolv.conf symlink before writing so glibc's resolver
124
+ # reads the file directly -- if a regression puts the symlink back,
125
+ # this marker fires.
126
+ [[test.pxe.forbidden_markers]]
127
+ key = "dns-broken"
128
+ needle = "Temporary failure resolving"
129
+
130
+ # sshd itself failed to start (its unit went into failed state during
131
+ # multi-user.target activation). Distinct from "sshd-started not
132
+ # seen" so the operator gets a clear "start failure" vs "start never
133
+ # attempted" diagnosis in the CI log.
134
+ [[test.pxe.forbidden_markers]]
135
+ key = "sshd-failed"
136
+ needle = "Failed to start ssh.service"
137
+
138
+ # Dracut emergency shell: the mount hook died with _pixie_die and
139
+ # systemd-emergency published the emergency-shell prompt. Every
140
+ # persist-branch regression this session ended here.
141
+ [[test.pxe.forbidden_markers]]
142
+ key = "dracut-emergency"
143
+ needle = "dracut-emergency"
144
+
145
+ # Post-pivot emergency mode (something failed in userspace's own
146
+ # init and systemd fell back to emergency.target).
147
+ [[test.pxe.forbidden_markers]]
148
+ key = "emergency-mode"
149
+ needle = "Emergency mode"
@@ -243,6 +243,7 @@ def main(args, cijoe) -> int:
243
243
  client = _start_client_vm(workspace, cfg, client_log, firmware)
244
244
 
245
245
  markers = _build_markers(cfg)
246
+ forbidden = [(e["key"], e["needle"]) for e in cfg.get("forbidden_markers", [])]
246
247
  # ``mode=flash`` runs the full real-image pipeline (oras
247
248
  # pull + img.gz decompress + dd of ~10 GiB) inside the live
248
249
  # env; ``mode=nbdboot`` also pulls the real nosi image but
@@ -252,7 +253,20 @@ def main(args, cijoe) -> int:
252
253
  # blob store, similar order of magnitude. Everything else
253
254
  # finishes well within the 5 min short-mode budget.
254
255
  chain_deadline = CHAIN_TIMEOUT_FLASH if mode in ("flash", "nbdboot") else CHAIN_TIMEOUT
255
- seen = _wait_for_chain_markers(client_log, markers, chain_deadline)
256
+ try:
257
+ seen = _wait_for_chain_markers(client_log, markers, chain_deadline, forbidden)
258
+ except ForbiddenMarkerSeen as exc:
259
+ log.error(
260
+ f"PXE chain hit forbidden marker {exc.key!r} ({exc.needle!r}); "
261
+ "a known-bad userland signal appeared on the client serial log "
262
+ "or in pixie's container logs. Dumping context."
263
+ )
264
+ qemu_log = client_log.with_suffix(".qemu.log")
265
+ _dump_tail(qemu_log, 60)
266
+ _dump_tail(workspace / "dnsmasq.log", 60)
267
+ _dump_tail(client_log, 200)
268
+ _dump_container_logs()
269
+ return errno.EPROTO
256
270
  missing = [k for k, ok in seen.items() if not ok]
257
271
  if missing:
258
272
  log.error(f"PXE chain incomplete; missing markers: {', '.join(missing)}")
@@ -620,30 +634,52 @@ def _build_markers(cfg):
620
634
  return out
621
635
 
622
636
 
623
- def _wait_for_chain_markers(log_path: Path, markers, timeout: int):
637
+ def _wait_for_chain_markers(log_path: Path, markers, timeout: int, forbidden=None):
638
+ """Poll the client serial log + pixie's container logs for the
639
+ positive markers, checking the same buffers for any ``forbidden``
640
+ substrings each round. A forbidden hit shortcuts the wait with
641
+ ``ForbiddenMarkerSeen`` so the caller fails the test loud instead
642
+ of timing out looking for a positive marker that would never
643
+ arrive (the DNS / sshd / emergency-shell regressions come with
644
+ highly recognisable serial output and belong in this list rather
645
+ than in the reactive "check what's missing" branch)."""
624
646
  seen = {key: False for key, _ in markers}
647
+ forbidden = forbidden or []
625
648
  deadline = time.monotonic() + timeout
626
649
  while time.monotonic() < deadline and not all(seen.values()):
627
- # Check the serial log for iPXE-side markers.
650
+ body = ""
628
651
  if log_path.exists():
629
652
  body = log_path.read_text(encoding="utf-8", errors="replace")
630
653
  for key, needle in markers:
631
654
  if not seen[key] and needle in body:
632
655
  log.info(f" + {key}: matched {needle!r}")
633
656
  seen[key] = True
634
- # Also mirror-check container logs so a server-side hit counts
635
- # even when the client's console doesn't spell the fetch out.
636
657
  cont = _container_log_snapshot()
637
658
  for key, needle in markers:
638
659
  if not seen[key] and needle in cont:
639
660
  log.info(f" + {key}: matched {needle!r} in container logs")
640
661
  seen[key] = True
662
+ for key, needle in forbidden:
663
+ if needle in body or needle in cont:
664
+ raise ForbiddenMarkerSeen(key, needle)
641
665
  if all(seen.values()):
642
666
  break
643
667
  time.sleep(2)
644
668
  return seen
645
669
 
646
670
 
671
+ class ForbiddenMarkerSeen(RuntimeError):
672
+ """A ``[[test.pxe.forbidden_markers]]`` entry was observed on the
673
+ client serial log or in pixie's container logs. Raised by the
674
+ marker-polling loop so the caller can log the exact key + needle
675
+ that fired and fail with a distinct error code."""
676
+
677
+ def __init__(self, key: str, needle: str) -> None:
678
+ super().__init__(f"forbidden marker {key!r} matched {needle!r}")
679
+ self.key = key
680
+ self.needle = needle
681
+
682
+
647
683
  def _container_log_snapshot() -> str:
648
684
  res = subprocess.run(
649
685
  ["podman", "logs", "--tail", "500", CONTAINER_NAME],
@@ -49,11 +49,50 @@ known-clean image each cycle.
49
49
  ### `nbdboot`
50
50
 
51
51
  The target boots the image's OWN kernel (extracted from the sibling
52
- netboot bundle) and mounts the image over NBD. Root is an
52
+ netboot bundle) and mounts the image over NBD. By default, root is an
53
53
  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.
80
+
81
+ **Kexec into a locally-installed kernel.** The netboot bundle owns
82
+ the kernel and initrd pixie serves. Installing `linux-image-*` on the
83
+ target's persistent overlay writes files to `/boot` but the next
84
+ power-cycle refetches pixie's kernel and those files sit unused.
85
+ `kexec` bridges that gap: the netboot kernel comes up, then the
86
+ operator runs
87
+ `kexec -l /boot/vmlinuz-<v> --initrd=/boot/initrd.img-<v>
88
+ --reuse-cmdline && systemctl kexec` to switch to the local kernel
89
+ without going through firmware. Every netboot-shipping nosi variant
90
+ now bakes `kexec-tools` in; see nosi's [Custom kernel under netboot
91
+ (kexec)](https://safl.github.io/nosi/kexec.html) for the full
92
+ workflow. Recovery from a bad kernel is a power-cycle back to
93
+ pixie's kernel; nothing kexec's automatically, so a broken install
94
+ never becomes a boot loop.
95
+
57
96
  ## Prerequisites at a glance
58
97
 
59
98
  | Mode | Needs image | Needs inventory | Needs target disk |
@@ -1,6 +1,6 @@
1
1
  # Deployment
2
2
 
3
- Pixie ships as a single podman-compose service. The `pixie-deploy`
3
+ Pixie ships as a single podman-compose service. The `pixie-lab`
4
4
  CLI writes the compose file + envvars into a directory of the
5
5
  operator's choosing.
6
6
 
@@ -17,10 +17,82 @@ The recommended layout on the deploy host:
17
17
  live-env/ <- staged pixie live env (vmlinuz + initrd + squashfs)
18
18
  ```
19
19
 
20
- `compose.yml` is generated by `pixie-deploy init` and shouldn't need
20
+ `compose.yml` is generated by `pixie-lab init` and shouldn't need
21
21
  hand-edits for a standard deploy. `envvars` carries the site-specific
22
22
  values.
23
23
 
24
+ ## Lifecycle
25
+
26
+ The `pixie-lab` CLI drives a deploy directory. Install it with
27
+ `uv tool install pixie-lab` (or `pipx install pixie-lab`); each
28
+ subcommand takes an optional path, defaulting to the current
29
+ directory.
30
+
31
+ ### First deploy
32
+
33
+ ```
34
+ pixie-lab deploy /opt/pixie
35
+ ```
36
+
37
+ `deploy` writes `compose.yml` + `envvars` (auto-filling a random admin
38
+ password and the detected host address), brings the stack up with
39
+ `podman compose`, and waits for `/healthz`. It refuses to overwrite an
40
+ existing `compose.yml` / `envvars`, so it is a fresh-directory command;
41
+ pass `--admin-password` / `--host-addr` to pin those values, or edit
42
+ `envvars` and re-run `podman compose --env-file envvars up -d`.
43
+
44
+ ### Restart vs. teardown
45
+
46
+ A restart is safe for running targets: pixie respawns every nbdkit +
47
+ qemu-nbd export from `state.db` on startup, and the nbdboot initrd
48
+ connects with `nbd-client -persist`, so a target whose root is on
49
+ pixie's NBD reconnects across a quick bounce.
50
+
51
+ ```
52
+ podman restart pixie
53
+ ```
54
+
55
+ Taking the container fully down drops those exports. Any target
56
+ currently booted `nbdboot` off pixie loses its backing device and
57
+ hangs - power such targets off before a teardown, or reboot them after.
58
+
59
+ ### Purge and start from scratch
60
+
61
+ ```
62
+ pixie-lab purge /opt/pixie --data
63
+ ```
64
+
65
+ `purge` stops and removes the container. It prints what it will do and
66
+ asks for a `y/N` confirmation first; pass `-y` / `--yes` for scripted
67
+ runs (on a non-TTY it refuses without `--yes` rather than fire
68
+ unattended). Flags choose how far the wipe goes:
69
+
70
+ - `--data` - also delete `data/`: `state.db` (catalog, machines,
71
+ events, exports, overlays, settings), every fetched blob and unpacked
72
+ artifact, all per-machine overlays, and the staged `live-env/`.
73
+ `data/` is a bind mount (`./data:/var/lib/pixie`), not a named
74
+ volume, so purge unlinks it directly - `podman compose down -v`
75
+ alone would not touch it.
76
+ - `--images` - also remove the pixie container image.
77
+ - `--all` - also delete the deploy directory (`compose.yml`,
78
+ `envvars`); implies `--data`.
79
+
80
+ With no flag, purge only stops the container and leaves state in place.
81
+ Bring a config-preserved deploy back up with `podman compose --env-file
82
+ envvars up -d`. For a from-nothing redeploy - regenerated compose +
83
+ envvars + a fresh admin password - purge with `--all`, then
84
+ `pixie-lab deploy /opt/pixie` on the now-empty directory.
85
+
86
+ ### Upgrade the image
87
+
88
+ The service runs the `pixie:local` image. Rebuild and recreate; state
89
+ in `data/` survives untouched:
90
+
91
+ ```
92
+ podman build -t pixie:local -f Containerfile .
93
+ cd /opt/pixie && podman compose --env-file envvars up -d
94
+ ```
95
+
24
96
  ## Environment variables
25
97
 
26
98
  Pixie reads these at container startup. The compose file passes the
@@ -51,7 +123,9 @@ workarounds; see [](hardware-quirks.md).
51
123
 
52
124
  **`PIXIE_DATA_DIR`** (default: `/var/lib/pixie`) - state root. Holds
53
125
  `state.db`, `blobs/` (fetched image bytes), `artifacts/` (extracted
54
- netboot bundles), `live-env/` (staged pixie live env).
126
+ netboot bundles), `overlays/<mac>/<image_sha>/<profile>.qcow2`
127
+ (per-machine writable overlays for nbdboot; see [](boot-modes.md)),
128
+ `live-env/` (staged pixie live env).
55
129
 
56
130
  **`PIXIE_LIVE_ENV_DIR`** (default: `$PIXIE_DATA_DIR/live-env`) - the
57
131
  `pixie-*` boot modes degrade to an unavailable plan when this dir is
@@ -69,6 +69,38 @@ workaround kicks in the kernel logs:
69
69
 
70
70
  ...and the live-boot initrd fetches the squashfs normally.
71
71
 
72
+ ## Target firmware prerequisites (not cmdline)
73
+
74
+ Two quirks are about the target's firmware / BMC rather than a kernel
75
+ token, but they produce the same "nbdboot doesn't work" report.
76
+
77
+ ### UEFI network boot must be enabled in BIOS
78
+
79
+ nbdboot only happens if the target's UEFI actually PXE-boots. If the
80
+ board's network stack / PXE option-ROM is disabled in BIOS setup, or
81
+ the network device isn't in the boot order, the machine silently boots
82
+ its local disk instead - and, because that local install answers SSH,
83
+ it's easy to mistake for a working nbdboot. Some BMCs make this worse:
84
+ a one-shot **or** persistent "boot from PXE" override set over IPMI /
85
+ Redfish is accepted by the BMC (`bootparam get 5` shows `Force PXE`)
86
+ yet **ignored by the BIOS**, and the target never even appears in
87
+ pixie's access log. Seen on a Supermicro board whose BIOS network boot
88
+ had been turned off. Fix in BIOS setup directly: enable the UEFI
89
+ network stack + PXE, and put the NIC ahead of the local disk in the
90
+ boot order. Confirm success from pixie's side (a `GET /pxe/<mac>` in
91
+ the log, then `online.started … ramboot.up` status events), not from
92
+ SSH being open.
93
+
94
+ ### Don't trust IPMI SOL to prove a boot
95
+
96
+ Some BMCs (e.g. the GIGABYTE MC12-LE0's Aspeed) do **not** mirror the
97
+ host serial console to IPMI SOL, so SOL stays blank even though the
98
+ target booted fine and `serial-getty@ttyS1` is running. Absence of a
99
+ login prompt on SOL is not proof of a failed boot on those boards.
100
+ Judge nbdboot by ground truth instead: root is `/dev/nbd0` (not the
101
+ SATA/NVMe disk), a live NBD socket to pixie's export port, and the
102
+ image's hostname rather than the local install's.
103
+
72
104
  ## Adding a row
73
105
 
74
106
  If you hit new hardware, keep the row short. Reader wants:
@@ -12,7 +12,7 @@ pipx install pixie-lab
12
12
  `pipx` is preferred so pixie's runtime deps stay isolated from the
13
13
  system Python. `uv tool install pixie-lab` works too.
14
14
 
15
- The `pixie-lab` distribution ships two console-scripts: `pixie-deploy`
15
+ The `pixie-lab` distribution ships two console-scripts: `pixie-lab`
16
16
  generates the container deployment bundle, and `pixie` is the
17
17
  operator TUI baked into the pixie live env (targets run it after
18
18
  booting the live env; operators don't call it directly on their
@@ -22,11 +22,11 @@ workstation).
22
22
 
23
23
  ```
24
24
  mkdir /opt/pixie && cd /opt/pixie
25
- pixie-deploy init
25
+ pixie-lab init
26
26
  ${EDITOR:-vi} envvars # set PIXIE_HOST_ADDR + PIXIE_ADMIN_PASSWORD
27
27
  ```
28
28
 
29
- `pixie-deploy init` writes `compose.yml`, `envvars` (with placeholder
29
+ `pixie-lab init` writes `compose.yml`, `envvars` (with placeholder
30
30
  values), and `envvars.example` into the current directory. Edit
31
31
  `envvars` to point pixie at your LAN address and set a real admin
32
32
  password.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pixie-lab"
3
- version = "0.1.0"
3
+ version = "0.2.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"