devices: Installations-Meilensteine/Fehlschlaege melden (device_installation_events)
Neue Tabelle (Migration 0025) + POST /api/v1/devices/{device_id}/
installation-events (device_fingerprint-authentifiziert statt Service-
Token - das Geraet hat waehrend der Installation noch kein agent_secret,
gleiches Vertrauensniveau wie /api/v1/activate) + GET-Gegenstueck
(Service-Token, fuer die Kundenplattform).
Nutzer-Wunsch (01.09.2026, nach dem ersten echten Hardware-Test):
Fehlschlaege waehrend der Installation sichtbar machen, ohne am
Bildschirm mitschreiben zu muessen ("das ist kein Flow"). Bewusst
getrennt von device_merkmal_events (andere Lebensphase, andere Auth,
kein Merkmal-Bezug) und zunaechst nur admin-/operatorseitig ausgewertet -
ob/wie umfangreich der Kunde das in seiner eigenen GUI sieht, ist noch
offen (Nutzer: "das können wir noch wann anders diskutieren").
This commit is contained in:
parent
fb87a2377a
commit
51fbf0fa0e
99
app.py
99
app.py
@ -87,6 +87,19 @@ class ResolveRequest(BaseModel):
|
||||
device_id: str
|
||||
|
||||
|
||||
class InstallationEventRequest(BaseModel):
|
||||
"""
|
||||
Meilenstein/Fehlschlag-Meldung vom Boot-Medium waehrend der Installation
|
||||
(siehe device_installation_events, Migration 0025) - das Geraet hat zu
|
||||
diesem Zeitpunkt noch kein agent_secret, deshalb device_fingerprint als
|
||||
Nachweis statt eines Service-Tokens (gleiches Vertrauensniveau wie
|
||||
/api/v1/activate selbst).
|
||||
"""
|
||||
device_fingerprint: str
|
||||
event_type: str
|
||||
detail: str | None = None
|
||||
|
||||
|
||||
class AgentBootstrapRequest(BaseModel):
|
||||
device_id: str
|
||||
|
||||
@ -1617,6 +1630,55 @@ def fetch_device_events(device_id: str):
|
||||
for occurred_at, merkmal_key, merkmal_name, event_type, detail in cur.fetchall()
|
||||
]
|
||||
|
||||
def device_fingerprint_matches(device_id: str, device_fingerprint: str) -> bool:
|
||||
with get_database_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT device_fingerprint FROM devices WHERE id = %s",
|
||||
(device_id,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
|
||||
return row is not None and row[0] == device_fingerprint
|
||||
|
||||
def record_installation_event(device_id: str, event_type: str, detail: str | None) -> None:
|
||||
with get_database_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO device_installation_events (id, device_id, event_type, detail)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
""",
|
||||
(uuid4(), device_id, event_type, detail),
|
||||
)
|
||||
|
||||
def fetch_installation_events(device_id: str):
|
||||
"""
|
||||
Meilensteine/Fehlschlaege waehrend der Installation (siehe
|
||||
device_installation_events, Migration 0025) - separat von
|
||||
fetch_device_events() (Merkmal-Anwendungen), neueste zuerst.
|
||||
"""
|
||||
|
||||
with get_database_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT occurred_at, event_type, detail
|
||||
FROM device_installation_events
|
||||
WHERE device_id = %s
|
||||
ORDER BY occurred_at DESC
|
||||
""",
|
||||
(device_id,),
|
||||
)
|
||||
return [
|
||||
{
|
||||
"occurred_at": occurred_at.isoformat(),
|
||||
"event_type": event_type,
|
||||
"detail": detail,
|
||||
}
|
||||
for occurred_at, event_type, detail in cur.fetchall()
|
||||
]
|
||||
|
||||
def deprovision_device(device_id: str) -> bool:
|
||||
"""
|
||||
Serverseitiger Entzug (siehe Kundenportal-Geräteaktionen-Plan): macht das
|
||||
@ -2866,6 +2928,43 @@ def get_device_events(device_id: str, authorization: str | None = Header(default
|
||||
return {"success": True, "events": fetch_device_events(device_id)}
|
||||
|
||||
|
||||
@app.post("/api/v1/devices/{device_id}/installation-events")
|
||||
def post_installation_event(device_id: str, payload: InstallationEventRequest):
|
||||
"""
|
||||
Vom Boot-Medium selbst waehrend der Installation aufgerufen (siehe
|
||||
scripts/lib/reporting.sh in tuxflotte-installer) - das Geraet hat zu
|
||||
diesem Zeitpunkt noch kein agent_secret, deshalb kein Service-Token-
|
||||
Zwang wie bei den uebrigen Endpunkten. device_fingerprint dient als
|
||||
Nachweis, gleiches Vertrauensniveau wie /api/v1/activate selbst -
|
||||
bewusst kein neues Token-System fuer einen reinen Diagnose-Kanal.
|
||||
Rein melde-basiert (best effort auf Aufruferseite) - ein Fehlschlag
|
||||
hier darf die eigentliche Installation nie aufhalten.
|
||||
"""
|
||||
|
||||
if not device_fingerprint_matches(device_id, payload.device_fingerprint):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "unauthorized",
|
||||
"message": "Geräte-Kennung/Fingerprint stimmen nicht überein.",
|
||||
}
|
||||
|
||||
record_installation_event(device_id, payload.event_type, payload.detail)
|
||||
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@app.get("/api/v1/devices/{device_id}/installation-events")
|
||||
def get_installation_events(device_id: str, authorization: str | None = Header(default=None)):
|
||||
if not require_service_token(authorization):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "unauthorized",
|
||||
"message": "Fehlendes oder ungültiges Service-Token.",
|
||||
}
|
||||
|
||||
return {"success": True, "events": fetch_installation_events(device_id)}
|
||||
|
||||
|
||||
@app.post("/api/v1/devices/{device_id}/deprovision")
|
||||
def deprovision(device_id: str, authorization: str | None = Header(default=None)):
|
||||
if not require_service_token(authorization):
|
||||
|
||||
25
migrations/0025_device_installation_events.sql
Normal file
25
migrations/0025_device_installation_events.sql
Normal file
@ -0,0 +1,25 @@
|
||||
BEGIN;
|
||||
|
||||
-- Installations-Meilensteine/Fehlschlaege vom Boot-Medium selbst gemeldet -
|
||||
-- andere Lebensphase als device_merkmal_events (laeuft schon waehrend der
|
||||
-- Installation, bevor das Geraet ein agent_secret hat) und andere Datenform
|
||||
-- (kein merkmal_id-Bezug), deshalb eigene Tabelle statt Wiederverwendung.
|
||||
-- Nutzer-Wunsch (01.09.2026, nach dem ersten echten Hardware-Test):
|
||||
-- Fehlschlaege waehrend der Installation sichtbar machen, ohne am
|
||||
-- Bildschirm mitschreiben/-fotografieren zu muessen. Bewusst zunaechst nur
|
||||
-- admin-/operatorseitig ausgewertet (siehe kundenplattform) - ob und wie
|
||||
-- umfangreich das dem Kunden selbst gezeigt wird, ist noch offen.
|
||||
CREATE TABLE device_installation_events (
|
||||
id UUID PRIMARY KEY,
|
||||
device_id UUID NOT NULL
|
||||
REFERENCES devices(id)
|
||||
ON DELETE CASCADE,
|
||||
event_type TEXT NOT NULL,
|
||||
detail TEXT,
|
||||
occurred_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_device_installation_events_device_id
|
||||
ON device_installation_events(device_id);
|
||||
|
||||
COMMIT;
|
||||
Loading…
x
Reference in New Issue
Block a user