webapp/imaging: rewind detection + WinPE-phase status push

services/imaging_status.py - if a new POST arrives with stage_index <= 1
that is lower than the cached stage_index, OR the previous run already
finished (status=succeeded|failed), reset the session: clear log_tail,
mint a fresh started_at, drop the status field so the in_progress
default re-applies. Preserves serial + records the previous run's
last_updated under previous_run_at for audit. Without this, a reimage
on the same bay would leave a stale 6/8 "succeeded" card visible until
the new run progressed past that index.

playbook/startnet.cmd - one-line PowerShell POST after the PXE menu
choice + enrollment-share mount, before PESetup.exe waits to start.
Captures BIOS serial via wmic, MAC via Get-NetAdapter, and posts:
  stage_index=2, current_stage="WinPE: PESetup / WIM apply".
Best-effort; try/catch swallows any network failure so a missing
webapp never blocks imaging. PXE clients will now appear on the
/imaging dashboard during WinPE phase instead of only post-PPKG.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-05-13 11:11:03 -04:00
parent 908b668bde
commit 4e018feaa0
2 changed files with 65 additions and 12 deletions

View File

@@ -66,12 +66,31 @@ def update_session(payload: dict) -> dict:
except (json.JSONDecodeError, OSError):
state = {}
# Reimage detection: if the new payload's stage_index is <= 1 and we have
# an existing session that was further along, treat this as a fresh run.
# Clear log_tail + reset started_at; preserve serial. Without this, a
# reimage on the same bay leaves stale "succeeded" / high-idx state on
# the dashboard until the new run progresses past idx 1.
if state:
try:
old_idx = int(state.get("stage_index") or 0)
new_idx = int(payload.get("stage_index") or 0)
except (TypeError, ValueError):
old_idx, new_idx = 0, 0
rewind = new_idx > 0 and new_idx < old_idx and new_idx <= 1
prev_done = state.get("status") in ("succeeded", "failed")
if rewind or (prev_done and new_idx > 0 and new_idx <= 1):
state = {"serial": serial, "previous_run_at": state.get("last_updated"), "log_tail": []}
if not state:
state = {
"serial": serial,
"started_at": _now_iso(),
"log_tail": [],
}
elif "started_at" not in state:
# Fresh state after a rewind - mint a new started_at.
state["started_at"] = _now_iso()
# Append any new log lines (preserve old; cap to LOG_TAIL_MAX).
new_lines = payload.pop("log_lines", None)