fix(smoke): conditional role drill, healthy-gate for drills, exit reaping

This commit is contained in:
2026-08-22 16:01:32 -06:00
parent 25bae3b4a2
commit d954b6e90d
+83 -4
View File
@@ -18,14 +18,20 @@
# 8. Bad-value drill: feeds --reconcile a wrong bands value and an
# oversized SSID pool (25 dummy entries), verifies both are
# repaired, restores originals.
# 9. Rollback watchdog drill: runs mk8-watchdog.sh against a config
# 9. RF role drill (only when SMOKE_UPLINK_SSID is set): switches
# radio1 to the uplink role against the named lab AP, expects an
# association, then back to attack with hopping resumed.
# 10. Rollback watchdog drill: runs mk8-watchdog.sh against a config
# profile while the web UI is up (expects clean promote exit),
# then STOPS the pagerwebui service and expects the watchdog to
# roll back and journal a 'rollback' event, then restarts webui.
# Preceded by a 5-second warning countdown; brief web outage.
# Drills run only when every read-only check has passed.
#
# Environment:
# PASS webui password used for POST /api/login (check 7).
# SMOKE_UPLINK_SSID lab AP SSID; enables the --write RF role drill.
# SMOKE_UPLINK_PSK optional PSK for the lab AP.
#
# Exit status: 0 when every executed check passes, 1 otherwise.
@@ -42,9 +48,12 @@ JAR=/tmp/mk8-smoke-cookies.$$
WRITE=0
PASS="${PASS:-}"
UPLINK_SSID="${SMOKE_UPLINK_SSID:-}"
UPLINK_PSK="${SMOKE_UPLINK_PSK:-}"
WEB_STOPPED=0
FAILED=0
WAIT_RC=0
wp=""
PY="$(command -v python3 2>/dev/null || true)"
[ -n "$PY" ] || PY=/usr/bin/python3
@@ -53,14 +62,20 @@ PY="$(command -v python3 2>/dev/null || true)"
usage() {
printf 'Usage: smoke.sh [--write]\n'
printf '\n'
printf 'Read-only checks run by default. --write adds two destructive\n'
printf 'drills that briefly toggle UCI config and stop/start the\n'
printf 'pagerwebui service; original values are restored automatically.\n'
printf 'Read-only checks run by default. --write adds destructive drills\n'
printf '(run only if every read-only check passed) that briefly toggle\n'
printf 'UCI config and stop/start the pagerwebui service; original values\n'
printf 'are restored automatically.\n'
printf 'Set PASS=<webui password> to enable the authenticated API check.\n'
printf 'Set SMOKE_UPLINK_SSID=[<PSK via SMOKE_UPLINK_PSK>] to enable the\n'
printf '--write RF role drill against a lab AP.\n'
}
on_exit() {
rm -f "$JAR" 2>/dev/null
if [ -n "$wp" ]; then
kill "$wp" 2>/dev/null
fi
if [ "$WEB_STOPPED" = "1" ]; then
info 'restoring pagerwebui service'
"$WEBUI_INIT" start >/dev/null 2>&1
@@ -291,6 +306,67 @@ drill_bad_values() {
"$PY" "$REL/server.py" --reconcile >/dev/null 2>&1
}
drill_role() {
if [ -z "$UPLINK_SSID" ]; then
skip 'role drill: set SMOKE_UPLINK_SSID to enable'
return
fi
info "role drill: uplink '$UPLINK_SSID' then attack"
out="$("$PY" - "$REL" "$LEGACY" "$UPLINK_SSID" "$UPLINK_PSK" <<'PYEOF' 2>&1
import json
import os
import sys
rel, legacy, ssid, psk = sys.argv[1:5]
mk8_rfplan = None
for d in (rel, legacy):
if os.path.isfile(os.path.join(d, 'mk8_rfplan.py')):
sys.path.insert(0, d)
try:
import mk8_rfplan
break
except Exception:
sys.path.remove(d)
if mk8_rfplan is None:
print('FAIL mk8_rfplan not importable from release or legacy dir')
raise SystemExit(1)
r1 = mk8_rfplan.set_role(
'uplink', ssid=ssid or None, psk=psk or None)
if not isinstance(r1, dict) or not r1.get('ok'):
print('FAIL uplink set_role failed: %s' % json.dumps(r1))
raise SystemExit(1)
assoc = mk8_rfplan.associated()
if not assoc:
print('FAIL uplink associated() returned nothing after set_role')
else:
print('associated as %s' % assoc)
try:
r2 = mk8_rfplan.set_role('attack')
except Exception as exc:
r2 = {'ok': False, 'error': str(exc)}
if not isinstance(r2, dict) or not r2.get('ok'):
print('FAIL attack set_role failed: %s' % json.dumps(r2))
raise SystemExit(1)
raise SystemExit(0 if assoc else 1)
PYEOF
)"
rc=$?
printf '%s\n' "$out" | sed 's/^/ /'
if [ "$rc" -ne 0 ]; then
fail 'role drill: uplink association failed (see detail above)'
return
fi
hop="$(uci_get pineapd.wlan1mon.hop)"
if [ "$hop" = "1" ]; then
pass 'role drill: uplink assoc + attack role + hopping resumed'
else
fail "role drill: pineapd.wlan1mon.hop=$hop after attack role (want 1)"
fi
}
drill_watchdog() {
WDOG="$REL/mk8-watchdog.sh"
[ -f "$WDOG" ] || WDOG="$LEGACY/mk8-watchdog.sh"
@@ -399,9 +475,12 @@ main() {
if [ "$WRITE" = "1" ]; then
if [ -z "$PY" ]; then
fail 'drills: python3 required but not found'
elif [ "$FAILED" -ne 0 ]; then
skip 'drills: read-only checks failed; refusing drills'
else
printf -- '--- --write drills ---\n'
drill_bad_values
drill_role
drill_watchdog
fi
fi