105 lines
5.6 KiB
Markdown
105 lines
5.6 KiB
Markdown
# 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.py`s `main()`-Loop addiert 0–30 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).
|