[BUG][Docgen] Agent-Daten fehlen in 78/78 VM-Dateien — falsches Dict + nicht existenter Key; Anreicherung nach services/guest_view.py extrahieren #212

Open
opened 2026-07-20 22:31:33 +00:00 by chinux · 1 comment
Owner

Befund

Im Ziel-Repo chinux/Dokumentation zeigen 78 von 78 generierten VM-Dateien | Agent | — |. Die einzige Datei mit Inhalt ist die handgepflegte _BEISPIEL. Der theProx-VM-Agent ist damit in der gesamten erzeugten Doku unsichtbar — obwohl er der Kern des Datenmodells ist: ohne Agent gibt es keine Disk-Auslastung, kein Docker, keine Updates, keine HTTP-Services.

Ursache

server/services/docgen.py, _vm_md() ab Zeile 244:

scan  = (ld.get("vm_qemu_scans") or {}).get(str(vmid)) or {}   # QEMU-Guest-Agent
gscan = (ld.get("vm_scans")      or {}).get(str(vmid)) or {}   # theProx-VM-Guest-Agent
...
f"| Agent | {scan.get('agent_version', '—')} |",

Zwei Fehler gleichzeitig:

  1. Falsches Dict. Gelesen wird aus vm_qemu_scans. Die Agent-Version steht in vm_scans.
  2. Falscher Key. agent_version existiert in last_data nirgends. Der Rohkey heisst version. agent_version entsteht erst beim Zusammenbauen der API-Antwort in deploy_router.py:351:
    entry["agent_version"] = scan.get("version") or None   # scan = vm_scans[vmid]
    

Beide Fehler zusammen machen das Feld deterministisch leer.

Gleiches Muster bei weiteren Feldern

IP-Adresse (41 von 79 Dateien leer): scan.get('ip_address') ist kein Rohfeld. _qemu_identity() in deploy_router.py:103 leitet es ab:

data["ip_address"] = (
    _primary_ipv4((qemu or {}).get("network", []), node.internal_network or node.ip_address or "")
    or (qemu or {}).get("ip_address") or ""
)

Docgen greift auf den Fallback-Zweig zu und verpasst die eigentliche Ableitung aus network[]. Fuer VM-Agent-VMs existiert zusaetzlich _vm_agent_ip(scan, node) — von Docgen gar nicht genutzt.

Prioritaet invertiert. deploy_router.py:343 dokumentiert die verbindliche Regel:

Wenn der theProx-VM-Agent installiert ist (version vorhanden), kommt die gesamte Identitaet vom Agent; der qemu-guest-agent dient nur noch als Ping (agent_available) und als Fallback je Feld.

Docgen macht das Gegenteil: scan.get('os_pretty') or gscan.get('os_pretty') — QEMU zuerst, Agent als Fallback. Die Doku kann damit andere Werte zeigen als das Dashboard fuer dieselbe VM.

Bewertung

Das ist kein Dreizeiler-Fix. Docgen hat eine zweite Anreicherungsschicht neben der korrekten in deploy_router.py aufgebaut und diese falsch nachimplementiert. Solange beide existieren, driften sie weiter auseinander. Die Anreicherung gehoert genau einmal ins System — das zahlt direkt auf #95 (Service-Extraktion aus deploy/admin/security_router) ein.


Prompt fuer Claude Code

Behebe die fehlerhafte Guest-Datenanreicherung im Docgen von theProx, indem du
die Anreicherungslogik aus deploy_router.py in einen gemeinsamen Service
extrahierst und BEIDE Aufrufer darauf umstellst.

--- 1. NEUER SERVICE ---
server/services/guest_view.py (neu). Enthaelt die aus deploy_router.py
HERAUSGELOESTEN (nicht kopierten!) reinen Funktionen:

    _qemu_scan_for, _qemu_identity, _primary_ipv4, _vm_agent_ip,
    _vm_agent_data, _proxmox_static, _ct_ip_from_config
    (Namen ohne fuehrenden Unterstrich exportieren)

Plus die neue zentrale Funktion:

    def build_guest_view(node, guest: dict, kind: str, ld: dict) -> dict

Sie liefert genau EIN normalisiertes Dict pro Guest mit mindestens:
    vmid, name, kind, status, cpus, maxmem, maxdisk,
    agent_version, agent_last_check, agent_available, vm_agent_connected,
    os_pretty, kernel, ip_address,
    disk[], docker, updates, http_services[], ports[], scanned_at

Die Prioritaetsregel wird HIER einmal implementiert und ist verbindlich:
  - Liegt ein VM-Agent-Scan mit "version" vor, gewinnt der Agent fuer
    os_pretty / kernel / ip_address; QEMU ist Fallback JE FELD.
  - Ohne Agent: QEMU-Identitaet.
  - ip_address IMMER ueber die Ableitung (_primary_ipv4 aus network[] bzw.
    _vm_agent_ip), NIE als roher Key-Zugriff.
  - agent_version kommt aus vm_scans[vmid]["version"] — NICHT aus
    vm_qemu_scans, und NICHT unter dem Namen "agent_version" (den gibt es im
    Rohdatenmodell nicht).

--- 2. AUFRUFER UMSTELLEN ---
a) deploy_router.py: die verschobenen Funktionen importieren statt lokal
   definieren. Die Schleife ab ~Zeile 298 auf build_guest_view() umstellen.
   Die API-Antwort muss BYTE-GLEICH bleiben — vorher/nachher-Vergleich gegen
   einen echten Node-Datensatz, das ist die Abnahmebedingung.
   _apply_card_override() bleibt wo es ist und wird NACH build_guest_view
   angewandt.
b) update_router.py:295 (gleiches agent_version-Muster) ebenfalls umstellen.
c) docgen.py: _vm_md() bekommt kuenftig das fertige View-Dict statt roher
   guest/scan/gscan-Dicts. Alle direkten Zugriffe auf ld["vm_scans"] /
   ld["vm_qemu_scans"] aus docgen.py ENTFERNEN — Docgen darf Rohdaten nicht
   mehr selbst interpretieren.

--- 3. DOCGEN-AUSGABE ---
Agent-Zeile ehrlich rendern, drei unterscheidbare Zustaende statt einem "—":

    | Agent | v1.4.2 (zuletzt 2026-07-19 03:12 UTC) |   # Agent laeuft
    | Agent | nicht installiert |                        # kein vm_scans-Eintrag
    | Agent | nicht erreichbar (QEMU-Agent antwortet) |   # agent_available true,
                                                         # aber kein VM-Agent

Der Unterschied zwischen "nicht installiert" und "keine Daten" ist fuer die
Doku wesentlich — heute sieht beides gleich aus.

Zusaetzlich im Frontmatter (siehe #209):
    agent: true|false
    agent_version: "…"        # nur wenn vorhanden
    agent_last_check: <ISO>   # nur wenn vorhanden

Und Tag "theprox/no-agent" fuer Guests ohne Agent, damit sie im Graphen und
in der Matrix sofort auffindbar sind. Ins _MOC.md eine Sektion
"Guests ohne Agent" aufnehmen — das ist eine Arbeitsliste, kein Schoenheits-
fehler.

--- 4. TESTS ---
server/tests/test_guest_view.py — mit ECHTEN, anonymisierten last_data-
Fixtures (Hostnamen/IPs ersetzt), nicht mit synthetischen Minimal-Dicts.
Genau diese Klasse Bug entsteht, wenn man gegen erfundene Strukturen testet.

  - VM MIT Agent: agent_version gefuellt, os/kernel/ip aus vm_scans
  - VM OHNE Agent, MIT QEMU: agent_version None, Identitaet aus vm_qemu_scans
  - VM ohne beides: alle Identitaetsfelder leer, kein Crash
  - Prioritaet: Agent liefert os_pretty, QEMU liefert ein anderes ->
    Agent gewinnt
  - Feld-Fallback: Agent hat kein kernel, QEMU schon -> QEMU-Kernel greift
  - ip_address wird aus network[] abgeleitet, nicht aus dem Rohkey
  - LXC: kein Agent-Konzept, ip aus der Proxmox-Config
  - Regressionstest: deploy_router-Antwort vor/nach dem Refactor identisch

--- 5. VERIFIKATION ---
Nach dem Fix einen Docgen-Lauf gegen echte Daten fahren und pruefen, dass die
Anzahl VM-Dateien mit gefuellter Agent-Zeile der Anzahl tatsaechlich
enrollter VMs entspricht. Dieses Verhaeltnis in die Task-Summary schreiben
("Agent-Daten: 54/79 Guests") — dann faellt ein Rueckfall sofort auf, statt
erst beim naechsten Lesen der Doku.

NICHT anfassen: Git-Push-Logik, Backups-Merge, Scheduler.

Definition of Done

  • services/guest_view.py existiert, deploy_router.py und update_router.py nutzen es
  • API-Antwort vor/nach Refactor nachweislich identisch
  • docgen.py greift nicht mehr direkt auf vm_scans / vm_qemu_scans zu
  • Agent-Zeile unterscheidet installiert / nicht installiert / nicht erreichbar
  • IP wird abgeleitet, nicht roh gelesen
  • Agent gewinnt gegenueber QEMU, Fallback je Feld
  • Tests laufen gegen anonymisierte echte Fixtures
  • Verhaeltnis Agent-Daten/Guests steht in der Task-Summary
## Befund Im Ziel-Repo `chinux/Dokumentation` zeigen **78 von 78** generierten VM-Dateien `| Agent | — |`. Die einzige Datei mit Inhalt ist die handgepflegte `_BEISPIEL`. Der theProx-VM-Agent ist damit in der gesamten erzeugten Doku unsichtbar — obwohl er der Kern des Datenmodells ist: ohne Agent gibt es keine Disk-Auslastung, kein Docker, keine Updates, keine HTTP-Services. ## Ursache `server/services/docgen.py`, `_vm_md()` ab Zeile 244: ```python scan = (ld.get("vm_qemu_scans") or {}).get(str(vmid)) or {} # QEMU-Guest-Agent gscan = (ld.get("vm_scans") or {}).get(str(vmid)) or {} # theProx-VM-Guest-Agent ... f"| Agent | {scan.get('agent_version', '—')} |", ``` Zwei Fehler gleichzeitig: 1. **Falsches Dict.** Gelesen wird aus `vm_qemu_scans`. Die Agent-Version steht in `vm_scans`. 2. **Falscher Key.** `agent_version` existiert in `last_data` nirgends. Der Rohkey heisst `version`. `agent_version` entsteht erst beim Zusammenbauen der API-Antwort in `deploy_router.py:351`: ```python entry["agent_version"] = scan.get("version") or None # scan = vm_scans[vmid] ``` Beide Fehler zusammen machen das Feld deterministisch leer. ## Gleiches Muster bei weiteren Feldern **IP-Adresse** (41 von 79 Dateien leer): `scan.get('ip_address')` ist kein Rohfeld. `_qemu_identity()` in `deploy_router.py:103` leitet es ab: ```python data["ip_address"] = ( _primary_ipv4((qemu or {}).get("network", []), node.internal_network or node.ip_address or "") or (qemu or {}).get("ip_address") or "" ) ``` Docgen greift auf den Fallback-Zweig zu und verpasst die eigentliche Ableitung aus `network[]`. Fuer VM-Agent-VMs existiert zusaetzlich `_vm_agent_ip(scan, node)` — von Docgen gar nicht genutzt. **Prioritaet invertiert.** `deploy_router.py:343` dokumentiert die verbindliche Regel: > Wenn der theProx-VM-Agent installiert ist (`version` vorhanden), kommt die gesamte Identitaet vom Agent; der qemu-guest-agent dient nur noch als Ping (`agent_available`) und als Fallback je Feld. Docgen macht das Gegenteil: `scan.get('os_pretty') or gscan.get('os_pretty')` — QEMU zuerst, Agent als Fallback. Die Doku kann damit andere Werte zeigen als das Dashboard fuer dieselbe VM. ## Bewertung Das ist kein Dreizeiler-Fix. Docgen hat eine **zweite Anreicherungsschicht** neben der korrekten in `deploy_router.py` aufgebaut und diese falsch nachimplementiert. Solange beide existieren, driften sie weiter auseinander. Die Anreicherung gehoert genau einmal ins System — das zahlt direkt auf #95 (Service-Extraktion aus deploy/admin/security_router) ein. --- ## Prompt fuer Claude Code ``` Behebe die fehlerhafte Guest-Datenanreicherung im Docgen von theProx, indem du die Anreicherungslogik aus deploy_router.py in einen gemeinsamen Service extrahierst und BEIDE Aufrufer darauf umstellst. --- 1. NEUER SERVICE --- server/services/guest_view.py (neu). Enthaelt die aus deploy_router.py HERAUSGELOESTEN (nicht kopierten!) reinen Funktionen: _qemu_scan_for, _qemu_identity, _primary_ipv4, _vm_agent_ip, _vm_agent_data, _proxmox_static, _ct_ip_from_config (Namen ohne fuehrenden Unterstrich exportieren) Plus die neue zentrale Funktion: def build_guest_view(node, guest: dict, kind: str, ld: dict) -> dict Sie liefert genau EIN normalisiertes Dict pro Guest mit mindestens: vmid, name, kind, status, cpus, maxmem, maxdisk, agent_version, agent_last_check, agent_available, vm_agent_connected, os_pretty, kernel, ip_address, disk[], docker, updates, http_services[], ports[], scanned_at Die Prioritaetsregel wird HIER einmal implementiert und ist verbindlich: - Liegt ein VM-Agent-Scan mit "version" vor, gewinnt der Agent fuer os_pretty / kernel / ip_address; QEMU ist Fallback JE FELD. - Ohne Agent: QEMU-Identitaet. - ip_address IMMER ueber die Ableitung (_primary_ipv4 aus network[] bzw. _vm_agent_ip), NIE als roher Key-Zugriff. - agent_version kommt aus vm_scans[vmid]["version"] — NICHT aus vm_qemu_scans, und NICHT unter dem Namen "agent_version" (den gibt es im Rohdatenmodell nicht). --- 2. AUFRUFER UMSTELLEN --- a) deploy_router.py: die verschobenen Funktionen importieren statt lokal definieren. Die Schleife ab ~Zeile 298 auf build_guest_view() umstellen. Die API-Antwort muss BYTE-GLEICH bleiben — vorher/nachher-Vergleich gegen einen echten Node-Datensatz, das ist die Abnahmebedingung. _apply_card_override() bleibt wo es ist und wird NACH build_guest_view angewandt. b) update_router.py:295 (gleiches agent_version-Muster) ebenfalls umstellen. c) docgen.py: _vm_md() bekommt kuenftig das fertige View-Dict statt roher guest/scan/gscan-Dicts. Alle direkten Zugriffe auf ld["vm_scans"] / ld["vm_qemu_scans"] aus docgen.py ENTFERNEN — Docgen darf Rohdaten nicht mehr selbst interpretieren. --- 3. DOCGEN-AUSGABE --- Agent-Zeile ehrlich rendern, drei unterscheidbare Zustaende statt einem "—": | Agent | v1.4.2 (zuletzt 2026-07-19 03:12 UTC) | # Agent laeuft | Agent | nicht installiert | # kein vm_scans-Eintrag | Agent | nicht erreichbar (QEMU-Agent antwortet) | # agent_available true, # aber kein VM-Agent Der Unterschied zwischen "nicht installiert" und "keine Daten" ist fuer die Doku wesentlich — heute sieht beides gleich aus. Zusaetzlich im Frontmatter (siehe #209): agent: true|false agent_version: "…" # nur wenn vorhanden agent_last_check: <ISO> # nur wenn vorhanden Und Tag "theprox/no-agent" fuer Guests ohne Agent, damit sie im Graphen und in der Matrix sofort auffindbar sind. Ins _MOC.md eine Sektion "Guests ohne Agent" aufnehmen — das ist eine Arbeitsliste, kein Schoenheits- fehler. --- 4. TESTS --- server/tests/test_guest_view.py — mit ECHTEN, anonymisierten last_data- Fixtures (Hostnamen/IPs ersetzt), nicht mit synthetischen Minimal-Dicts. Genau diese Klasse Bug entsteht, wenn man gegen erfundene Strukturen testet. - VM MIT Agent: agent_version gefuellt, os/kernel/ip aus vm_scans - VM OHNE Agent, MIT QEMU: agent_version None, Identitaet aus vm_qemu_scans - VM ohne beides: alle Identitaetsfelder leer, kein Crash - Prioritaet: Agent liefert os_pretty, QEMU liefert ein anderes -> Agent gewinnt - Feld-Fallback: Agent hat kein kernel, QEMU schon -> QEMU-Kernel greift - ip_address wird aus network[] abgeleitet, nicht aus dem Rohkey - LXC: kein Agent-Konzept, ip aus der Proxmox-Config - Regressionstest: deploy_router-Antwort vor/nach dem Refactor identisch --- 5. VERIFIKATION --- Nach dem Fix einen Docgen-Lauf gegen echte Daten fahren und pruefen, dass die Anzahl VM-Dateien mit gefuellter Agent-Zeile der Anzahl tatsaechlich enrollter VMs entspricht. Dieses Verhaeltnis in die Task-Summary schreiben ("Agent-Daten: 54/79 Guests") — dann faellt ein Rueckfall sofort auf, statt erst beim naechsten Lesen der Doku. NICHT anfassen: Git-Push-Logik, Backups-Merge, Scheduler. ``` ## Definition of Done - [ ] `services/guest_view.py` existiert, `deploy_router.py` und `update_router.py` nutzen es - [ ] API-Antwort vor/nach Refactor nachweislich identisch - [ ] `docgen.py` greift nicht mehr direkt auf `vm_scans` / `vm_qemu_scans` zu - [ ] Agent-Zeile unterscheidet installiert / nicht installiert / nicht erreichbar - [ ] IP wird abgeleitet, nicht roh gelesen - [ ] Agent gewinnt gegenueber QEMU, Fallback je Feld - [ ] Tests laufen gegen anonymisierte echte Fixtures - [ ] Verhaeltnis Agent-Daten/Guests steht in der Task-Summary
Author
Owner

Beweis aus dem Bestand. Kreuzprobe ueber die 78 generierten VM-Dateien im Ziel-Repo:

49 × "## Belegter Speicher" vorhanden  →  | Agent | — |
29 × keine Speicher-Sektion            →  | Agent | — |

## Belegter Speicher wird aus gscan["disk"] gerendert, also aus vm_scans — Daten, die ausschliesslich existieren, wenn der theProx-VM-Agent installiert ist und meldet. 49 Guests haben den Agent nachweislich, und alle 49 zeigen . Damit ist die Diagnose (falsches Dict + nicht existenter Key) am echten Datenbestand bestaetigt, nicht nur am Code.

Gegenprobe Node-Ebene: alle 7 node.md sind vollstaendig gefuellt (PVE-Version, CPU, RAM, Kernel, Temp, Uptime, IP, Updates, Storage, Netzwerk). Der Fehler ist sauber auf die Guest-Ebene begrenzt, also genau dort, wo die beiden Scan-Dicts nebeneinander liegen.

Erwartungswert nach dem Fix: 49 von 78 Guests mit gefuellter Agent-Zeile. Diese Zahl eignet sich direkt als Abnahmekriterium.

Folgeissue #213 deckt die Datenquellen ab, die _vm_md() gar nicht rendert (Updates 0/78, HTTP-Services 0/78).

**Beweis aus dem Bestand.** Kreuzprobe ueber die 78 generierten VM-Dateien im Ziel-Repo: ``` 49 × "## Belegter Speicher" vorhanden → | Agent | — | 29 × keine Speicher-Sektion → | Agent | — | ``` `## Belegter Speicher` wird aus `gscan["disk"]` gerendert, also aus `vm_scans` — Daten, die ausschliesslich existieren, wenn der theProx-VM-Agent installiert ist und meldet. **49 Guests haben den Agent nachweislich**, und alle 49 zeigen `—`. Damit ist die Diagnose (falsches Dict + nicht existenter Key) am echten Datenbestand bestaetigt, nicht nur am Code. Gegenprobe Node-Ebene: alle 7 `node.md` sind vollstaendig gefuellt (PVE-Version, CPU, RAM, Kernel, Temp, Uptime, IP, Updates, Storage, Netzwerk). Der Fehler ist sauber auf die Guest-Ebene begrenzt, also genau dort, wo die beiden Scan-Dicts nebeneinander liegen. Erwartungswert nach dem Fix: **49 von 78** Guests mit gefuellter Agent-Zeile. Diese Zahl eignet sich direkt als Abnahmekriterium. Folgeissue #213 deckt die Datenquellen ab, die `_vm_md()` gar nicht rendert (Updates 0/78, HTTP-Services 0/78).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chinux/theProx#212
No description provided.