fix(smoke): conditional role drill, healthy-gate for drills, exit reaping
This commit is contained in:
+83
-4
@@ -18,14 +18,20 @@
|
|||||||
# 8. Bad-value drill: feeds --reconcile a wrong bands value and an
|
# 8. Bad-value drill: feeds --reconcile a wrong bands value and an
|
||||||
# oversized SSID pool (25 dummy entries), verifies both are
|
# oversized SSID pool (25 dummy entries), verifies both are
|
||||||
# repaired, restores originals.
|
# 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),
|
# profile while the web UI is up (expects clean promote exit),
|
||||||
# then STOPS the pagerwebui service and expects the watchdog to
|
# then STOPS the pagerwebui service and expects the watchdog to
|
||||||
# roll back and journal a 'rollback' event, then restarts webui.
|
# roll back and journal a 'rollback' event, then restarts webui.
|
||||||
# Preceded by a 5-second warning countdown; brief web outage.
|
# Preceded by a 5-second warning countdown; brief web outage.
|
||||||
|
# Drills run only when every read-only check has passed.
|
||||||
#
|
#
|
||||||
# Environment:
|
# Environment:
|
||||||
# PASS webui password used for POST /api/login (check 7).
|
# 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.
|
# Exit status: 0 when every executed check passes, 1 otherwise.
|
||||||
|
|
||||||
@@ -42,9 +48,12 @@ JAR=/tmp/mk8-smoke-cookies.$$
|
|||||||
|
|
||||||
WRITE=0
|
WRITE=0
|
||||||
PASS="${PASS:-}"
|
PASS="${PASS:-}"
|
||||||
|
UPLINK_SSID="${SMOKE_UPLINK_SSID:-}"
|
||||||
|
UPLINK_PSK="${SMOKE_UPLINK_PSK:-}"
|
||||||
WEB_STOPPED=0
|
WEB_STOPPED=0
|
||||||
FAILED=0
|
FAILED=0
|
||||||
WAIT_RC=0
|
WAIT_RC=0
|
||||||
|
wp=""
|
||||||
|
|
||||||
PY="$(command -v python3 2>/dev/null || true)"
|
PY="$(command -v python3 2>/dev/null || true)"
|
||||||
[ -n "$PY" ] || PY=/usr/bin/python3
|
[ -n "$PY" ] || PY=/usr/bin/python3
|
||||||
@@ -53,14 +62,20 @@ PY="$(command -v python3 2>/dev/null || true)"
|
|||||||
usage() {
|
usage() {
|
||||||
printf 'Usage: smoke.sh [--write]\n'
|
printf 'Usage: smoke.sh [--write]\n'
|
||||||
printf '\n'
|
printf '\n'
|
||||||
printf 'Read-only checks run by default. --write adds two destructive\n'
|
printf 'Read-only checks run by default. --write adds destructive drills\n'
|
||||||
printf 'drills that briefly toggle UCI config and stop/start the\n'
|
printf '(run only if every read-only check passed) that briefly toggle\n'
|
||||||
printf 'pagerwebui service; original values are restored automatically.\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 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() {
|
on_exit() {
|
||||||
rm -f "$JAR" 2>/dev/null
|
rm -f "$JAR" 2>/dev/null
|
||||||
|
if [ -n "$wp" ]; then
|
||||||
|
kill "$wp" 2>/dev/null
|
||||||
|
fi
|
||||||
if [ "$WEB_STOPPED" = "1" ]; then
|
if [ "$WEB_STOPPED" = "1" ]; then
|
||||||
info 'restoring pagerwebui service'
|
info 'restoring pagerwebui service'
|
||||||
"$WEBUI_INIT" start >/dev/null 2>&1
|
"$WEBUI_INIT" start >/dev/null 2>&1
|
||||||
@@ -291,6 +306,67 @@ drill_bad_values() {
|
|||||||
"$PY" "$REL/server.py" --reconcile >/dev/null 2>&1
|
"$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() {
|
drill_watchdog() {
|
||||||
WDOG="$REL/mk8-watchdog.sh"
|
WDOG="$REL/mk8-watchdog.sh"
|
||||||
[ -f "$WDOG" ] || WDOG="$LEGACY/mk8-watchdog.sh"
|
[ -f "$WDOG" ] || WDOG="$LEGACY/mk8-watchdog.sh"
|
||||||
@@ -399,9 +475,12 @@ main() {
|
|||||||
if [ "$WRITE" = "1" ]; then
|
if [ "$WRITE" = "1" ]; then
|
||||||
if [ -z "$PY" ]; then
|
if [ -z "$PY" ]; then
|
||||||
fail 'drills: python3 required but not found'
|
fail 'drills: python3 required but not found'
|
||||||
|
elif [ "$FAILED" -ne 0 ]; then
|
||||||
|
skip 'drills: read-only checks failed; refusing drills'
|
||||||
else
|
else
|
||||||
printf -- '--- --write drills ---\n'
|
printf -- '--- --write drills ---\n'
|
||||||
drill_bad_values
|
drill_bad_values
|
||||||
|
drill_role
|
||||||
drill_watchdog
|
drill_watchdog
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user