From 1b902d18e613bb6ba104e1ecc97bb8536db8a32b Mon Sep 17 00:00:00 2001 From: nearxos Date: Wed, 18 Feb 2026 14:20:31 +0200 Subject: [PATCH] Add no-cache response header to dashboard for immediate visibility after deploys; enhance Proxmox monitoring documentation and update flash script to allow Backup without a golden image. --- .../emmc-provisioning/dashboard/app.py | 10 +++++ .../docs/PROXMOX-LXC-DEPLOYMENT.md | 26 +++++++++++ .../host/90-cm4-boot-mode.rules | 8 ++-- .../host/flash-emmc-on-connect.sh | 12 +++-- .../scripts/check-usb-on-host.sh | 21 +++++++++ .../scripts/deploy-dashboard-to-lxc.sh | 33 ++++++++++++++ .../scripts/monitor-from-host.sh | 44 +++++++++++++++++++ 7 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 chromium-setup/emmc-provisioning/scripts/check-usb-on-host.sh create mode 100755 chromium-setup/emmc-provisioning/scripts/deploy-dashboard-to-lxc.sh create mode 100755 chromium-setup/emmc-provisioning/scripts/monitor-from-host.sh diff --git a/chromium-setup/emmc-provisioning/dashboard/app.py b/chromium-setup/emmc-provisioning/dashboard/app.py index c429caa..acf4d1e 100644 --- a/chromium-setup/emmc-provisioning/dashboard/app.py +++ b/chromium-setup/emmc-provisioning/dashboard/app.py @@ -14,6 +14,16 @@ from flask import Flask, render_template, jsonify, request, send_file, Response app = Flask(__name__) + +@app.after_request +def no_cache(response): + """Prevent browser from caching the dashboard so deploys are visible immediately.""" + if request.path == "/" or request.path.startswith("/api/"): + response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" + response.headers["Pragma"] = "no-cache" + return response + + BASE_DIR = Path(os.environ.get("CM4_PROVISIONING_DIR", "/var/lib/cm4-provisioning")) STATUS_FILE = os.environ.get("CM4_STATUS_FILE", str(BASE_DIR / "status.json")) LOG_FILE = os.environ.get("CM4_LOG_FILE", str(BASE_DIR / "flash.log")) diff --git a/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md b/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md index eedd7c5..00ebc8e 100644 --- a/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md +++ b/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md @@ -141,6 +141,32 @@ Then open **http://<LXC-201-IP>:5000** (get the IP with `pct exec 201 -- h --- +## Monitoring from the host + +From the **Proxmox host** you can monitor: + +| What | How | +|------|-----| +| **USB device** | `lsusb` — CM4 in boot mode shows as **2b8e** (RPi) or **0a5c:2711** (Broadcom BCM2711) | +| **Live status** | `cat /var/lib/cm4-provisioning/status.json` — same JSON the dashboard shows (phase, message, error) | +| **Flash log** | `tail -f /var/lib/cm4-provisioning/flash.log` — script log (rpiboot, dd, errors) | +| **Flash job** | `systemctl status cm4-flash-once` — whether the udev-triggered job is running/failed | +| **Journal** | `journalctl -u cm4-flash-once -f` or `journalctl -t cm4-flash -f` — systemd/log output | +| **Block devices** | `lsblk` — after rpiboot, the eMMC appears as a new disk (e.g. `/dev/sdb`) | +| **Backups** | `ls /var/lib/cm4-provisioning/backups/` — backup images created from the dashboard | +| **Config** | `cat /opt/cm4-provisioning/env` — GOLDEN_IMAGE, RPIBOOT_DIR, EMMC_SIZE_BYTES | + +**One-command snapshot:** + +```bash +# From your machine (stream script to host): +ssh root@10.130.60.224 'bash -s' < chromium-setup/emmc-provisioning/scripts/monitor-from-host.sh +``` + +Or copy `scripts/monitor-from-host.sh` to the host and run `./monitor-from-host.sh` for a full status dump (USB, status.json, flash unit, last log lines, block devices, config). + +--- + ## Redeploy / update scripts From your repo (e.g. after changing scripts): diff --git a/chromium-setup/emmc-provisioning/host/90-cm4-boot-mode.rules b/chromium-setup/emmc-provisioning/host/90-cm4-boot-mode.rules index b0ffa11..0b51fd5 100644 --- a/chromium-setup/emmc-provisioning/host/90-cm4-boot-mode.rules +++ b/chromium-setup/emmc-provisioning/host/90-cm4-boot-mode.rules @@ -1,10 +1,10 @@ # When reTerminal (CM4) is connected in USB boot mode (eMMC disable jumper), -# Raspberry Pi Foundation USB device appears (vendor 2b8e). Trigger provisioning: -# run rpiboot to expose eMMC, then wait for user to choose Backup or Deploy in the portal. -# No auto-flash — action runs only after portal choice. +# the device appears as either Raspberry Pi Foundation (2b8e) or Broadcom BCM2711 Boot (0a5c:2711). +# Trigger provisioning: run rpiboot, then wait for user to choose Backup or Deploy in the portal. # Install: sudo cp 90-cm4-boot-mode.rules /etc/udev/rules.d/ # sudo udevadm control --reload-rules -# The trigger script starts the provisioning script via systemd so udev does not block. SUBSYSTEM=="usb", ATTR{idVendor}=="2b8e", ACTION=="add", \ RUN+="/usr/local/bin/cm4-flash-trigger.sh" +SUBSYSTEM=="usb", ATTR{idVendor}=="0a5c", ATTR{idProduct}=="2711", ACTION=="add", \ + RUN+="/usr/local/bin/cm4-flash-trigger.sh" diff --git a/chromium-setup/emmc-provisioning/host/flash-emmc-on-connect.sh b/chromium-setup/emmc-provisioning/host/flash-emmc-on-connect.sh index d0feadf..65a76e4 100644 --- a/chromium-setup/emmc-provisioning/host/flash-emmc-on-connect.sh +++ b/chromium-setup/emmc-provisioning/host/flash-emmc-on-connect.sh @@ -48,11 +48,9 @@ ACTION_REQUEST_FILE="${ACTION_REQUEST_FILE:-/var/lib/cm4-provisioning/action_req CURRENT_DEVICE_FILE="${CURRENT_DEVICE_FILE:-/var/lib/cm4-provisioning/current_device}" DEVICE_SOURCE_FILE="${DEVICE_SOURCE_FILE:-/var/lib/cm4-provisioning/device_source}" WAIT_TIMEOUT="${WAIT_TIMEOUT:-600}" -# Golden image required for deploy +# Golden image required only for Deploy; allow Backup without it if [[ ! -f "$GOLDEN_IMAGE" ]]; then - log "Golden image not found (required for deploy): $GOLDEN_IMAGE" - write_status "error" "Golden image not found" "null" "Golden image not found. Add golden.img for deploy." - exit 1 + log "Golden image not found (Deploy will be unavailable): $GOLDEN_IMAGE" fi RPIBOOT_BIN="$RPIBOOT_DIR/rpiboot" @@ -140,6 +138,12 @@ for (( i = 0; i < WAIT_TIMEOUT; i += 2 )); do write_status "error" "Backup failed" "null" "dd failed" fi elif [[ "$action" == "deploy" ]]; then + if [[ ! -f "$GOLDEN_IMAGE" ]]; then + log "Golden image not found; cannot deploy." + write_status "error" "Deploy unavailable" "null" "Golden image not found. Add golden.img to /var/lib/cm4-provisioning/ for deploy." + rm -f "$CURRENT_DEVICE_FILE" "$DEVICE_SOURCE_FILE" 2>/dev/null || true + exit 1 + fi write_status "flashing" "Writing golden image…" "null" log "Flashing $GOLDEN_IMAGE to $target_dev..." if dd if="$GOLDEN_IMAGE" of="$target_dev" bs=4M status=progress conv=fsync 2>>"$LOG_FILE"; then diff --git a/chromium-setup/emmc-provisioning/scripts/check-usb-on-host.sh b/chromium-setup/emmc-provisioning/scripts/check-usb-on-host.sh new file mode 100644 index 0000000..5ede0ce --- /dev/null +++ b/chromium-setup/emmc-provisioning/scripts/check-usb-on-host.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Run on the Proxmox HOST (where USB is connected) to verify the reTerminal in boot mode is seen. +# Usage: ssh root@10.130.60.224 'bash -s' < scripts/check-usb-on-host.sh +# Or copy to host and run: ./check-usb-on-host.sh + +echo "=== CM4 boot-mode USB (2b8e = RPi, 0a5c:2711 = Broadcom BCM2711) ===" +lsusb | grep -E "2b8e|0a5c" || echo "None found. Connect reTerminal in boot mode (eMMC disable jumper) and use the USB slave port." +echo "" +echo "=== All USB devices ===" +lsusb +echo "" +echo "=== Provisioning status ===" +cat /var/lib/cm4-provisioning/status.json 2>/dev/null || echo "No status.json (script has not run yet)." +echo "" +echo "=== Last flash log ===" +tail -15 /var/lib/cm4-provisioning/flash.log 2>/dev/null || echo "No flash.log" +echo "" +echo "=== udev rule and rpiboot ===" +test -f /etc/udev/rules.d/90-cm4-boot-mode.rules && echo "90-cm4-boot-mode.rules: present" || echo "90-cm4-boot-mode.rules: MISSING" +test -x /opt/usbboot/rpiboot && echo "rpiboot: present" || echo "rpiboot: MISSING" +test -f /etc/cm4-provisioning/enabled && echo "enabled: yes" || echo "enabled: no (provisioning disabled)" diff --git a/chromium-setup/emmc-provisioning/scripts/deploy-dashboard-to-lxc.sh b/chromium-setup/emmc-provisioning/scripts/deploy-dashboard-to-lxc.sh new file mode 100755 index 0000000..a1edb31 --- /dev/null +++ b/chromium-setup/emmc-provisioning/scripts/deploy-dashboard-to-lxc.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Deploy only the dashboard to the LXC by IP (no Proxmox host needed). +# Usage: ./deploy-dashboard-to-lxc.sh [user@lxc_ip] +# Example: ./deploy-dashboard-to-lxc.sh root@10.130.60.119 + +set -e +LXC="${1:-root@10.130.60.119}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(dirname "$SCRIPT_DIR")" +DASHBOARD_DIR="$REPO_DIR/dashboard" +REMOTE_DIR="/opt/cm4-provisioning/dashboard" + +if [[ ! -d "$DASHBOARD_DIR" ]]; then + echo "Error: dashboard dir not found: $DASHBOARD_DIR" + exit 1 +fi + +echo "Deploying dashboard to $LXC ($REMOTE_DIR) ..." +ssh "$LXC" "mkdir -p $REMOTE_DIR/templates" + +rsync -avz \ + "$DASHBOARD_DIR/app.py" \ + "$DASHBOARD_DIR/cm4-dashboard.service" \ + "$LXC:$REMOTE_DIR/" + +rsync -avz \ + "$DASHBOARD_DIR/templates/" \ + "$LXC:$REMOTE_DIR/templates/" + +echo "Restarting cm4-dashboard service ..." +ssh "$LXC" "systemctl restart cm4-dashboard && systemctl is-active --quiet cm4-dashboard && echo 'Dashboard restarted and running.'" + +echo "Done. Dashboard at http://$(echo "$LXC" | cut -d@ -f2):5000" diff --git a/chromium-setup/emmc-provisioning/scripts/monitor-from-host.sh b/chromium-setup/emmc-provisioning/scripts/monitor-from-host.sh new file mode 100755 index 0000000..f30c6ab --- /dev/null +++ b/chromium-setup/emmc-provisioning/scripts/monitor-from-host.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Run on the Proxmox HOST to see provisioning status, USB, flash job, and logs. +# Usage: ./monitor-from-host.sh or: ssh root@10.130.60.224 'bash -s' < scripts/monitor-from-host.sh + +PROV=/var/lib/cm4-provisioning + +echo "═══════════════════════════════════════════════════════════════" +echo " CM4 provisioning – host monitor" +echo "═══════════════════════════════════════════════════════════════" +echo "" + +echo "--- USB (CM4 boot mode: 2b8e or 0a5c:2711) ---" +lsusb | grep -E "2b8e|0a5c" || echo " No CM4 boot device seen." +echo "" + +echo "--- Current status (dashboard reads this) ---" +if [[ -f "$PROV/status.json" ]]; then + cat "$PROV/status.json" | python3 -m json.tool 2>/dev/null || cat "$PROV/status.json" +else + echo " No status.json (no run yet)." +fi +echo "" + +echo "--- Flash job (systemd unit) ---" +systemctl show cm4-flash-once --property=ActiveState,SubState,ExecMainPID 2>/dev/null || echo " Unit not run yet." +echo "" + +echo "--- Last 20 lines of flash.log ---" +tail -20 "$PROV/flash.log" 2>/dev/null || echo " No flash.log" +echo "" + +echo "--- Provisioning dir ---" +ls -la "$PROV/" 2>/dev/null || echo " Dir missing." +echo "" + +echo "--- Block devices (eMMC appears here after rpiboot) ---" +lsblk -d -o NAME,SIZE,MODEL,TRAN 2>/dev/null | head -15 +echo "" + +echo "--- Config ---" +echo " enabled: $([ -f /etc/cm4-provisioning/enabled ] && echo yes || echo no)" +echo " rpiboot: $([ -x /opt/usbboot/rpiboot ] && echo /opt/usbboot/rpiboot || echo MISSING)" +echo " golden: $([ -f $PROV/golden.img ] && echo present || echo not set)" +echo " backups: $(ls "$PROV/backups" 2>/dev/null | wc -l) file(s)"