#!/bin/bash # ============================================================================ # PoE Device Check Script # - prüft Erreichbarkeit von Geräten # - startet PoE-Port bei Ausfall neu # - loggt Ereignisse # - löscht alte Logfiles (>30 Tage) # ============================================================================ LOG_DIR="/var/log" LOGFILE="$LOG_DIR/rpi-$(date '+%Y%m%d%H%M%S').log" # Alte Logfiles löschen (älter als 30 Tage) find "$LOG_DIR" -type f -name "rpi-*.log" -mtime +30 -delete # Intervall aus DB abrufen SLEEP=$(python3 - <<'END' import sqlite3 conn = sqlite3.connect("/srv/poe_manager/sqlite.db") row = conn.execute("SELECT value FROM settings WHERE key='check_interval'").fetchone() conn.close() print(row[0] if row else 300) END ) SLEEP=${SLEEP:-300} # disable_poe()/enable_poe() geben jetzt den TATSÄCHLICHEN Erfolg als # Exit-Code zurück (0 = Switch erreicht UND Kommando bestätigt, 1 = # fehlgeschlagen), statt blind anzunehmen, dass ein SSH-Verbindungsversuch # immer klappt. Jeder kritische expect-Schritt (Verbindungsaufbau, # configure-terminal-Prompt, Interface-Prompt, Bestätigung des # power-over-ethernet-Kommandos) hat dafür einen eigenen timeout-Zweig, # der explizit mit "exit 1" abbricht -- vorher lief das expect-Skript bei # einem unerreichbaren Switch einfach durch alle Timeouts durch und endete # trotzdem mit Exit-Code 0, wodurch der Aufrufer fälschlich "PoE # deaktiviert/aktiviert" geloggt hat, obwohl am Switch nichts passiert war. function disable_poe() { local switch_ip=$1 local switch_port=$2 local username=$3 local password=$4 local ssh_port=${5:-22} expect < $" { } timeout { exit 1 } eof { exit 1 } } send "configure terminal\r" expect { "(config)#" { } timeout { exit 1 } } send "interface $switch_port\r" expect { "(eth-$switch_port)#" { } timeout { exit 1 } } send "no power-over-ethernet\r" expect { "(eth-$switch_port)#" { } timeout { exit 1 } } send "exit\r" expect "(config)#" send "exit\r" expect "#" send "exit\r" expect ">"; send "exit\r" expect "Do you want to log out (y/n)?" { send "y\r" } expect eof exit 0 EOF } function enable_poe() { local switch_ip=$1 local switch_port=$2 local username=$3 local password=$4 local ssh_port=${5:-22} expect < $" { } timeout { exit 1 } eof { exit 1 } } send "configure terminal\r" expect { "(config)#" { } timeout { exit 1 } } send "interface $switch_port\r" expect { "(eth-$switch_port)#" { } timeout { exit 1 } } send "power-over-ethernet\r" expect { "(eth-$switch_port)#" { } timeout { exit 1 } } send "exit\r" expect "(config)#" send "exit\r" expect "#" send "exit\r" expect ">"; send "exit\r" expect "Do you want to log out (y/n)?" { send "y\r" } expect eof exit 0 EOF } # Ein Geräte-Check (Ping + ggf. PoE-Neustart) als eigene Funktion, damit sie # sowohl vom manuellen Neustart (einzeln, synchron) als auch von der # Hauptschleife (parallel im Hintergrund, siehe unten) verwendet werden kann. function check_device() { local rpi_ip=$1 dev_name=$2 switch_ip=$3 switch_ssh_port=$4 local switch_hostname=$5 switch_port=$6 switch_user=$7 switch_pass=$8 if ping -c 1 -W 2 "$rpi_ip" &> /dev/null; then echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist erreichbar!" >> "$LOGFILE" return fi echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist nicht erreichbar!" >> "$LOGFILE" # Nur PoE neu starten, wenn dem Gerät auch tatsächlich ein Switch # zugeordnet ist (switch_ip) — ein reiner Port-Wert ohne Switch # (switch_ip leer) darf keinen SSH-Versuch mit leeren # Zugangsdaten auslösen. if [ -z "$switch_ip" ] || [ -z "$switch_port" ] || [ "$switch_port" == "None" ]; then return fi if ! disable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass" "$switch_ssh_port"; then echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name: Switch $switch_hostname nicht erreichbar oder Kommando abgelehnt — PoE-Deaktivierung fehlgeschlagen, kein Neustart durchgeführt." >> "$LOGFILE" return fi echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE auf Port $switch_port am Switch $switch_hostname deaktiviert." >> "$LOGFILE" sleep 2 if ! enable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass" "$switch_ssh_port"; then echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name: Switch $switch_hostname nicht erreichbar oder Kommando abgelehnt — PoE-Reaktivierung fehlgeschlagen! Port bleibt ggf. deaktiviert." >> "$LOGFILE" return fi echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE auf Port $switch_port am Switch $switch_hostname aktiviert." >> "$LOGFILE" } function manual_restart() { local target_mac="$1" python3 /srv/poe_manager/generate_ips.py | while IFS='|' read -r rpi_ip dev_name switch_ip switch_ssh_port switch_hostname switch_port switch_user switch_pass mac; do if [[ "$mac" != "$target_mac" ]]; then continue fi echo "$(date '+%Y-%m-%d %H:%M:%S') Manueller Neustart von $dev_name gestartet." >> "$LOGFILE" if [ -n "$switch_ip" ] && [ -n "$switch_port" ] && [ "$switch_port" != "None" ]; then if ! disable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass" "$switch_ssh_port"; then echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name: Switch $switch_hostname nicht erreichbar oder Kommando abgelehnt — manueller Neustart abgebrochen." >> "$LOGFILE" exit 1 fi echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE deaktiviert." >> "$LOGFILE" sleep 2 if ! enable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass" "$switch_ssh_port"; then echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name: Switch $switch_hostname nicht erreichbar oder Kommando abgelehnt — PoE-Reaktivierung fehlgeschlagen! Port bleibt ggf. deaktiviert." >> "$LOGFILE" exit 1 fi echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE aktiviert." >> "$LOGFILE" echo "$(date '+%Y-%m-%d %H:%M:%S') Manueller Neustart von $dev_name abgeschlossen." >> "$LOGFILE" else echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name besitzt keinen PoE-Port." >> "$LOGFILE" fi exit 0 done } if [[ "$1" == "restart" ]]; then if [[ -z "$2" ]]; then echo "MAC-Adresse fehlt." exit 1 fi manual_restart "$2" exit 0 fi echo "" > "$LOGFILE" while true; do echo "--------------------------------------------------------------------" >> "$LOGFILE" # Jedes Gerät wird als eigener Hintergrund-Job geprüft, statt strikt # nacheinander in einer einzigen Schleife -- bei einer größeren # Geräteliste (z.B. nach einem Import) hätte ein einzelnes bereits # ausgefallenes Gerät mit langsamem/nicht erreichbarem Switch (jeder # expect-Versuch bis zu ~5s Timeout je Verbindungsschritt) sonst die # Prüfung ALLER nachfolgenden Geräte verzögert. "< <(...)" (Process # Substitution) statt einer Pipe, damit die Hintergrund-Jobs Kinder # dieser Shell bleiben und "wait" sie unten tatsächlich erfassen kann # (bei "cmd | while ...; done" liefe die Schleife in einer eigenen # Subshell, deren Hintergrund-Jobs von außen nicht mehr sichtbar wären). while IFS='|' read -r rpi_ip dev_name switch_ip switch_ssh_port switch_hostname switch_port switch_user switch_pass mac; do check_device "$rpi_ip" "$dev_name" "$switch_ip" "$switch_ssh_port" "$switch_hostname" "$switch_port" "$switch_user" "$switch_pass" & done < <(python3 /srv/poe_manager/generate_ips.py) # Auf alle in diesem Durchlauf gestarteten Geräte-Checks warten, bevor # das Intervall abgewartet wird -- sonst könnten sich Durchläufe # überlappen (z.B. mehrere parallele expect-Sitzungen zum selben # Switch aus zwei verschiedenen, noch laufenden Zyklen gleichzeitig). wait sleep "$SLEEP" done