diff --git a/.gitignore b/.gitignore index fcb2d1b..2f136e5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ node_modules/ __pycache__/ deploy-webhook.log .phpunit.result.cache +monitor-api.conf +credentials.conf diff --git a/README.md b/README.md index 7c5819f..8fcf4f1 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,8 @@ die dazugehörigen Klärungen in [Detailfragen.md](Detailfragen.md) und - `api/` — REST-API (PHP, SQLite), siehe [api/README.md](api/README.md) - `html/` — Frontend: `office.html` (Büro-Visualisierung), `js/`, `css/` - `tests/frontend/` — Node-Tests für die Frontend-Layoutlogik (`node --test`) -- `check_fds.sh`, `mail_fds.py`, `hosts-sample.conf` — bestehende Ping-Überwachung +- `check_fds.sh`, `mail_fds.py`, `hosts-sample.conf`, `monitor-api-sample.conf` — bestehende + Ping-Überwachung (letztere: optionale Zugangsdaten für die Meldung per REST) - `docker-compose.yml` — komplette Entwicklungsumgebung (siehe unten) - `update.sh` — holt die neueste Version vom konfigurierten Git-Remote und aktualisiert die Docker-Compose-Umgebung (Abhängigkeiten, Images, Dienste, Tests) @@ -104,6 +105,12 @@ kein System gemeldet wurde — dafür gibt es zwei unabhängige Wege: 4. Die API liest `status.txt` laufend mit und zeigt die Einträge als Systeme (`source: "legacy"`) im System-Monitor an — ohne dass etwas neu gestartet werden muss. +> ⚠️ Dieser Import liest `MONITOR_STATUS_FILE` als **lokale Datei**. Läuft `check_fds.sh` auf +> einem anderen Rechner als die API (oder liegt `status.txt` außerhalb des API-Containers — +> der Mount in `docker-compose.yml` ist standardmäßig auskommentiert), erreicht sie die API +> nie und der System-Monitor bleibt bei „Keine Systeme gemeldet". Fehlt die Datei, meldet die +> API keinen Fehler, sondern liefert eine leere Liste. In dem Fall Weg 3 verwenden. + **Weg 2: Status direkt per REST-API melden** Eigene Server/Skripte können ihren Status auch ohne `hosts.conf` melden: @@ -121,4 +128,21 @@ Eigene Server/Skripte können ihren Status auch ohne `hosts.conf` melden: So gemeldete Systeme (`source: "api"`) haben Vorrang vor dem Legacy-Import aus `status.txt` und werden von ihm nicht überschrieben. Details zu Feldern/Auth: [API.md](API.md#systeme). +**Weg 3: Ping-Überwachung, die ihre Ergebnisse selbst per REST meldet** + +Kombiniert beide Wege und ist der richtige Weg, wenn der Ping-Check auf einem anderen Rechner +läuft als die API — dann fällt der Umweg über die Datei `status.txt` weg: + +1. Wie in Weg 1 die `hosts.conf` pflegen und `check_fds.sh` per Cron laufen lassen. +2. Wie in Weg 2 einen Agenten registrieren (Rolle `user`, damit er keinen Schreibtisch in der + Büro-Visualisierung belegt). +3. `monitor-api-sample.conf` nach `monitor-api.conf` neben `check_fds.sh` kopieren, dort + `MONITOR_API_BASE` und `MONITOR_API_KEY` eintragen und die Datei auf `chmod 600` setzen. + +`check_fds.sh` meldet dann nach jedem Ping zusätzlich per REST (`online` → `ok`, +`offline` → `error`) und schreibt `status.txt` unverändert weiter für den alten Wandmonitor. +Die System-ID entsteht dabei aus dem Hostnamen (`Nextcloud(intern)` → `nextcloud-intern`), +der Originalname bleibt als Anzeigename erhalten. Ist die API nicht erreichbar, läuft die +Ping-Überwachung normal weiter und protokolliert nur eine Warnung. + diff --git a/check_fds.sh b/check_fds.sh index 440ed3d..b0fa4ca 100755 --- a/check_fds.sh +++ b/check_fds.sh @@ -1,11 +1,78 @@ #!/bin/bash +# Pfade relativ zum Skript aufloesen, nicht zum Arbeitsverzeichnis: der Cronjob startet +# das Skript zwar mit "cd", ein Aufruf von aussen laese sonst aber die falsche hosts.conf +# (oder gar keine). +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # Pfad zur Textdatei mit Hostname-IP-Paaren (pro Zeile: hostname ip) -HOST_FILE="hosts.conf" +HOST_FILE="$SCRIPT_DIR/hosts.conf" # Pfad zum mail_fds.py-Skript -MAIL_SCRIPT="mail_fds.py" +MAIL_SCRIPT="$SCRIPT_DIR/mail_fds.py" # Pfad zur Statusdatei STATUS_FILE="/var/www/html/status.txt" +# Zugangsdaten fuer die Statusmeldung per REST (siehe monitor-api-sample.conf) +API_CONF="$SCRIPT_DIR/monitor-api.conf" + +# Die Meldung per REST ist optional: fehlt monitor-api.conf oder ist sie leer, schreibt der +# Check wie bisher nur status.txt und mailt bei Ausfall. Noetig ist sie dort, wo Ping-Check +# und API auf verschiedenen Rechnern laufen - der Legacy-Import aus status.txt (F6) liest +# eine lokale Datei und erreicht die API dann nicht. +MONITOR_API_BASE="" +MONITOR_API_KEY="" +if [ -f "$API_CONF" ]; then + # shellcheck source=/dev/null + . "$API_CONF" +fi + +# Erzeugt aus dem Hostnamen eine URL-sichere System-ID. Der Router der API matcht auf dem +# rohen, nicht dekodierten Pfad (api/src/Http/Router.php) - eine prozentkodierte ID landete +# also kodiert in der Datenbank. Der lesbare Originalname geht im Feld "name" mit. +system_id() { + local slug + slug=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' \ + | sed -e 's/[^a-z0-9]\+/-/g' -e 's/^-//' -e 's/-$//') + printf '%s' "${slug:-unbekannt}" +} + +json_escape() { + printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' +} + +# Meldet einen Host per POST /api/v1/systems/{id}/status. Fehler werden nur protokolliert: +# eine unerreichbare API darf die Ping-Ueberwachung nicht anhalten. +report_to_api() { + local hostname="$1" + local ip="$2" + local status="$3" + + if [ -z "$MONITOR_API_BASE" ] || [ -z "$MONITOR_API_KEY" ]; then + return 0 + fi + + local api_status="error" + if [ "$status" = "online" ]; then + api_status="ok" + fi + + local payload + payload=$(printf '{"status":"%s","name":"%s","type":"host","detail":"%s"}' \ + "$api_status" "$(json_escape "$hostname")" "$(json_escape "$ip")") + + local code + if ! code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 \ + -X POST "${MONITOR_API_BASE%/}/api/v1/systems/$(system_id "$hostname")/status" \ + -H "Content-Type: application/json" \ + -H "X-Api-Key: ${MONITOR_API_KEY}" \ + -d "$payload"); then + echo " Warnung: API unter $MONITOR_API_BASE nicht erreichbar, $hostname nicht gemeldet." + return 0 + fi + + if [ "$code" != "200" ]; then + echo " Warnung: API antwortet mit HTTP $code auf die Meldung fuer $hostname." + fi +} # Funktion zum Prüfen einer IP und Schreiben des Status check_ip() { @@ -26,11 +93,13 @@ check_ip() { if [ "$success_count" -lt 2 ]; then status="offline" echo " Mehr als ein Ping fehlgeschlagen für $hostname ($ip). Führe $MAIL_SCRIPT aus." - ./"$MAIL_SCRIPT" --subject "Ping-Fehler bei $hostname $ip" --credentials credentials.conf + "$MAIL_SCRIPT" --subject "Ping-Fehler bei $hostname $ip" --credentials "$SCRIPT_DIR/credentials.conf" else echo " $hostname ($ip) ist erreichbar." fi + report_to_api "$hostname" "$ip" "$status" + # Status in status.conf schreiben/aktualisieren local now=$(date +"%Y-%m-%d %H:%M") awk -v h="$hostname" -v i="$ip" -v s="$status" -v d="$now" ' @@ -64,7 +133,9 @@ fi # Statusdatei anlegen, falls nicht vorhanden touch "$STATUS_FILE" -while read -r line; do +# "|| [ -n "$line" ]" nimmt auch eine letzte Zeile ohne abschliessenden Zeilenumbruch mit - +# ohne sie verschluckt read den zuletzt angehaengten Host stillschweigend. +while read -r line || [ -n "$line" ]; do case "$line" in \#*|"") continue ;; esac diff --git a/monitor-api-sample.conf b/monitor-api-sample.conf new file mode 100644 index 0000000..35c82c5 --- /dev/null +++ b/monitor-api-sample.conf @@ -0,0 +1,20 @@ +# Zugangsdaten, mit denen check_fds.sh die Ping-Ergebnisse zusaetzlich per REST an die +# monitor-API meldet (POST /api/v1/systems/{id}/status, siehe API.md Abschnitt "Systeme"). +# +# Noetig ist das, wenn check_fds.sh auf einem anderen Rechner laeuft als die API: der +# Legacy-Import liest status.txt als lokale Datei und erreicht sie dann nicht. +# +# Diese Datei nach "monitor-api.conf" neben check_fds.sh kopieren und ausfuellen. Fehlt sie +# oder bleibt ein Wert leer, meldet check_fds.sh wie bisher nur nach status.txt und per Mail. +# +# Der Key gehoert einem eigens registrierten Agenten der Rolle "user" - die bekommt nie einen +# Schreibtisch in der Buero-Visualisierung (AgentRepository::register). Anlegen mit +# register-agent.sh; der Key wird nur bei der Registrierung einmalig ausgegeben. +# +# Enthaelt ein Secret: chmod 600 monitor-api.conf + +# Basis-URL der API, ohne abschliessenden Slash (Host-Port des api-Service) +MONITOR_API_BASE="http://monitor.example.lan:8091" + +# API-Key des registrierten Agenten +MONITOR_API_KEY=""