pixie-lab 0.4.3__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 +9 -1
- pixie/web/_templates/live_env.html +103 -28
- pixie/web/main.py +135 -3
- {pixie_lab-0.4.3.dist-info → pixie_lab-0.4.4.dist-info}/METADATA +14 -7
- {pixie_lab-0.4.3.dist-info → pixie_lab-0.4.4.dist-info}/RECORD +8 -8
- {pixie_lab-0.4.3.dist-info → pixie_lab-0.4.4.dist-info}/WHEEL +0 -0
- {pixie_lab-0.4.3.dist-info → pixie_lab-0.4.4.dist-info}/entry_points.txt +0 -0
- {pixie_lab-0.4.3.dist-info → pixie_lab-0.4.4.dist-info}/licenses/LICENSE +0 -0
pixie/events/_kinds.py
CHANGED
|
@@ -196,7 +196,14 @@ 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
|
|
199
|
+
# ---------- live env ------------------------------------------------
|
|
200
|
+
|
|
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``."""
|
|
205
|
+
|
|
206
|
+
|
|
200
207
|
# The canonical closed set. Every kind above is registered here; the
|
|
201
208
|
# ``EventsLog.emit`` call rejects anything not in this frozenset.
|
|
202
209
|
KNOWN_EVENT_KINDS: frozenset[str] = frozenset(
|
|
@@ -230,6 +237,7 @@ KNOWN_EVENT_KINDS: frozenset[str] = frozenset(
|
|
|
230
237
|
AUTH_LOGIN_SUCCEEDED,
|
|
231
238
|
AUTH_LOGIN_FAILED,
|
|
232
239
|
EVENTS_CLEARED,
|
|
240
|
+
LIVE_ENV_IMAGE_SELECTED,
|
|
233
241
|
}
|
|
234
242
|
)
|
|
235
243
|
|
|
@@ -6,7 +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="#
|
|
9
|
+
<a href="#setup">Setup</a>
|
|
10
|
+
<a href="#advanced">Advanced</a>
|
|
10
11
|
<a href="#options">Options</a>
|
|
11
12
|
</span>
|
|
12
13
|
</div>
|
|
@@ -14,60 +15,104 @@
|
|
|
14
15
|
{% endblock %}
|
|
15
16
|
|
|
16
17
|
{% block content %}
|
|
17
|
-
{
|
|
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.') }}
|
|
18
20
|
|
|
19
21
|
{% if flash_error %}
|
|
20
22
|
<div class="alert alert-danger py-2">{{ flash_error }}</div>
|
|
21
23
|
{% endif %}
|
|
22
24
|
|
|
23
|
-
{# ----
|
|
24
|
-
<div class="card shadow-sm mb-4" id="
|
|
25
|
+
{# ---- One-click setup -------------------------------------------- #}
|
|
26
|
+
<div class="card shadow-sm mb-4" id="setup">
|
|
25
27
|
<div class="card-header bg-light d-flex align-items-center gap-2">
|
|
26
|
-
<h2 class="h5 mb-0"><i class="bi bi-boombox me-2"></i>Live
|
|
27
|
-
{% if
|
|
28
|
-
<span class="badge text-bg-success ms-auto"><i class="bi bi-check-circle me-1"></i>
|
|
28
|
+
<h2 class="h5 mb-0"><i class="bi bi-boombox me-2"></i>Live env</h2>
|
|
29
|
+
{% if live_env_ready %}
|
|
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>
|
|
29
33
|
{% else %}
|
|
30
|
-
<span class="badge text-bg-warning ms-auto"><i class="bi bi-exclamation-triangle me-1"></i>
|
|
34
|
+
<span class="badge text-bg-warning ms-auto"><i class="bi bi-exclamation-triangle me-1"></i>not set up</span>
|
|
31
35
|
{% endif %}
|
|
32
36
|
</div>
|
|
37
|
+
<div class="card-body">
|
|
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.
|
|
50
|
+
</p>
|
|
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...
|
|
73
|
+
{% else %}
|
|
74
|
+
<i class="bi bi-boombox me-1"></i>{% if live_env_ready %}Re-run setup{% else %}Set up live env{% endif %}
|
|
75
|
+
{% endif %}
|
|
76
|
+
</button>
|
|
77
|
+
<span class="text-muted small ms-2">Fetch + select in one step.</span>
|
|
78
|
+
</form>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{# ---- Advanced: select by content sha ---------------------------- #}
|
|
83
|
+
<div class="card shadow-sm mb-4" id="advanced">
|
|
84
|
+
<div class="card-header bg-light fw-semibold">
|
|
85
|
+
<i class="bi bi-sliders me-1"></i>Advanced: select by content sha
|
|
86
|
+
</div>
|
|
33
87
|
<div class="card-body">
|
|
34
88
|
<p class="small text-muted mb-3">
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
<code>pixie-flash-*</code> modes boot it over
|
|
40
|
-
<strong>ephemeral nbdboot</strong> (a RO NBD lower + tmpfs
|
|
41
|
-
overlay); dracut brings the NIC up where the retired
|
|
42
|
-
live-boot squashfs hung in the initramfs on some boards. The
|
|
43
|
-
on-target pixie CLI phones home so inventory / flash work as
|
|
44
|
-
before.
|
|
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.
|
|
45
93
|
</p>
|
|
46
94
|
<form method="post" action="/ui/live-env/image/edit" class="row g-3">
|
|
47
95
|
<div class="col-12">
|
|
48
96
|
<label for="live_env_image_sha" class="form-label">
|
|
49
97
|
Image content sha
|
|
50
98
|
<span class="badge text-bg-{% if live_env_image_sha.override %}primary{% else %}secondary{% endif %} ms-1">
|
|
51
|
-
{% if live_env_image_sha.override %}override{% elif live_env_image_sha.effective %}env{% else %}
|
|
99
|
+
{% if live_env_image_sha.override %}override{% elif live_env_image_sha.effective %}env{% else %}unset{% endif %}
|
|
52
100
|
</span>
|
|
53
101
|
</label>
|
|
54
102
|
<input type="text" id="live_env_image_sha" name="live_env_image_sha"
|
|
55
103
|
class="form-control font-monospace"
|
|
56
104
|
value="{{ live_env_image_sha.override }}"
|
|
57
|
-
placeholder="(unset - live-env modes
|
|
105
|
+
placeholder="(unset - live-env modes render the unavailable plan)"
|
|
58
106
|
pattern="[0-9a-fA-F]{64}">
|
|
59
107
|
<div class="form-text">
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
fetched. Blank falls back to <code>{{ live_env_image_sha.env }}</code>
|
|
63
|
-
then unset, in which case the live-env boot modes render the
|
|
64
|
-
<code>unavailable</code> plan. Effective:
|
|
108
|
+
Blank falls back to <code>{{ live_env_image_sha.env }}</code> then unset.
|
|
109
|
+
Effective:
|
|
65
110
|
{% if live_env_image_sha.effective %}<code>{{ live_env_image_sha.effective }}</code>{% else %}<em class="text-muted">(none)</em>{% endif %}.
|
|
66
111
|
{% if live_env_image_sha.updated_at %}<br>Last updated: <code>{{ live_env_image_sha.updated_at | fmt_ts }}</code>.{% endif %}
|
|
67
112
|
</div>
|
|
68
113
|
</div>
|
|
69
114
|
<div class="col-12">
|
|
70
|
-
<button type="submit" class="btn btn-
|
|
115
|
+
<button type="submit" class="btn btn-outline-secondary"><i class="bi bi-check2 me-1"></i>Save</button>
|
|
71
116
|
<span class="text-muted small ms-2">
|
|
72
117
|
Takes effect on the NEXT <code>GET /pxe/<mac></code>; a target already mid-boot must be power-cycled.
|
|
73
118
|
</span>
|
|
@@ -79,7 +124,7 @@
|
|
|
79
124
|
{# ---- Options (config) ------------------------------------------- #}
|
|
80
125
|
<div class="card shadow-sm mb-4" id="options">
|
|
81
126
|
<div class="card-header bg-light fw-semibold">
|
|
82
|
-
<i class="bi bi-
|
|
127
|
+
<i class="bi bi-gear me-1"></i>Options
|
|
83
128
|
</div>
|
|
84
129
|
<div class="card-body">
|
|
85
130
|
<form method="post" action="/ui/live-env/cmdline/edit" class="row g-3">
|
|
@@ -99,7 +144,7 @@
|
|
|
99
144
|
<option value="pci=realloc=on,nocrs">
|
|
100
145
|
</datalist>
|
|
101
146
|
<div class="form-text">
|
|
102
|
-
Tokens appended verbatim to the
|
|
147
|
+
Tokens appended verbatim to the live-env kernel cmdline
|
|
103
148
|
(after <code>pixie.mac=</code> / <code>bty.mac=</code>, so last-token-wins on any
|
|
104
149
|
conflict). Whitespace-separated; single line. Blank falls back to
|
|
105
150
|
<code>{{ live_env_extra_cmdline.env }}</code> then no append. A machine with its own
|
|
@@ -119,4 +164,34 @@
|
|
|
119
164
|
</form>
|
|
120
165
|
</div>
|
|
121
166
|
</div>
|
|
167
|
+
|
|
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>
|
|
122
197
|
{% endblock %}
|
pixie/web/main.py
CHANGED
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import os
|
|
21
21
|
import secrets
|
|
22
22
|
import tempfile
|
|
23
|
-
from collections.abc import AsyncIterator
|
|
23
|
+
from collections.abc import AsyncIterator, Callable
|
|
24
24
|
from concurrent.futures import ThreadPoolExecutor
|
|
25
25
|
from contextlib import asynccontextmanager
|
|
26
26
|
from contextlib import suppress as contextlib_suppress
|
|
@@ -55,6 +55,7 @@ from pixie.events._kinds import (
|
|
|
55
55
|
CATALOG_IMPORT_OK,
|
|
56
56
|
EVENTS_CLEARED,
|
|
57
57
|
EXPORT_NBDKIT_SPAWNED,
|
|
58
|
+
LIVE_ENV_IMAGE_SELECTED,
|
|
58
59
|
TFTP_STARTED,
|
|
59
60
|
TFTP_STOPPED,
|
|
60
61
|
)
|
|
@@ -100,6 +101,13 @@ STATE_DIR_ENV = "PIXIE_DATA_DIR"
|
|
|
100
101
|
FETCH_POOL_SIZE_ENV = "PIXIE_FETCH_POOL_SIZE"
|
|
101
102
|
DEFAULT_FETCH_POOL_SIZE = 4
|
|
102
103
|
|
|
104
|
+
# Catalog entry name of the pixie-live-env disk image the one-click
|
|
105
|
+
# "Set up live env" action fetches + selects. Matches the bundled seed
|
|
106
|
+
# catalog (and the release catalog fragment); its netboot bundle is
|
|
107
|
+
# resolved from the entry's ``netboot_src``, so only the image name is
|
|
108
|
+
# pinned here.
|
|
109
|
+
LIVE_ENV_IMAGE_ENTRY = "pixie-live-env"
|
|
110
|
+
|
|
103
111
|
# NBD supervisor knobs. In production ``--network=host`` covers the
|
|
104
112
|
# bind + port range; in dev / tests operators tweak.
|
|
105
113
|
NBD_PORT_BASE_ENV = "PIXIE_NBD_PORT_BASE"
|
|
@@ -752,6 +760,11 @@ def create_app() -> FastAPI:
|
|
|
752
760
|
thread_name_prefix="pixie-fetch",
|
|
753
761
|
)
|
|
754
762
|
app.state.fetch_states = {}
|
|
763
|
+
# Progress for the one-click "Set up live env" action (fetch the
|
|
764
|
+
# pixie-live-env image + its bundle, then select the image). One per
|
|
765
|
+
# pixie, mirrors a fetch_states entry: state running/done/error +
|
|
766
|
+
# step + the underlying fetch's phase/byte counters.
|
|
767
|
+
app.state.live_env_setup_state = {}
|
|
755
768
|
|
|
756
769
|
# SessionMiddleware signs the ``pixie-token`` cookie. Sliding TTL:
|
|
757
770
|
# 7 days from last touch. ``https_only=False`` because pixie is
|
|
@@ -2440,14 +2453,25 @@ def create_app() -> FastAPI:
|
|
|
2440
2453
|
|
|
2441
2454
|
def _live_env_context(request: Request, flash_error: str | None = None) -> dict[str, Any]:
|
|
2442
2455
|
"""Render context for the dedicated /ui/live-env pane: the
|
|
2443
|
-
selected live-env disk image (nbdboot),
|
|
2444
|
-
|
|
2456
|
+
selected live-env disk image (nbdboot), the one-click setup
|
|
2457
|
+
progress, plus the extra-cmdline override."""
|
|
2445
2458
|
state = request.app.state
|
|
2446
2459
|
store: SettingsStore = state.settings_store
|
|
2460
|
+
# Ready == an image sha is selected AND its catalog image is
|
|
2461
|
+
# fetched on disk (the at-a-glance signal; the renderer does the
|
|
2462
|
+
# full bundle+blob check at boot).
|
|
2463
|
+
selected_sha = store.resolve_live_env_image_sha()
|
|
2464
|
+
live_env_ready = bool(selected_sha) and any(
|
|
2465
|
+
e.content_sha256 == selected_sha
|
|
2466
|
+
for e in state.catalog_store.list_entries()
|
|
2467
|
+
if getattr(e, "bindable", False) and e.content_sha256
|
|
2468
|
+
)
|
|
2447
2469
|
return {
|
|
2448
2470
|
"version": pixie.__version__,
|
|
2449
2471
|
"authed": True,
|
|
2450
2472
|
"page": "live-env",
|
|
2473
|
+
"live_env_ready": live_env_ready,
|
|
2474
|
+
"live_env_setup_state": dict(state.live_env_setup_state),
|
|
2451
2475
|
"live_env_extra_cmdline": {
|
|
2452
2476
|
"override": store.get(KEY_LIVE_ENV_EXTRA_CMDLINE) or "",
|
|
2453
2477
|
"effective": store.resolve_live_env_extra_cmdline(),
|
|
@@ -2542,6 +2566,114 @@ def create_app() -> FastAPI:
|
|
|
2542
2566
|
store.clear(KEY_LIVE_ENV_IMAGE_SHA)
|
|
2543
2567
|
return RedirectResponse(url="/ui/live-env", status_code=status.HTTP_303_SEE_OTHER)
|
|
2544
2568
|
|
|
2569
|
+
@app.post("/ui/live-env/setup")
|
|
2570
|
+
def ui_live_env_setup(
|
|
2571
|
+
request: Request,
|
|
2572
|
+
_auth: None = Depends(_require_ui_auth),
|
|
2573
|
+
) -> RedirectResponse:
|
|
2574
|
+
"""One-click live-env setup: fetch the ``pixie-live-env`` image +
|
|
2575
|
+
its netboot bundle (from the seeded catalog), then select the
|
|
2576
|
+
image (set ``live_env.image_sha``) -- the whole
|
|
2577
|
+
catalog-fetch-then-copy-sha dance in a single click. Runs on the
|
|
2578
|
+
fetch pool; progress lands on ``app.state.live_env_setup_state``
|
|
2579
|
+
for the Live env page to poll. Idempotent: re-running when
|
|
2580
|
+
everything is already fetched just re-selects the image."""
|
|
2581
|
+
state = request.app.state
|
|
2582
|
+
store = state.catalog_store
|
|
2583
|
+
settings: SettingsStore = state.settings_store
|
|
2584
|
+
setup_state = state.live_env_setup_state
|
|
2585
|
+
events = getattr(state, "events_log", None)
|
|
2586
|
+
|
|
2587
|
+
if setup_state.get("state") == "running":
|
|
2588
|
+
return RedirectResponse(url="/ui/live-env", status_code=status.HTTP_303_SEE_OTHER)
|
|
2589
|
+
|
|
2590
|
+
image_entry = store.get_entry(LIVE_ENV_IMAGE_ENTRY)
|
|
2591
|
+
if image_entry is None:
|
|
2592
|
+
setup_state.clear()
|
|
2593
|
+
setup_state.update(
|
|
2594
|
+
{
|
|
2595
|
+
"state": "error",
|
|
2596
|
+
"error": (
|
|
2597
|
+
f"no {LIVE_ENV_IMAGE_ENTRY!r} entry in the catalog; import the "
|
|
2598
|
+
"release catalog first (it seeds on a fresh deploy)."
|
|
2599
|
+
),
|
|
2600
|
+
"at_iso": _now_iso(),
|
|
2601
|
+
}
|
|
2602
|
+
)
|
|
2603
|
+
return RedirectResponse(url="/ui/live-env", status_code=status.HTTP_303_SEE_OTHER)
|
|
2604
|
+
bundle_entry = (
|
|
2605
|
+
store.get_entry_by_src(image_entry.netboot_src) if image_entry.netboot_src else None
|
|
2606
|
+
)
|
|
2607
|
+
if bundle_entry is None:
|
|
2608
|
+
setup_state.clear()
|
|
2609
|
+
setup_state.update(
|
|
2610
|
+
{
|
|
2611
|
+
"state": "error",
|
|
2612
|
+
"error": (
|
|
2613
|
+
f"{LIVE_ENV_IMAGE_ENTRY!r} has no netboot bundle in the catalog "
|
|
2614
|
+
"(its netboot_src does not resolve)."
|
|
2615
|
+
),
|
|
2616
|
+
"at_iso": _now_iso(),
|
|
2617
|
+
}
|
|
2618
|
+
)
|
|
2619
|
+
return RedirectResponse(url="/ui/live-env", status_code=status.HTTP_303_SEE_OTHER)
|
|
2620
|
+
|
|
2621
|
+
setup_state.clear()
|
|
2622
|
+
setup_state.update({"state": "running", "step": "bundle", "at_iso": _now_iso()})
|
|
2623
|
+
|
|
2624
|
+
def _step_report(step: str) -> Callable[[dict[str, Any]], None]:
|
|
2625
|
+
def cb(payload: dict[str, Any]) -> None:
|
|
2626
|
+
setup_state.update(payload)
|
|
2627
|
+
setup_state["step"] = step
|
|
2628
|
+
setup_state["state"] = "running"
|
|
2629
|
+
|
|
2630
|
+
return cb
|
|
2631
|
+
|
|
2632
|
+
def _run() -> None:
|
|
2633
|
+
try:
|
|
2634
|
+
# Bundle first (small; the render needs its unpacked
|
|
2635
|
+
# kernel/initrd), then the image (the ~900 MiB leg).
|
|
2636
|
+
setup_state["step"] = "bundle"
|
|
2637
|
+
_fetch(bundle_entry, store, progress=_step_report("bundle"))
|
|
2638
|
+
setup_state["step"] = "image"
|
|
2639
|
+
img = _fetch(image_entry, store, progress=_step_report("image"))
|
|
2640
|
+
settings.set_value(KEY_LIVE_ENV_IMAGE_SHA, img.content_sha256)
|
|
2641
|
+
setup_state.clear()
|
|
2642
|
+
setup_state.update(
|
|
2643
|
+
{
|
|
2644
|
+
"state": "done",
|
|
2645
|
+
"image_sha": img.content_sha256,
|
|
2646
|
+
"at_iso": _now_iso(),
|
|
2647
|
+
}
|
|
2648
|
+
)
|
|
2649
|
+
if events is not None:
|
|
2650
|
+
events.emit(
|
|
2651
|
+
LIVE_ENV_IMAGE_SELECTED,
|
|
2652
|
+
summary=(
|
|
2653
|
+
f"live env set up + selected (image sha {img.content_sha256[:12]})"
|
|
2654
|
+
),
|
|
2655
|
+
details={"image_sha": img.content_sha256},
|
|
2656
|
+
)
|
|
2657
|
+
except FetchError as exc:
|
|
2658
|
+
setup_state.clear()
|
|
2659
|
+
setup_state.update({"state": "error", "error": str(exc), "at_iso": _now_iso()})
|
|
2660
|
+
except Exception as exc: # pragma: no cover -- defensive
|
|
2661
|
+
setup_state.clear()
|
|
2662
|
+
setup_state.update(
|
|
2663
|
+
{"state": "error", "error": f"internal: {exc}", "at_iso": _now_iso()}
|
|
2664
|
+
)
|
|
2665
|
+
|
|
2666
|
+
state.fetch_pool.submit(_run)
|
|
2667
|
+
return RedirectResponse(url="/ui/live-env", status_code=status.HTTP_303_SEE_OTHER)
|
|
2668
|
+
|
|
2669
|
+
@app.get("/ui/live-env/setup-state.json")
|
|
2670
|
+
def ui_live_env_setup_state(
|
|
2671
|
+
request: Request,
|
|
2672
|
+
_auth: None = Depends(_require_ui_auth),
|
|
2673
|
+
) -> JSONResponse:
|
|
2674
|
+
"""Poll target for the Live env page's setup progress bar."""
|
|
2675
|
+
return JSONResponse(dict(request.app.state.live_env_setup_state))
|
|
2676
|
+
|
|
2545
2677
|
# ---------- feature routers --------------------------------------
|
|
2546
2678
|
#
|
|
2547
2679
|
# Catalog + blob + artifacts routes live at the same URL shape they
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pixie-lab
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
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
|
|
@@ -67,13 +67,18 @@ top nav has Machines, Catalog, Events, and Settings.
|
|
|
67
67
|
2. Click Fetch on a row to download it. The status pill ticks
|
|
68
68
|
through downloading -> decompressing -> unpacking with a
|
|
69
69
|
`bytes / total` counter so you see progress live.
|
|
70
|
-
3.
|
|
70
|
+
3. For the live-env boot modes (`pixie-inventory` / `pixie-tui` /
|
|
71
|
+
`pixie-flash-*`), Fetch the `pixie-live-env` image + its netboot
|
|
72
|
+
bundle, then set the image's `content_sha256` on the Live env page
|
|
73
|
+
(`/ui/live-env`) or via `PIXIE_LIVE_ENV_IMAGE_SHA`. Those modes
|
|
74
|
+
nbdboot that image; until it's selected they render `unavailable`.
|
|
75
|
+
4. Power-cycle a target with pixie in its DHCP next-server chain.
|
|
71
76
|
A first contact shows up on `/ui/machines` with the default
|
|
72
77
|
`ipxe-exit` boot mode. Open the row to bind a mode + image.
|
|
73
|
-
|
|
78
|
+
5. `pixie-inventory` prompts the live env to POST an inventory
|
|
74
79
|
blob; the target's disks + NICs + memory show up on the
|
|
75
80
|
machine detail page.
|
|
76
|
-
|
|
81
|
+
6. Flash modes (`pixie-flash-once`, `pixie-flash-always`) require
|
|
77
82
|
a target disk serial that came in on the inventory. The
|
|
78
83
|
binding form disables Save until that prerequisite is met.
|
|
79
84
|
|
|
@@ -85,9 +90,11 @@ event log, settings, NBD-export records, and persistent-overlay
|
|
|
85
90
|
records. `blobs/<sha>/blob` holds fetched disk images.
|
|
86
91
|
`artifacts/<sha>/{vmlinuz,initrd,manifest.json}` holds unpacked
|
|
87
92
|
netboot bundles. `overlays/<mac>/<image_sha>/<profile>.qcow2` holds
|
|
88
|
-
per-machine writable overlays for the `nbdboot` boot mode.
|
|
89
|
-
`
|
|
90
|
-
|
|
93
|
+
per-machine writable overlays for the `nbdboot` boot mode. The live env
|
|
94
|
+
(`pixie-inventory` / `pixie-tui` / `pixie-flash-*`) is the fetched
|
|
95
|
+
`pixie-live-env` disk image, selected by `PIXIE_LIVE_ENV_IMAGE_SHA` /
|
|
96
|
+
the Live env page and nbdbooted like any other image, so it lives in
|
|
97
|
+
`blobs/` + `artifacts/` too - there is no separate live-env directory.
|
|
91
98
|
|
|
92
99
|
Both admin password and display timezone / strftime pattern have
|
|
93
100
|
env-var overrides plus DB overrides via `/ui/settings`; env wins
|
|
@@ -17,7 +17,7 @@ pixie/deploy/__init__.py,sha256=3PvSvrIRdqxLCz0nTdVSBiPzEtDl359XSUBgyMBkMT8,1019
|
|
|
17
17
|
pixie/deploy/_main.py,sha256=Or2espz9oWNmnGfyEzIsEyvRwWEHL4uxyE6U9OdMZJs,15403
|
|
18
18
|
pixie/deploy/_templates.py,sha256=WkkKRDU00FDWVZM0XeKR-BCtSBPvaZ2QHM1TBeq0RYg,5418
|
|
19
19
|
pixie/events/__init__.py,sha256=3Quxl56ePkKD9-PjbomCOOZOpmqJ3wHcYa7VcT9KXv8,923
|
|
20
|
-
pixie/events/_kinds.py,sha256=
|
|
20
|
+
pixie/events/_kinds.py,sha256=BWHDCQC4oBS7qSgI_W6Wwz7839AZkFD1Cb1mGf0bDH8,10443
|
|
21
21
|
pixie/events/_log.py,sha256=0XSdIYrPIEtmxjTq9L7gy1Q4qGotTxgHhMkM9fv8tyY,9041
|
|
22
22
|
pixie/events/_routes.py,sha256=MbY69aZKDEzy4O8AizHtv_POPbdNMqz94imccFbtelM,1363
|
|
23
23
|
pixie/exports/__init__.py,sha256=RiI69GG_bjkb_d1CNRyk9KI7wyMpLVvWc_NgNnWxOYc,546
|
|
@@ -46,7 +46,7 @@ pixie/web/_overlay_bind.py,sha256=7aKYqMaujxt-XRa1jB6mVEkbt-w_koXjLvUzBTzy2o8,41
|
|
|
46
46
|
pixie/web/_overlays.py,sha256=5yZp6pDiFj_WKdLx6vCoEKD8mXG3KjkMOQt4OGYYdyc,8571
|
|
47
47
|
pixie/web/_settings_store.py,sha256=X9dO5dr31PvRUM-Y7cLilEiNG_7MCV8CpDW2Nc6V0Tc,11242
|
|
48
48
|
pixie/web/_table_state.py,sha256=zGH5oZ-XlMqUFQqmYglYqT_fu1Yrtaw-g5yhPtAKAEc,9524
|
|
49
|
-
pixie/web/main.py,sha256=
|
|
49
|
+
pixie/web/main.py,sha256=PkBcCmSs04GiEsCudlwqG9SrVmR_yMvN4lBFCrb8E9U,114145
|
|
50
50
|
pixie/web/_static/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
51
|
pixie/web/_static/bootstrap-icons.min.css,sha256=pdY4ejLKO67E0CM2tbPtq1DJ3VGDVVdqAR6j3ZwdiE4,87008
|
|
52
52
|
pixie/web/_static/bootstrap.min.css,sha256=NXgoHLHuUWz0lbqh8PRwCIl7_QhZE74v-FCwrYdCW1E,234717
|
|
@@ -62,7 +62,7 @@ pixie/web/_templates/events.html,sha256=ifawe--m5FMcicKZ2daCCycV7rGgpLJz7A309UrT
|
|
|
62
62
|
pixie/web/_templates/image_detail.html,sha256=--DjHCDgvripwr-9_lmnmaLCe4Iw2W_gXIGuKLMvcJw,6926
|
|
63
63
|
pixie/web/_templates/images.html,sha256=PO8r0hZ6tXMqHZo_m1t_wgh4NEZP6u_72owH_Jbz2Zo,7266
|
|
64
64
|
pixie/web/_templates/layout.html,sha256=9kQ1sWyAqMl9YgwJO1jIjwRbiG8bt2sUpJLNrbBZmzo,13988
|
|
65
|
-
pixie/web/_templates/live_env.html,sha256=
|
|
65
|
+
pixie/web/_templates/live_env.html,sha256=1oNnjBq9r-MSr-tmnThi0xMI8ysQVOl8I1TpwFNNtJQ,10878
|
|
66
66
|
pixie/web/_templates/login.html,sha256=d60N8kmr7YaAoTKRBaOiGN5Srdy8-BgB8_Zz9TQvGwc,1810
|
|
67
67
|
pixie/web/_templates/machine_detail.html,sha256=ijI5FKuyqOdDwCdLGM_H7yHgqn6fFMm6pOPVBLT9G00,49797
|
|
68
68
|
pixie/web/_templates/machines.html,sha256=modlu9MNqpxOkDd90o-yV1g5yuEiC61I6JtXQk7_PUo,9803
|
|
@@ -75,8 +75,8 @@ pixie/web/_templates/ipxe/bootstrap.j2,sha256=xt13H2dHf-SeLhWRQvptqGY51sJ_L24C2E
|
|
|
75
75
|
pixie/web/_templates/ipxe/exit.j2,sha256=pbTXTwjgiEsPZlRuv7_J0Q3BepXEqXnb82mv1sxDW24,371
|
|
76
76
|
pixie/web/_templates/ipxe/nbdboot.j2,sha256=s1CwIKou5W6LRoC2MdZYwA3RcRN8AU3B1gbJmGLS9MM,3950
|
|
77
77
|
pixie/web/_templates/ipxe/unavailable.j2,sha256=zt4DYfYRXmOIjL13diYPxEApTP3CRnn5Beszeh3BPBw,440
|
|
78
|
-
pixie_lab-0.4.
|
|
79
|
-
pixie_lab-0.4.
|
|
80
|
-
pixie_lab-0.4.
|
|
81
|
-
pixie_lab-0.4.
|
|
82
|
-
pixie_lab-0.4.
|
|
78
|
+
pixie_lab-0.4.4.dist-info/METADATA,sha256=KQr4pQm8RhsU-8vEBSZ06JYfBT2S_8_0-3L9HxldDEE,4989
|
|
79
|
+
pixie_lab-0.4.4.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
80
|
+
pixie_lab-0.4.4.dist-info/entry_points.txt,sha256=G3vtKpg_gQtisiyS31EjdvC4QC52Kfw2K7iMsfsnj5A,71
|
|
81
|
+
pixie_lab-0.4.4.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
82
|
+
pixie_lab-0.4.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|