Webapp redesign phase 4: Updates overview and rollback review

- GET /updates/: one card per updater (Dell, iPXE, Blancco, BPRT) with
  its state, a Check now / Open action and details in accordions; the
  shared download cap is a small popover.
- Dell applies list: one line per apply titled by its first change
  ('Latitude 5350 pack A11 to A12', model looked up in the maps) and
  'and n more'; the chip says how many written files are unchanged since
  the apply. The body lists every change, skipped item, error and file.
- dell_updates.file_status(): per file an apply wrote, unchanged /
  changed / missing by content hash, the same test rollback uses.
  describe(), history_view() and rollback_preview() build on it.
- GET /updates/dell/rollback/<id>: review a rollback before doing it,
  with the files that would be restored and a diff of each. The POST
  route is unchanged; both forms can return to the overview.
This commit is contained in:
cproudlock
2026-10-14 13:54:41 -04:00
parent 731d7b044d
commit cc1e76f3ab
6 changed files with 391 additions and 2 deletions

View File

@@ -130,6 +130,35 @@ def dashboard():
)
@app.route("/updates/")
def updates_overview():
"""One card per updater (Dell, iPXE, Blancco, BPRT) and the Dell applies
with their rollback state. Each card links to its own page."""
from services import blancco_update, bprt_sync, dell_updates, ipxe_update, updates_common
scan = dell_updates.last_scan()
dell = {"checked": scan.get("checked") if scan else None,
"drivers": len(scan.get("drivers") or []) if scan else 0,
"bios": len(scan.get("bios") or []) if scan else 0,
"drv_updates": sum(1 for r in (scan.get("drivers") or []) if r.get("status") == "update") if scan else 0,
"bios_updates": sum(1 for r in (scan.get("bios") or []) if r.get("status") == "update") if scan else 0}
rows = ipxe_update.installed()
by_name = {r["name"]: r["info"] for r in rows}
latest = ipxe_update.last_check()
efi = by_name.get("ipxe.efi") or {}
ipxe = {"rows": rows, "version": efi.get("version"), "latest": latest,
"status": ipxe_update.compare(latest["tag"], efi.get("version")) if latest else None}
try:
blancco = {"current": blancco_update.current(), "staged": blancco_update.staged()}
except Exception as ex: # a broken release folder must not take the page down
blancco = {"current": {}, "staged": [], "error": str(ex)}
try:
bprt = {"live": bprt_sync.live_summary(), "batches": bprt_sync.batches()}
except Exception as ex:
bprt = {"live": {}, "batches": [], "error": str(ex)}
return render_template("updates_overview.html", dell=dell, ipxe=ipxe, blancco=blancco, bprt=bprt,
history=dell_updates.history_view(10), settings=updates_common.settings())
@app.route("/images/import", methods=["GET", "POST"])
def images_import():
usb_mounts = system.find_usb_mounts()