platform-docs/adr/0021-agent-intervall-debug-modus-health-monitoring.md
2026-08-26 10:07:15 +02:00

5.6 KiB
Raw Permalink Blame History

ADR-0021: Agent-Standardintervall 6h+Jitter, Debug-Modus, Health-Monitoring

Status: Beschlossen Datum: 26.08.2026

Kontext

Der Provisioning-Agent (provisioning-agent/agent.py) checkte bislang alle 5 Minuten ein (AGENT_POLL_INTERVAL_SECONDS, fest über eine Server-Umgebungsvariable) — sinnvoll für die Entwicklungsphase, aber bei einer wachsenden Geräteflotte unnötig häufig für den Normalbetrieb. Es gab außerdem keine Möglichkeit, ein einzelnes Gerät gezielt kurzfristig engmaschiger abzufragen (z.B. um eine frisch zugewiesene Rolle zeitnah zu beobachten), ohne das Intervall global zu ändern. Und: der Agent sammelte nur einmalig bei der Installation eine statische Hardware-Inventur (hardware_snapshots, siehe 10_hardware.sh) — es gab keine periodische Sicht auf den aktuellen Gerätezustand (Diskbelegung, Auslastung, Uptime).

Der Boot-Checkin selbst (Agent meldet sich direkt beim Start) war entgegen der ursprünglichen Annahme bereits vorhanden — agent.py:main() ruft run_once() grundsätzlich vor der ersten sleep()-Anweisung auf, daher hier nicht angefasst.

Entscheidung

Standardintervall: AGENT_POLL_INTERVAL_SECONDS-Default von 300 auf 21600 (6 Stunden) angehoben. Jitter kommt bewusst aus dem Agenten selbst, nicht vom Server (der kennt keinen sinnvollen Zufallszustand pro Gerät): agent.pys main()-Loop addiert 030 Minuten zufällig auf das zurückgelieferte Intervall, aber nur wenn es „groß" ist (JITTER_THRESHOLD_SECONDS = 3600) — der kurze Debug-Modus (60s) und der Fallback-Retry bei Fehlern (FALLBACK_INTERVAL_SECONDS = 60) bleiben bewusst unverjittert, sonst würde Jitter genau den Zweck des schnellen Debug-Zyklus untergraben.

Debug-Modus: neue Spalte devices.debug_mode_until (Migration 0022_agent_debug_mode_und_health.sql) ist der einzige Zustand — kein separates Enum, kein Cron-Job zum Zurücksetzen. agent_checkin() vergleicht bei jeder Anfrage debug_mode_until mit der aktuellen Zeit und liefert poll_interval_seconds entsprechend (60s im Debug-Modus, sonst 6h) — das automatische Zurückfallen nach Ablauf passiert rein durch Zeitvergleich, ohne dass irgendetwas aktiv zurückgesetzt werden muss. Aktivieren (POST /api/v1/devices/{id}/debug-mode, setzt now()+6h) und Deaktivieren (DELETE, setzt NULL) sind eigene, dünne Endpunkte, gegen require_service_token() wie üblich — Organisationszugehörigkeitsprüfung bleibt Aufgabe der aufrufenden Kundenplattform (find_device_in_organization()), nicht dieses Endpunkts. Bewusst kein zusätzliches Rechte-Gate: jeder eingeloggte Nutzer der eigenen Organisation darf den Debug-Modus für die eigenen Geräte umschalten, gleiches Muster wie die bestehenden Auftragskatalog-Aktionen.

Health-Monitoring: sechs neue, nullable devices-Spalten (health_disk_used_percent, health_ram_used_percent, health_uptime_seconds, health_load_1m/5m/15m, health_collected_at) — halten bewusst nur den jeweils letzten Stand, keine wachsende Historie (konsistent mit dem Plattenplatz-Bewusstsein an anderer Stelle, siehe iso_builds-Aufräumung). Der Agent sammelt die Werte bei jedem Checkin rein über Python-Stdlib (shutil.disk_usage("/"), /proc/meminfo, /proc/uptime, os.getloadavg()) — keine neue Abhängigkeit, jeder Einzelwert einzeln per try/except, damit ein fehlschlagender Wert (z.B. Load Average auf einem ungewöhnlichen System) die übrigen nicht verhindert. Bewusst ausgeschlossen: Temperatur/SMART-Werte — hätten zusätzliche Pakete/Berechtigungen gebraucht, war nicht Teil der 25./26.08.-Planung.

agent_checkin() schreibt agent_last_checkin und die Health-Felder in einem kombinierten UPDATE ... RETURNING debug_mode_until statt separater Round-Trips. Fehlt das health-Objekt in einem einzelnen Checkin (z.B. Sammel-Fehler im Agenten), bleiben die zuvor gespeicherten Health-Werte unverändert stehen statt auf NULL zurückzufallen — ein CASE WHEN pro Spalte statt eines bedingungslosen Überschreibens.

fetch_devices_for_organization()/fetch_all_devices() liefern debug_mode_until und health jetzt mit aus (gemeinsame SQL-Spaltenliste GERAETE_HEALTH_SPALTEN_SQL, gemeinsame Aufbereitung _health_felder_aus_row()) — Kundenplattform braucht dafür keinen zusätzlichen Request, das Device-Dict aus der ohnehin schon vorhandenen Besitz-Validierung (find_device_in_organization()) trägt die Felder direkt mit.

Kundenplattform-UI: neue Sektion „Debug-Modus" auf der Auftragskatalog-Seite (Kunden- und Admin-Bereich, gleiches zurueck_url-Präfix-Muster wie die bestehenden Select/Deselect-Formulare aus ADR-0015), neue Sektion „Aktueller Zustand (letzter Checkin)" auf der Hardware-Seite (klar getrennt von der bestehenden statischen Install-Zeit-Inventur), Debug-Badge in beiden Geräte-Listen, und „Zuletzt gemeldet" jetzt auch auf der Log-Seite (vorher nur in der Geräteliste sichtbar, nicht in der Detail-Historie).

Konsequenzen

Migration 0022_agent_debug_mode_und_health.sql (8 neue devices-Spalten, alle nullable, kein Backfill nötig). Kein neuer Cron/Scheduler-Prozess für den Debug-Modus-Rückfall — reine Zeitvergleichslogik im bestehenden Checkin-Pfad.

Live auf anode verifiziert: agent_checkin() mit Health-Payload durchlaufen, debug_mode_until korrekt in poll_interval_seconds übersetzt (Debug-Endpunkt aktiviert → nächster Checkin liefert 60s, nach Ablauf/Deaktivieren wieder 21600s). collect_health() gegen ein echtes Linux-System (enterprise) getestet, liefert plausible Werte ohne Zusatzpaket. UI-Änderungen lokal mit gemocktem Kontext gerendert (Debug-Toggle in beiden Zuständen, Health-Sektion mit/ohne Daten, Log-Seite mit Zeitstempel).