pixie-lab 0.4.2__py3-none-any.whl → 0.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pixie/events/_kinds.py +6 -18
- pixie/pxe/_renderer.py +31 -64
- pixie/web/_settings_store.py +8 -32
- pixie/web/_templates/dashboard.html +19 -41
- pixie/web/_templates/live_env.html +108 -116
- pixie/web/main.py +137 -195
- {pixie_lab-0.4.2.dist-info → pixie_lab-0.4.4.dist-info}/METADATA +14 -7
- {pixie_lab-0.4.2.dist-info → pixie_lab-0.4.4.dist-info}/RECORD +11 -13
- pixie/catalog/_live_env.py +0 -128
- pixie/web/_templates/ipxe/pixie-live-env.j2 +0 -55
- {pixie_lab-0.4.2.dist-info → pixie_lab-0.4.4.dist-info}/WHEEL +0 -0
- {pixie_lab-0.4.2.dist-info → pixie_lab-0.4.4.dist-info}/entry_points.txt +0 -0
- {pixie_lab-0.4.2.dist-info → pixie_lab-0.4.4.dist-info}/licenses/LICENSE +0 -0
pixie/events/_kinds.py
CHANGED
|
@@ -196,23 +196,12 @@ EVENTS_CLEARED = "events.cleared"
|
|
|
196
196
|
right after the wipe so the freshly-empty log still records who reset
|
|
197
197
|
it and when; carries the deleted-row count in ``details``."""
|
|
198
198
|
|
|
199
|
-
# ---------- live
|
|
200
|
-
#
|
|
201
|
-
# The operator "Fetch live-env" action pulls the netboot-pc bake into
|
|
202
|
-
# PIXIE_LIVE_ENV_DIR. No subject (``subject_kind`` = ""): the live env
|
|
203
|
-
# is per-pixie, not per-resource. Dotted ``.done`` / ``.failed`` names
|
|
204
|
-
# mirror the catalog-fetch kinds so ``.failed`` auto-classifies as an
|
|
205
|
-
# error via the ERROR_KINDS ``LIKE '%.failed'`` probe.
|
|
206
|
-
|
|
207
|
-
LIVE_ENV_FETCH_DONE = "live_env.fetch.done"
|
|
208
|
-
"""``POST /ui/live-env/fetch`` downloaded + staged vmlinuz + initrd +
|
|
209
|
-
live.squashfs into PIXIE_LIVE_ENV_DIR. Carries the src + content sha in
|
|
210
|
-
``details``."""
|
|
199
|
+
# ---------- live env ------------------------------------------------
|
|
211
200
|
|
|
212
|
-
|
|
213
|
-
"""The
|
|
214
|
-
|
|
215
|
-
``details``."""
|
|
201
|
+
LIVE_ENV_IMAGE_SELECTED = "live_env.image.selected"
|
|
202
|
+
"""The one-click "Set up live env" action fetched the pixie-live-env
|
|
203
|
+
image + its bundle and selected the image (set ``live_env.image_sha``).
|
|
204
|
+
Carries the selected ``image_sha`` in ``details``."""
|
|
216
205
|
|
|
217
206
|
|
|
218
207
|
# The canonical closed set. Every kind above is registered here; the
|
|
@@ -248,8 +237,7 @@ KNOWN_EVENT_KINDS: frozenset[str] = frozenset(
|
|
|
248
237
|
AUTH_LOGIN_SUCCEEDED,
|
|
249
238
|
AUTH_LOGIN_FAILED,
|
|
250
239
|
EVENTS_CLEARED,
|
|
251
|
-
|
|
252
|
-
LIVE_ENV_FETCH_FAILED,
|
|
240
|
+
LIVE_ENV_IMAGE_SELECTED,
|
|
253
241
|
}
|
|
254
242
|
)
|
|
255
243
|
|
pixie/pxe/_renderer.py
CHANGED
|
@@ -91,12 +91,12 @@ class RenderContext:
|
|
|
91
91
|
# no-op is a legal value.
|
|
92
92
|
extra_cmdline: str = ""
|
|
93
93
|
# Disk-image content sha the live-env boot modes boot over
|
|
94
|
-
# ephemeral nbdboot
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
94
|
+
# ephemeral nbdboot. Operator-set via PIXIE_LIVE_ENV_IMAGE_SHA /
|
|
95
|
+
# the Live env page. Empty means no live-env image is configured,
|
|
96
|
+
# so LIVE_ENV_MODES machines degrade to the ``unavailable`` plan;
|
|
97
|
+
# when set, ``render`` streams this image over NBD for every
|
|
98
|
+
# LIVE_ENV_MODES machine (the machine's own bind still drives what
|
|
99
|
+
# GET /pxe/<mac>/plan returns, so the on-target CLI dispatches
|
|
100
100
|
# inventory / interactive / flash correctly).
|
|
101
101
|
live_env_image_sha: str = ""
|
|
102
102
|
|
|
@@ -113,7 +113,6 @@ class PlanRenderer:
|
|
|
113
113
|
overlays: OverlaysStore,
|
|
114
114
|
nbd: NbdServer,
|
|
115
115
|
overlays_dir: Path,
|
|
116
|
-
live_env_dir: Path | None = None,
|
|
117
116
|
events: Any = None,
|
|
118
117
|
) -> None:
|
|
119
118
|
self._catalog = catalog
|
|
@@ -128,29 +127,12 @@ class PlanRenderer:
|
|
|
128
127
|
# booted) land alongside the rest of pixie's audit trail. None
|
|
129
128
|
# for unit tests that don't wire an events sink.
|
|
130
129
|
self._events = events
|
|
131
|
-
# Where the netboot-pc bake's vmlinuz + initrd + squashfs are
|
|
132
|
-
# staged on disk. When set + the three files exist, the
|
|
133
|
-
# ``pixie-*`` boot modes chain into the live env; otherwise
|
|
134
|
-
# they fall back to the ``unavailable`` plan so a bound
|
|
135
|
-
# target does not silently boot nothing.
|
|
136
|
-
self._live_env_dir = live_env_dir
|
|
137
130
|
self._env = Environment(
|
|
138
131
|
loader=FileSystemLoader(str(_TEMPLATES_DIR)),
|
|
139
132
|
autoescape=select_autoescape(disabled_extensions=("j2",)),
|
|
140
133
|
keep_trailing_newline=True,
|
|
141
134
|
)
|
|
142
135
|
|
|
143
|
-
def _live_env_ready(self) -> bool:
|
|
144
|
-
"""True iff the netboot-pc artifacts are staged on disk under
|
|
145
|
-
``self._live_env_dir``. Called per-render so an operator
|
|
146
|
-
dropping the files in without a pixie restart takes effect
|
|
147
|
-
on the next PXE hit."""
|
|
148
|
-
if self._live_env_dir is None:
|
|
149
|
-
return False
|
|
150
|
-
return all(
|
|
151
|
-
(self._live_env_dir / name).is_file() for name in ("vmlinuz", "initrd", "live.squashfs")
|
|
152
|
-
)
|
|
153
|
-
|
|
154
136
|
def render(self, machine: Machine, ctx: RenderContext) -> str:
|
|
155
137
|
mode = machine.boot_mode
|
|
156
138
|
# Per-machine ``extra_cmdline`` fully overrides the deploy-wide
|
|
@@ -341,52 +323,37 @@ class PlanRenderer:
|
|
|
341
323
|
) -> str:
|
|
342
324
|
"""Render a plan for one of the ``LIVE_ENV_MODES``.
|
|
343
325
|
|
|
344
|
-
|
|
345
|
-
(PIXIE_LIVE_ENV_IMAGE_SHA / the Live
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
(``pixie-live-env.j2``), or ``unavailable`` when the
|
|
357
|
-
netboot-pc bake artifacts have not been staged.
|
|
358
|
-
|
|
359
|
-
Either way the machine's OWN ``boot_mode`` still drives what
|
|
326
|
+
The live env boots the disk image named by
|
|
327
|
+
``ctx.live_env_image_sha`` (PIXIE_LIVE_ENV_IMAGE_SHA / the Live
|
|
328
|
+
env page) over EPHEMERAL nbdboot, the same code path the
|
|
329
|
+
``nbdboot`` mode uses -- proven on hardware to bring the NIC up
|
|
330
|
+
via dracut where the retired Debian live-boot squashfs hung in
|
|
331
|
+
the initramfs on some boards. If no image is configured, or the
|
|
332
|
+
configured image / its netboot bundle isn't fetched, degrade to
|
|
333
|
+
``unavailable`` (matching how ``nbdboot`` degrades) rather than
|
|
334
|
+
booting nothing -- a live-env mode with no bootable image is an
|
|
335
|
+
operator setup gap worth surfacing.
|
|
336
|
+
|
|
337
|
+
The machine's OWN ``boot_mode`` still drives what
|
|
360
338
|
``GET /pxe/<mac>/plan`` returns, so the on-target pixie CLI
|
|
361
|
-
dispatches inventory / interactive / flash
|
|
339
|
+
dispatches inventory / interactive / flash accordingly."""
|
|
362
340
|
live_env_sha = ctx.live_env_image_sha
|
|
363
|
-
if live_env_sha:
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
)
|
|
370
|
-
bundle_entry, blob = resolved
|
|
371
|
-
return self._render_ephemeral_nbdboot(
|
|
372
|
-
machine, ctx, live_env_sha, bundle_entry, blob, extra_cmdline
|
|
341
|
+
if not live_env_sha:
|
|
342
|
+
return self._unavailable(
|
|
343
|
+
machine,
|
|
344
|
+
f"boot_mode={mode!r} needs a live-env image; set "
|
|
345
|
+
"PIXIE_LIVE_ENV_IMAGE_SHA (or the Live env page) to a "
|
|
346
|
+
"fetched image's content sha",
|
|
373
347
|
)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
# deploy yet; degrade to the readable unavailable plan so a
|
|
377
|
-
# bound target lands on a legible screen instead of a
|
|
378
|
-
# bty-media initrd.
|
|
348
|
+
resolved = self._resolve_bundle_and_blob(live_env_sha)
|
|
349
|
+
if isinstance(resolved, str):
|
|
379
350
|
return self._unavailable(
|
|
380
351
|
machine,
|
|
381
|
-
f"
|
|
382
|
-
f"stage vmlinuz+initrd+squashfs under $PIXIE_LIVE_ENV_DIR",
|
|
352
|
+
f"live-env image {live_env_sha[:12]} not bootable: {resolved}",
|
|
383
353
|
)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
host=ctx.host,
|
|
388
|
-
port=ctx.port,
|
|
389
|
-
extra_cmdline=extra_cmdline,
|
|
354
|
+
bundle_entry, blob = resolved
|
|
355
|
+
return self._render_ephemeral_nbdboot(
|
|
356
|
+
machine, ctx, live_env_sha, bundle_entry, blob, extra_cmdline
|
|
390
357
|
)
|
|
391
358
|
|
|
392
359
|
def _unavailable(self, machine: Machine, reason: str) -> str:
|
pixie/web/_settings_store.py
CHANGED
|
@@ -74,29 +74,16 @@ ENV_LIVE_ENV_EXTRA_CMDLINE = "PIXIE_LIVE_ENV_EXTRA_CMDLINE"
|
|
|
74
74
|
# it never reaches a kernel cmdline.
|
|
75
75
|
_UNEXPANDED_ENV_PLACEHOLDER = re.compile(r"^\$\{[A-Za-z_][A-Za-z0-9_]*(:[-+?=]?[^}]*)?\}$")
|
|
76
76
|
|
|
77
|
-
# Where the operator "Fetch live-env" action pulls the netboot-pc bake
|
|
78
|
-
# from: a single tarball ``src`` (``https://`` or ``oras://``, the same
|
|
79
|
-
# schemes the catalog fetch speaks) that unpacks to vmlinuz + initrd +
|
|
80
|
-
# live.squashfs. Defaults to the pixie GitHub release's stable-named
|
|
81
|
-
# asset via ``/releases/latest/download/`` so a plain deploy can pull
|
|
82
|
-
# the live env with no config; override for an air-gapped mirror.
|
|
83
|
-
KEY_LIVE_ENV_SRC = "live_env.src"
|
|
84
|
-
ENV_LIVE_ENV_SRC = "PIXIE_LIVE_ENV_SRC"
|
|
85
|
-
DEFAULT_LIVE_ENV_SRC = (
|
|
86
|
-
"https://github.com/safl/pixie/releases/latest/download/pixie-live-env-x86_64.tar.gz"
|
|
87
|
-
)
|
|
88
|
-
|
|
89
77
|
# Disk-image content sha the live-env boot modes
|
|
90
78
|
# (pixie-inventory / -tui / -flash-once / -flash-always) boot over
|
|
91
|
-
# EPHEMERAL nbdboot
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# sending every live-env boot into the unavailable plan.
|
|
79
|
+
# EPHEMERAL nbdboot. When set (and the named image + its netboot bundle
|
|
80
|
+
# are fetched) the renderer streams this image over NBD -- proven on
|
|
81
|
+
# hardware to bring the NIC up via dracut where the retired squashfs
|
|
82
|
+
# live-boot hung in the initramfs on some boards. Empty (the default)
|
|
83
|
+
# means those modes degrade to the unavailable plan. Resolution: DB
|
|
84
|
+
# override -> env -> empty. A value that is not 64 lowercase hex chars
|
|
85
|
+
# is treated as unset so a fat-fingered setting degrades to unavailable
|
|
86
|
+
# cleanly.
|
|
100
87
|
KEY_LIVE_ENV_IMAGE_SHA = "live_env.image_sha"
|
|
101
88
|
ENV_LIVE_ENV_IMAGE_SHA = "PIXIE_LIVE_ENV_IMAGE_SHA"
|
|
102
89
|
|
|
@@ -216,17 +203,6 @@ class SettingsStore:
|
|
|
216
203
|
return env
|
|
217
204
|
return ""
|
|
218
205
|
|
|
219
|
-
def resolve_live_env_src(self) -> str:
|
|
220
|
-
"""Effective live-env fetch src: DB override -> env -> default
|
|
221
|
-
(the pixie GitHub release asset). Never empty; the caller feeds
|
|
222
|
-
it straight to the live-env fetch, which raises on a bad
|
|
223
|
-
scheme."""
|
|
224
|
-
return (
|
|
225
|
-
self.get(KEY_LIVE_ENV_SRC)
|
|
226
|
-
or (os.environ.get(ENV_LIVE_ENV_SRC) or "").strip()
|
|
227
|
-
or DEFAULT_LIVE_ENV_SRC
|
|
228
|
-
)
|
|
229
|
-
|
|
230
206
|
def resolve_live_env_image_sha(self) -> str:
|
|
231
207
|
"""Effective live-env disk-image content sha: DB override -> env
|
|
232
208
|
-> empty.
|
|
@@ -126,67 +126,45 @@
|
|
|
126
126
|
</div>
|
|
127
127
|
</div>
|
|
128
128
|
|
|
129
|
-
{# Live-env
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
{% set lfs = stats.live_env_fetch_state or {} %}
|
|
129
|
+
{# Live-env readiness (OUTPUT ONLY). The dashboard reports whether the
|
|
130
|
+
pixie-* boot modes have a bootable image selected; the image + cmdline
|
|
131
|
+
config live on the dedicated /ui/live-env pane. #}
|
|
133
132
|
<div class="card shadow-sm mb-4">
|
|
134
133
|
<div class="card-header bg-light d-flex align-items-center gap-2">
|
|
135
|
-
<h2 class="h5 mb-0"><i class="bi bi-boombox me-2"></i>Live
|
|
134
|
+
<h2 class="h5 mb-0"><i class="bi bi-boombox me-2"></i>Live env</h2>
|
|
136
135
|
<div class="ms-auto d-flex align-items-center gap-2">
|
|
137
|
-
{% if
|
|
138
|
-
<span class="badge text-bg-info"><span class="spinner-border spinner-border-sm me-1" role="status"></span>fetching</span>
|
|
139
|
-
{% elif stats.live_env_ready %}
|
|
136
|
+
{% if stats.live_env_ready %}
|
|
140
137
|
<span class="badge text-bg-success"><i class="bi bi-check-circle me-1"></i>ready</span>
|
|
141
138
|
{% else %}
|
|
142
|
-
<span class="badge text-bg-warning"><i class="bi bi-exclamation-triangle me-1"></i>not
|
|
139
|
+
<span class="badge text-bg-warning"><i class="bi bi-exclamation-triangle me-1"></i>not ready</span>
|
|
143
140
|
{% endif %}
|
|
144
141
|
<a href="/ui/live-env" class="btn btn-sm btn-outline-primary text-nowrap"
|
|
145
|
-
title="
|
|
142
|
+
title="Select + configure the live env">
|
|
146
143
|
<i class="bi bi-boombox me-1"></i>Manage
|
|
147
144
|
</a>
|
|
148
145
|
</div>
|
|
149
146
|
</div>
|
|
150
147
|
<div class="card-body">
|
|
151
148
|
{% if stats.live_env_ready %}
|
|
152
|
-
<p class="small mb-
|
|
153
|
-
|
|
149
|
+
<p class="small mb-0">
|
|
150
|
+
Image <code>{{ stats.live_env_image_sha[:12] }}</code> selected and fetched;
|
|
154
151
|
<code>pixie-tui</code> / <code>pixie-inventory</code> /
|
|
155
|
-
<code>pixie-flash-*</code>
|
|
152
|
+
<code>pixie-flash-*</code> boot it over ephemeral nbdboot.
|
|
156
153
|
</p>
|
|
157
|
-
{%
|
|
158
|
-
<p class="small text-warning mb-
|
|
159
|
-
Live-env
|
|
160
|
-
Every <code>pixie-*</code> boot_mode falls back to the "unavailable"
|
|
161
|
-
plan until
|
|
154
|
+
{% elif stats.live_env_image_sha %}
|
|
155
|
+
<p class="small text-warning mb-0">
|
|
156
|
+
Live-env image <code>{{ stats.live_env_image_sha[:12] }}</code> is selected but
|
|
157
|
+
not fetched. Every <code>pixie-*</code> boot_mode falls back to the "unavailable"
|
|
158
|
+
iPXE plan until the image is fetched. <a href="/ui/catalog">Fetch it →</a>
|
|
162
159
|
</p>
|
|
163
|
-
{%
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
<
|
|
167
|
-
<a href="/ui/live-env">Live env</a>.
|
|
160
|
+
{% else %}
|
|
161
|
+
<p class="small text-warning mb-0">
|
|
162
|
+
No live-env image selected. Every <code>pixie-*</code> boot_mode falls back to the
|
|
163
|
+
"unavailable" iPXE plan until one is set. <a href="/ui/live-env">Select it →</a>
|
|
168
164
|
</p>
|
|
169
165
|
{% endif %}
|
|
170
|
-
<dl class="row small mb-0">
|
|
171
|
-
{% for name, size in stats.live_env_files.items() %}
|
|
172
|
-
<dt class="col-sm-3 font-monospace">{{ name }}</dt>
|
|
173
|
-
<dd class="col-sm-9">
|
|
174
|
-
{% if size is not none %}
|
|
175
|
-
<span class="text-success"><i class="bi bi-check-circle me-1"></i>{{ (size / 1024 / 1024) | round(1) }} MiB</span>
|
|
176
|
-
{% else %}
|
|
177
|
-
<span class="text-warning"><i class="bi bi-x-circle me-1"></i>missing</span>
|
|
178
|
-
{% endif %}
|
|
179
|
-
</dd>
|
|
180
|
-
{% endfor %}
|
|
181
|
-
</dl>
|
|
182
166
|
</div>
|
|
183
167
|
</div>
|
|
184
|
-
{% if lfs.state == 'fetching' %}
|
|
185
|
-
{# The fetch runs on the server's thread pool; poll the page so the
|
|
186
|
-
spinner flips to "ready" (or an error) on its own. A big squashfs
|
|
187
|
-
download takes a while, so 5 s is frequent enough without hammering. #}
|
|
188
|
-
<script>setTimeout(function () { location.reload(); }, 5000);</script>
|
|
189
|
-
{% endif %}
|
|
190
168
|
|
|
191
169
|
<div class="card shadow-sm">
|
|
192
170
|
<div class="card-header bg-light d-flex align-items-center">
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<nav class="navbar subnav-strip" aria-label="Section sub-navigation">
|
|
7
7
|
<div class="container">
|
|
8
8
|
<span class="subnav-jumps d-flex align-items-center flex-wrap">
|
|
9
|
-
<a href="#
|
|
10
|
-
<a href="#
|
|
9
|
+
<a href="#setup">Setup</a>
|
|
10
|
+
<a href="#advanced">Advanced</a>
|
|
11
11
|
<a href="#options">Options</a>
|
|
12
12
|
</span>
|
|
13
13
|
</div>
|
|
@@ -15,104 +15,108 @@
|
|
|
15
15
|
{% endblock %}
|
|
16
16
|
|
|
17
17
|
{% block content %}
|
|
18
|
-
{% set
|
|
19
|
-
{{ page_description('boombox', 'The pixie live env
|
|
18
|
+
{% set ss = live_env_setup_state or {} %}
|
|
19
|
+
{{ page_description('boombox', 'The pixie live env that the pixie-inventory / pixie-tui / pixie-flash-once / pixie-flash-always boot modes boot into: a disk image (the pixie-live-env catalog entry) booted over ephemeral nbdboot. Click Set up live env to fetch it and select it in one step; until then those boot modes render an "unavailable" plan.') }}
|
|
20
20
|
|
|
21
21
|
{% if flash_error %}
|
|
22
22
|
<div class="alert alert-danger py-2">{{ flash_error }}</div>
|
|
23
23
|
{% endif %}
|
|
24
24
|
|
|
25
|
-
{# ----
|
|
26
|
-
<div class="card shadow-sm mb-4" id="
|
|
25
|
+
{# ---- One-click setup -------------------------------------------- #}
|
|
26
|
+
<div class="card shadow-sm mb-4" id="setup">
|
|
27
27
|
<div class="card-header bg-light d-flex align-items-center gap-2">
|
|
28
|
-
<h2 class="h5 mb-0"><i class="bi bi-boombox me-2"></i>
|
|
28
|
+
<h2 class="h5 mb-0"><i class="bi bi-boombox me-2"></i>Live env</h2>
|
|
29
29
|
{% if live_env_ready %}
|
|
30
30
|
<span class="badge text-bg-success ms-auto"><i class="bi bi-check-circle me-1"></i>ready</span>
|
|
31
|
+
{% elif ss.state == 'running' %}
|
|
32
|
+
<span class="badge text-bg-info ms-auto"><span class="spinner-border spinner-border-sm me-1" role="status"></span>setting up</span>
|
|
31
33
|
{% else %}
|
|
32
|
-
<span class="badge text-bg-warning ms-auto"><i class="bi bi-exclamation-triangle me-1"></i>not
|
|
34
|
+
<span class="badge text-bg-warning ms-auto"><i class="bi bi-exclamation-triangle me-1"></i>not set up</span>
|
|
33
35
|
{% endif %}
|
|
34
36
|
</div>
|
|
35
37
|
<div class="card-body">
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<code
|
|
39
|
-
<code>
|
|
38
|
+
{% if live_env_ready %}
|
|
39
|
+
<p class="small mb-3">
|
|
40
|
+
Image <code>{{ live_env_image_sha.effective[:12] }}</code> is fetched and
|
|
41
|
+
selected; the <code>pixie-inventory</code> / <code>pixie-tui</code> /
|
|
42
|
+
<code>pixie-flash-*</code> modes nbdboot it. Nothing else to do.
|
|
43
|
+
</p>
|
|
44
|
+
{% else %}
|
|
45
|
+
<p class="small mb-3">
|
|
46
|
+
One click fetches the <code>pixie-live-env</code> image (~900 MiB) +
|
|
47
|
+
its arch-headless netboot bundle from the catalog, then selects the image.
|
|
48
|
+
No catalog hopping or sha copying. It runs on the server; you can leave
|
|
49
|
+
this page. Re-running when everything is already fetched just re-selects.
|
|
40
50
|
</p>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<span class="
|
|
51
|
+
{% endif %}
|
|
52
|
+
|
|
53
|
+
{# Progress (polled live while a setup runs). #}
|
|
54
|
+
<div id="setup-progress" class="{% if ss.state != 'running' %}d-none{% endif %} mb-3">
|
|
55
|
+
<div class="d-flex align-items-center gap-2 small mb-1">
|
|
56
|
+
<span class="spinner-border spinner-border-sm" role="status"></span>
|
|
57
|
+
<span id="setup-step">Fetching {{ ss.step or 'live env' }}{% if ss.phase %} ({{ ss.phase }}){% endif %}...</span>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="progress" role="progressbar" style="height: 1.1rem;">
|
|
60
|
+
<div id="setup-bar" class="progress-bar progress-bar-striped progress-bar-animated"
|
|
61
|
+
style="width: {% if ss.total_bytes %}{{ (100 * ss.bytes_downloaded / ss.total_bytes) | round(0, 'floor') }}{% else %}100{% endif %}%"></div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div id="setup-error" class="alert alert-danger py-2 small {% if ss.state != 'error' %}d-none{% endif %}">
|
|
65
|
+
<i class="bi bi-exclamation-triangle me-1"></i><span id="setup-error-msg">{{ ss.error }}</span>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<form method="post" action="/ui/live-env/setup" class="m-0">
|
|
69
|
+
<button type="submit" class="btn {% if live_env_ready %}btn-outline-secondary{% else %}btn-primary{% endif %}"
|
|
70
|
+
id="setup-btn" {% if ss.state == 'running' %}disabled{% endif %}>
|
|
71
|
+
{% if ss.state == 'running' %}
|
|
72
|
+
<span class="spinner-border spinner-border-sm me-1" role="status"></span>Setting up...
|
|
47
73
|
{% else %}
|
|
48
|
-
<
|
|
74
|
+
<i class="bi bi-boombox me-1"></i>{% if live_env_ready %}Re-run setup{% else %}Set up live env{% endif %}
|
|
49
75
|
{% endif %}
|
|
50
|
-
</
|
|
51
|
-
|
|
52
|
-
</
|
|
76
|
+
</button>
|
|
77
|
+
<span class="text-muted small ms-2">Fetch + select in one step.</span>
|
|
78
|
+
</form>
|
|
53
79
|
</div>
|
|
54
80
|
</div>
|
|
55
81
|
|
|
56
|
-
{# ----
|
|
57
|
-
<div class="card shadow-sm mb-4" id="
|
|
82
|
+
{# ---- Advanced: select by content sha ---------------------------- #}
|
|
83
|
+
<div class="card shadow-sm mb-4" id="advanced">
|
|
58
84
|
<div class="card-header bg-light fw-semibold">
|
|
59
|
-
<i class="bi bi-
|
|
85
|
+
<i class="bi bi-sliders me-1"></i>Advanced: select by content sha
|
|
60
86
|
</div>
|
|
61
87
|
<div class="card-body">
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
</div>
|
|
75
|
-
{% endif %}
|
|
76
|
-
|
|
77
|
-
{# Editable source, then the fetch button that consumes it. #}
|
|
78
|
-
<form method="post" action="/ui/live-env/src/edit" class="row g-2 align-items-end mb-3">
|
|
79
|
-
<div class="col-12 col-lg-9">
|
|
80
|
-
<label for="live_env_src" class="form-label mb-1">
|
|
81
|
-
Source
|
|
82
|
-
<span class="badge text-bg-{% if live_env_src.override %}primary{% else %}secondary{% endif %} ms-1">
|
|
83
|
-
{% if live_env_src.override %}override{% elif live_env_src.effective != live_env_src.default %}env{% else %}default{% endif %}
|
|
88
|
+
<p class="small text-muted mb-3">
|
|
89
|
+
Manual override of what Set up above does automatically: point the live env
|
|
90
|
+
at a specific fetched image by its <code>content_sha256</code> (from its
|
|
91
|
+
<a href="/ui/catalog">Catalog</a> row). The image plus its netboot bundle
|
|
92
|
+
(via the entry's <code>netboot_src</code>) must both be fetched.
|
|
93
|
+
</p>
|
|
94
|
+
<form method="post" action="/ui/live-env/image/edit" class="row g-3">
|
|
95
|
+
<div class="col-12">
|
|
96
|
+
<label for="live_env_image_sha" class="form-label">
|
|
97
|
+
Image content sha
|
|
98
|
+
<span class="badge text-bg-{% if live_env_image_sha.override %}primary{% else %}secondary{% endif %} ms-1">
|
|
99
|
+
{% if live_env_image_sha.override %}override{% elif live_env_image_sha.effective %}env{% else %}unset{% endif %}
|
|
84
100
|
</span>
|
|
85
101
|
</label>
|
|
86
|
-
<input type="text" id="
|
|
102
|
+
<input type="text" id="live_env_image_sha" name="live_env_image_sha"
|
|
87
103
|
class="form-control font-monospace"
|
|
88
|
-
value="{{
|
|
89
|
-
placeholder="
|
|
90
|
-
|
|
91
|
-
<div class="col-12 col-lg-3">
|
|
92
|
-
<button type="submit" class="btn btn-outline-secondary w-100">
|
|
93
|
-
<i class="bi bi-check2 me-1"></i>Save source
|
|
94
|
-
</button>
|
|
95
|
-
</div>
|
|
96
|
-
<div class="col-12">
|
|
104
|
+
value="{{ live_env_image_sha.override }}"
|
|
105
|
+
placeholder="(unset - live-env modes render the unavailable plan)"
|
|
106
|
+
pattern="[0-9a-fA-F]{64}">
|
|
97
107
|
<div class="form-text">
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{% if live_env_src.updated_at %}<br>Last updated: <code>{{ live_env_src.updated_at | fmt_ts }}</code>.{% endif %}
|
|
108
|
+
Blank falls back to <code>{{ live_env_image_sha.env }}</code> then unset.
|
|
109
|
+
Effective:
|
|
110
|
+
{% if live_env_image_sha.effective %}<code>{{ live_env_image_sha.effective }}</code>{% else %}<em class="text-muted">(none)</em>{% endif %}.
|
|
111
|
+
{% if live_env_image_sha.updated_at %}<br>Last updated: <code>{{ live_env_image_sha.updated_at | fmt_ts }}</code>.{% endif %}
|
|
103
112
|
</div>
|
|
104
113
|
</div>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
{% else %}
|
|
112
|
-
<i class="bi bi-cloud-download me-1"></i>{% if live_env_ready %}Re-fetch live env{% else %}Fetch live env{% endif %}
|
|
113
|
-
{% endif %}
|
|
114
|
-
</button>
|
|
115
|
-
<span class="text-muted small ms-2">Downloads + stages the three files; overwrites what's there.</span>
|
|
114
|
+
<div class="col-12">
|
|
115
|
+
<button type="submit" class="btn btn-outline-secondary"><i class="bi bi-check2 me-1"></i>Save</button>
|
|
116
|
+
<span class="text-muted small ms-2">
|
|
117
|
+
Takes effect on the NEXT <code>GET /pxe/<mac></code>; a target already mid-boot must be power-cycled.
|
|
118
|
+
</span>
|
|
119
|
+
</div>
|
|
116
120
|
</form>
|
|
117
121
|
</div>
|
|
118
122
|
</div>
|
|
@@ -120,7 +124,7 @@
|
|
|
120
124
|
{# ---- Options (config) ------------------------------------------- #}
|
|
121
125
|
<div class="card shadow-sm mb-4" id="options">
|
|
122
126
|
<div class="card-header bg-light fw-semibold">
|
|
123
|
-
<i class="bi bi-
|
|
127
|
+
<i class="bi bi-gear me-1"></i>Options
|
|
124
128
|
</div>
|
|
125
129
|
<div class="card-body">
|
|
126
130
|
<form method="post" action="/ui/live-env/cmdline/edit" class="row g-3">
|
|
@@ -140,7 +144,7 @@
|
|
|
140
144
|
<option value="pci=realloc=on,nocrs">
|
|
141
145
|
</datalist>
|
|
142
146
|
<div class="form-text">
|
|
143
|
-
Tokens appended verbatim to the
|
|
147
|
+
Tokens appended verbatim to the live-env kernel cmdline
|
|
144
148
|
(after <code>pixie.mac=</code> / <code>bty.mac=</code>, so last-token-wins on any
|
|
145
149
|
conflict). Whitespace-separated; single line. Blank falls back to
|
|
146
150
|
<code>{{ live_env_extra_cmdline.env }}</code> then no append. A machine with its own
|
|
@@ -158,48 +162,36 @@
|
|
|
158
162
|
</span>
|
|
159
163
|
</div>
|
|
160
164
|
</form>
|
|
161
|
-
|
|
162
|
-
<hr class="my-4">
|
|
163
|
-
|
|
164
|
-
<form method="post" action="/ui/live-env/image/edit" class="row g-3">
|
|
165
|
-
<div class="col-12">
|
|
166
|
-
<label for="live_env_image_sha" class="form-label">
|
|
167
|
-
Boot live env as disk image (nbdboot)
|
|
168
|
-
<span class="badge text-bg-{% if live_env_image_sha.override %}primary{% else %}secondary{% endif %} ms-1">
|
|
169
|
-
{% if live_env_image_sha.override %}override{% elif live_env_image_sha.effective %}env{% else %}default{% endif %}
|
|
170
|
-
</span>
|
|
171
|
-
</label>
|
|
172
|
-
<input type="text" id="live_env_image_sha" name="live_env_image_sha"
|
|
173
|
-
class="form-control font-monospace"
|
|
174
|
-
value="{{ live_env_image_sha.override }}"
|
|
175
|
-
placeholder="(unset - use the live-boot squashfs above)"
|
|
176
|
-
pattern="[0-9a-fA-F]{64}">
|
|
177
|
-
<div class="form-text">
|
|
178
|
-
A fetched disk-image's <code>content_sha256</code> (64 hex). When set - and that
|
|
179
|
-
image plus its netboot bundle are fetched - the <code>pixie-inventory</code> /
|
|
180
|
-
<code>pixie-tui</code> / <code>pixie-flash-*</code> modes boot it over
|
|
181
|
-
<strong>ephemeral nbdboot</strong> instead of fetching the squashfs. dracut brings
|
|
182
|
-
the NIC up where the live-boot squashfs hangs in the initramfs on some boards; the
|
|
183
|
-
on-target pixie CLI still phones home so inventory / flash work unchanged. Blank
|
|
184
|
-
falls back to <code>{{ live_env_image_sha.env }}</code> then the squashfs path.
|
|
185
|
-
Effective:
|
|
186
|
-
{% if live_env_image_sha.effective %}<code>{{ live_env_image_sha.effective }}</code>{% else %}<em class="text-muted">(none - squashfs)</em>{% endif %}.
|
|
187
|
-
{% if live_env_image_sha.updated_at %}<br>Last updated: <code>{{ live_env_image_sha.updated_at | fmt_ts }}</code>.{% endif %}
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
<div class="col-12">
|
|
191
|
-
<button type="submit" class="btn btn-primary"><i class="bi bi-check2 me-1"></i>Save</button>
|
|
192
|
-
<span class="text-muted small ms-2">
|
|
193
|
-
Takes effect on the NEXT <code>GET /pxe/<mac></code>; a target already mid-boot must be power-cycled.
|
|
194
|
-
</span>
|
|
195
|
-
</div>
|
|
196
|
-
</form>
|
|
197
165
|
</div>
|
|
198
166
|
</div>
|
|
199
167
|
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
{
|
|
168
|
+
{# Poll setup progress while a run is in flight; reload when it lands
|
|
169
|
+
so the ready badge + selected sha update. #}
|
|
170
|
+
<script>
|
|
171
|
+
(function () {
|
|
172
|
+
if ("{{ ss.state }}" !== "running") return;
|
|
173
|
+
var box = document.getElementById("setup-progress");
|
|
174
|
+
var step = document.getElementById("setup-step");
|
|
175
|
+
var bar = document.getElementById("setup-bar");
|
|
176
|
+
function poll() {
|
|
177
|
+
fetch("/ui/live-env/setup-state.json", { headers: { "Accept": "application/json" } })
|
|
178
|
+
.then(function (r) { return r.json(); })
|
|
179
|
+
.then(function (s) {
|
|
180
|
+
if (!s || s.state === "running") {
|
|
181
|
+
if (step) step.textContent = "Fetching " + (s.step || "live env") +
|
|
182
|
+
(s.phase ? " (" + s.phase + ")" : "") + "...";
|
|
183
|
+
if (bar && s.total_bytes) {
|
|
184
|
+
bar.style.width = Math.floor(100 * s.bytes_downloaded / s.total_bytes) + "%";
|
|
185
|
+
}
|
|
186
|
+
setTimeout(poll, 1500);
|
|
187
|
+
} else {
|
|
188
|
+
// done or error -> reload to render the final state.
|
|
189
|
+
location.reload();
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
.catch(function () { setTimeout(poll, 3000); });
|
|
193
|
+
}
|
|
194
|
+
setTimeout(poll, 1500);
|
|
195
|
+
})();
|
|
196
|
+
</script>
|
|
205
197
|
{% endblock %}
|