Files
bf-infra/switch/recovery/flash-and-recover.sh

137 lines
6.3 KiB
Bash

#!/bin/sh
# Autonome Flash- + Recovery-Orchestrierung für ALL-SG8316M.
# LÄUFT AUF DEM MAC MINI (10.40.41.167, en0, L2-lokal zum Switch).
# Zweck: Claude stößt es detached an und ist während des Switch-Reboots OFFLINE.
# Das Script zieht die ganze Kette autonom durch und loggt alles.
#
# Kette: pre-flight -> flash -> warte down -> warte up(.1.1|.239)
# -> (falls .1.1) IP zurück auf .239 [cmd dynamisch entdeckt]
# -> VLANs (best-effort) -> save-to-flash -> verify
#
# Aufruf: ./flash-and-recover.sh preflight # nur Checks, kein Flash
# ./flash-and-recover.sh run # echter Flash + Recovery
set -u
SW_PROD=10.40.41.239
SW_DEFAULT=192.168.1.1
PROD_MASK=255.255.255.0
PROD_GW=10.40.41.254
ALIAS_IP=192.168.1.2
VLANS=10,20,30,40,100
IMG="${IMG:-$HOME/bf-switch-flash/all-sg8316m-version_2.2.1_vmlinux.bix}"
IMG_SHA=e3f160983e8b17962dc70a52360b7f560c92da8197928d702f5266027a0fc7fc
LOG="${LOG:-$HOME/bf-switch-flash/flash-$(date +%Y%m%d-%H%M%S).log}"
MODE="${1:-preflight}"
mkdir -p "$(dirname "$LOG")"
log(){ printf '%s %s\n' "$(date '+%H:%M:%S')" "$*" | tee -a "$LOG"; }
# Liveness: Webserver-Root antwortet (firmware-stabiler als ein bestimmter cmd)
http_code(){ curl -s -o /dev/null -w '%{http_code}' --max-time 5 "http://$1/" 2>/dev/null; }
up(){ [ "$(http_code "$1")" = "200" ]; }
fw_version(){ curl -s --max-time 6 "http://$1/cgi-bin/dispatcher.cgi?cmd=512" \
| sed -e 's/<[^>]*>/ /g' | grep -oiE 'Firmware Version[^0-9]*[0-9.]+' | head -1; }
wait_up(){ _e=$(( $(date +%s) + $2 )); while [ "$(date +%s)" -lt "$_e" ]; do up "$1" && return 0; sleep 4; done; return 1; }
wait_down(){ _e=$(( $(date +%s) + $2 )); while [ "$(date +%s)" -lt "$_e" ]; do up "$1" || return 0; sleep 4; done; return 1; }
# IP zurücksetzen — cmd + Seite dynamisch aus der Live-UI entdecken (firmware-drift-sicher)
restore_ip(){
_host=$1
_menu=$(curl -s --max-time 8 "http://$_host/cgi-bin/dispatcher.cgi?cmd=2")
_ipcmd=$(printf '%s' "$_menu" | grep -i 'IP Address' | grep -oE 'cmd=[0-9]+' | head -1 | cut -d= -f2)
[ -z "$_ipcmd" ] && _ipcmd=517
_form=$(curl -s --max-time 8 "http://$_host/cgi-bin/dispatcher.cgi?cmd=$_ipcmd")
_post=$(printf '%s' "$_form" | grep -i 'name="cmd"' | grep -oE 'value="[0-9]+"' | head -1 | grep -oE '[0-9]+')
[ -z "$_post" ] && _post=518
log "restore_ip: ip-page=$_ipcmd post-cmd=$_post -> set $SW_PROD"
curl -s --max-time 10 -X POST "http://$_host/cgi-bin/dispatcher.cgi" \
--data "mode=0" --data "ip=$SW_PROD" --data "netmask=$PROD_MASK" \
--data "gateway=$PROD_GW" --data "cmd=$_post" >/dev/null 2>&1
}
add_vlans(){ curl -s --max-time 10 -X POST "http://$1/cgi-bin/dispatcher.cgi" \
--data "vlanlist=$VLANS" --data "vlanAction=0" --data "name=" --data "cmd=1284" >/dev/null 2>&1; }
save_flash(){ curl -s --max-time 15 "http://$1/cgi-bin/dispatcher.cgi?cmd=4358" >/dev/null 2>&1; }
# ---------- PRE-FLIGHT ----------
log "=== PRE-FLIGHT (mode=$MODE) ==="
fail=0
if [ -f "$IMG" ]; then
h=$(shasum -a 256 "$IMG" 2>/dev/null | awk '{print $1}')
if [ "$h" = "$IMG_SHA" ]; then log "OK image sha256"; else log "FAIL image sha256: $h"; fail=1; fi
else log "FAIL image missing: $IMG"; fail=1; fi
if ifconfig en0 2>/dev/null | grep -q "$ALIAS_IP"; then log "OK recovery-alias $ALIAS_IP on en0";
else log "FAIL alias $ALIAS_IP missing -> sudo ifconfig en0 alias $ALIAS_IP 255.255.255.0"; fail=1; fi
if up "$SW_PROD"; then log "OK switch up at $SW_PROD (FW: $(fw_version "$SW_PROD"))";
else log "FAIL switch not reachable at $SW_PROD"; fail=1; fi
if [ "$fail" -ne 0 ]; then log "PRE-FLIGHT FAILED -> abort"; exit 2; fi
log "PRE-FLIGHT OK"
[ "$MODE" = "preflight" ] && { log "preflight-only -> done (kein Flash)"; exit 0; }
# ---------- FLASH ----------
MARKER="$HOME/bf-switch-flash/.flashed-2.2.1"
if [ -f "$MARKER" ] && up "$SW_PROD"; then
log "Marker vorhanden + Switch up @ $SW_PROD -> Flash übersprungen (nur reconfig)"; skip=1
else skip=0; fi
if [ "$skip" -eq 0 ]; then
log "=== FLASH: lade Image hoch ($IMG) ==="
curl -s --max-time 200 -F "type=0" -F "http_file=@$IMG" -F "cmd=4364" \
"http://$SW_PROD/cgi-bin/httpupload.cgi" >>"$LOG" 2>&1
log "Upload abgeschlossen, warte auf Reboot (down)..."
# Der Reboot selbst ist das verlässliche Flash-Signal (Flash erzwingt Neustart).
if wait_down "$SW_PROD" 180; then
log "Switch down (Reboot) — Flash hat gegriffen"
else
log "FAIL kein Reboot binnen 180s — Flash vermutlich nicht angenommen (Upload/Format/Auth?)."
log " Switch unverändert @ $SW_PROD, Netz intakt, KEIN Reset. Abbruch."
exit 3
fi
fi
# ---------- WARTEN AUF WIEDERKEHR ----------
log "=== RECOVERY: warte auf Switch (max 6 min, .1.1 ODER .239) ==="
target=""; _e=$(( $(date +%s) + 360 ))
while [ "$(date +%s)" -lt "$_e" ]; do
if up "$SW_DEFAULT"; then target=$SW_DEFAULT; break; fi
if up "$SW_PROD"; then target=$SW_PROD; break; fi
sleep 4
done
if [ -z "$target" ]; then
log "FAIL Switch kam binnen 6min nicht auf $SW_DEFAULT noch $SW_PROD zurück."
log " -> MANUELLE Recovery nötig (Reset-Knopf 10s, dann erneut)."
exit 4
fi
log "Switch zurück an $target"
# ---------- IP WIEDERHERSTELLEN ----------
if [ "$target" = "$SW_DEFAULT" ]; then
log "Switch auf Default $SW_DEFAULT -> stelle $SW_PROD wieder her"
restore_ip "$SW_DEFAULT"
if wait_up "$SW_PROD" 60; then
log "OK Switch wieder auf $SW_PROD"
else
log "FAIL set-ip griff nicht — FW 2.2.1 erzwingt vermutlich Login (FW1.0 war offen)."
log " LAN/Internet laufen TROTZDEM (L2-Forwarding). Nur die Mgmt-IP hängt auf $SW_DEFAULT."
log " MANUELL: Browser http://$SW_DEFAULT Login admin/admin -> Network/IP Address"
log " -> $SW_PROD Maske $PROD_MASK GW $PROD_GW -> Save to Flash."
exit 5
fi
else
log "Switch hat $SW_PROD behalten (Firmware bewahrte IP)"
fi
# ---------- RECONFIG (best-effort, unkritisch — online nachholbar) ----------
log "=== reconfig (best-effort) ==="
if add_vlans "$SW_PROD"; then log "VLANs $VLANS abgeschickt"; else log "WARN VLAN-Add fehlgeschlagen (online nachholen)"; fi
if save_flash "$SW_PROD"; then log "save-to-flash abgeschickt"; else log "WARN save fehlgeschlagen (online nachholen!)"; fi
# ---------- VERIFY ----------
if up "$SW_PROD"; then
touch "$HOME/bf-switch-flash/.flashed-2.2.1"
log "=== SUCCESS: Switch up @ $SW_PROD (Marker gesetzt) ==="
exit 0
else
log "=== WARN: Switch am Ende nicht bestätigt up ==="
exit 6
fi