From 8e5bf74a30e1e67cbe54a250b59b0a50f9a1c9a0 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sat, 3 Oct 2026 18:11:38 +0200 Subject: [PATCH] fix(agent): Cluster-Knoten mit QDevice richtig lesen (#042) Der Agent nahm aus `pvecm nodes` blind die dritte Spalte als Knotennamen. Mit einem QDevice steht dort dessen Zustand (A,V,NMW oder NR), und der Name rutscht eine Spalte weiter - gemeldet wurden damit die Zustandskuerzel und das QDevice als zusaetzlicher vierter Knoten. Die Namensspalte wird jetzt aus der Kopfzeile bestimmt, die Zeile des QDevice (Nodeid 0) uebersprungen. Beide Ausgabeformen sind als Testfall hinterlegt. Agent-Version 0.2.2. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 6 +++++ DEVLOG.md | 8 ++++-- agent/proxledger-agent.sh | 19 ++++++++++++-- docs/issues/042-qdevice-cluster.md | 40 ++++++++++++++++++++++++++++++ docs/issues/README.md | 3 ++- package.json | 2 +- src/server/agent/http.ts | 2 +- tests/agent-skript.test.ts | 19 ++++++++++++++ 8 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 docs/issues/042-qdevice-cluster.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 87c71f1..2710f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/). ### Sicherheit +## [0.24.1] – 2026-10-03 + +### Behoben +- **Cluster mit QDevice meldeten falsche Knotennamen** (`A,V,NMW`, `NR`) und das QDevice als zusätzlichen Knoten: `pvecm nodes` hat dann eine Spalte mehr. Die Namensspalte wird jetzt aus der Kopfzeile bestimmt (#042) +- Agent-Version 0.2.2 + ## [0.24.0] – 2026-10-03 ### Hinzugefügt diff --git a/DEVLOG.md b/DEVLOG.md index 74592b6..b229db1 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -2,7 +2,7 @@ ## Zeitübersicht -**Gesamt:** 22 h 33 min +**Gesamt:** 22 h 53 min | Issue | Zeit | |---|---| @@ -47,6 +47,7 @@ | #039 Zugang zur Laufzeit | 0 h 30 min | | #040 Schlagworte sichtbar | 0 h 40 min | | #041 Doppelte Hosts | 0 h 50 min | +| #042 QDevice im Cluster | 0 h 20 min | | Meilenstein | Zeit | |---|---| @@ -69,7 +70,7 @@ ## 2026-09-17 · Sitzung 1 · 19:36–laufend **Issue:** #001–#007 (Meilensteine v0.1.0 bis v0.4.0) -**Version:** 0.24.0 +**Version:** 0.24.1 ### Erledigt - Auftrag gelesen, Rückfragen gestellt @@ -212,6 +213,9 @@ - Doppelte Hosts (#041): Zwei Einträge desselben Namens beim selben Kunden entstehen, wenn sich die Maschinenkennung ändert. Neue Warnregel plus die bis dahin fehlende Möglichkeit, einen Host überhaupt loszuwerden - Bewusst kein automatisches Zusammenführen: Es müsste raten, welcher Eintrag welche Rechnung erbt +- QDevice im Cluster (#042): Der Agent nahm blind die dritte Spalte von `pvecm nodes` als Namen. Mit QDevice steht dort der Zustand des QDevice +- Dritter Fall dieser Art: Was beim Feldtest auf einem Cluster stimmte, stimmt nicht überall. Spalten gehören über ihre Überschrift angesprochen + ### Nächster Schritt - #016 Benutzerhandbuch mit Screenshots, danach #017 Favicons und #019 CI-Pipeline - Für 1.0.0 fehlt noch: PMG (kein Host verfügbar) und der Fall „aktive Subscription auf PBS“ (#023) diff --git a/agent/proxledger-agent.sh b/agent/proxledger-agent.sh index 482ba7f..6d6ef5e 100755 --- a/agent/proxledger-agent.sh +++ b/agent/proxledger-agent.sh @@ -12,7 +12,7 @@ set -o nounset set -o pipefail -AGENT_VERSION="0.2.1" +AGENT_VERSION="0.2.2" CONFIG_DATEI="${PROXLEDGER_CONFIG:-/etc/proxledger/agent.conf}" SPOOL_VERZEICHNIS="${PROXLEDGER_SPOOL:-/var/lib/proxledger/spool}" SPOOL_MAXIMUM=30 @@ -175,9 +175,24 @@ cluster_name() { # Namen aller Cluster-Mitglieder laut corosync. Auch Knoten ohne Agent stehen hier – # daraus erkennt das Dashboard vergessene Knoten (#022). +# Namen aller Cluster-Knoten aus `pvecm nodes`. +# +# Die Spaltenzahl ist nicht fest: Mit einem QDevice steht zwischen Votes und Name noch dessen +# Zustand (A,V,NMW oder NR). Deshalb wird die Spalte "Name" aus der Kopfzeile bestimmt, statt +# blind die dritte zu nehmen. Die Zeile des QDevice selbst traegt die Nodeid 0 und ist kein Knoten. cluster_mitglieder() { if command -v pvecm >/dev/null 2>&1; then - pvecm nodes 2>/dev/null | awk '/^[[:space:]]*[0-9]+[[:space:]]+[0-9]+/ {print $3}' | sed 's/ *(local)//' + pvecm nodes 2>/dev/null | awk ' + /Nodeid/ { + for (i = 1; i <= NF; i++) if ($i == "Name") spalte = i + next + } + /^[[:space:]]*[0-9]+[[:space:]]+[0-9]+/ { + if ($1 == 0) next + name = (spalte ? $spalte : $3) + sub(/\(.*/, "", name) + if (name != "") print name + }' fi } diff --git a/docs/issues/042-qdevice-cluster.md b/docs/issues/042-qdevice-cluster.md new file mode 100644 index 0000000..a0179e5 --- /dev/null +++ b/docs/issues/042-qdevice-cluster.md @@ -0,0 +1,40 @@ +# #042 Cluster-Knoten falsch gelesen, wenn ein QDevice im Spiel ist +**Status:** erledigt +**Meilenstein:** v0.24.1 +**Labels:** bug, agent +**Geschätzt:** 1 h **Tatsächlich:** 0 h 20 min + +## Auslöser +Florians Cluster hat drei Knoten (`hv-pve-01`, `hv-pve-02`, `hv-pve-05`). ProxLedger zeigte vier +„Cluster-Knoten“: `A,V,NMW`, `A,V,NMW`, `NR` und `Qdevice`. + +## Ursache +Der Agent nahm aus `pvecm nodes` blind die dritte Spalte als Namen. Ohne QDevice stimmt das: + +``` + Nodeid Votes Name + 1 1 hv-rz5-pve-01 (local) +``` + +Mit QDevice steht dort aber dessen Zustand, und der Name rutscht eine Spalte weiter: + +``` + Nodeid Votes Qdevice Name + 1 1 A,V,NMW hv-pve-01 + 0 1 Qdevice (votes 1) +``` + +Gemeldet wurden damit die Zustandskürzel – und das QDevice selbst als vierter „Knoten“. + +## Behebung +Die Spalte „Name“ wird aus der Kopfzeile bestimmt, statt sie zu erraten. Die Zeile des QDevice trägt +die Nodeid 0 und wird übersprungen. Beide Ausgabeformen sind als Testfall hinterlegt. + +## Folgen +Die Warnregel „Cluster-Knoten ohne Agent“ hatte dadurch Warnungen für Knoten erzeugt, die es nicht +gibt. Sie schließen sich von selbst, sobald die Agents mit der neuen Fassung gemeldet haben. + +## Lehre +Dritter Fall in dieser Reihe: Eine Ausgabe, die beim Feldtest auf **einem** Cluster richtig gelesen +wurde, ist nicht auf jedem Cluster gleich. Spalten gehören über ihre Überschrift angesprochen, nicht +über ihre Position. diff --git a/docs/issues/README.md b/docs/issues/README.md index 18de0b4..37acc06 100644 --- a/docs/issues/README.md +++ b/docs/issues/README.md @@ -45,4 +45,5 @@ Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden a | [#039](039-zugang-zur-laufzeit.md) | Auf dem Telefon führte kein Weg zur Laufzeit | erledigt | v0.22.2 | 1 h | 0 h 30 min | | [#040](040-schlagworte-sichtbar.md) | Schlagworte und Notizen sichtbar machen | erledigt | v0.23.0 | 2 h | 0 h 40 min | | [#041](041-doppelte-hosts.md) | Gleichnamige Hosts erkennen und stilllegen | erledigt | v0.24.0 | 3 h | 0 h 50 min | -| | **Summe** | | | **168 h** | **22 h 33 min** | +| [#042](042-qdevice-cluster.md) | Cluster-Knoten falsch gelesen bei QDevice | erledigt | v0.24.1 | 1 h | 0 h 20 min | +| | **Summe** | | | **169 h** | **22 h 53 min** | diff --git a/package.json b/package.json index 74806be..c2f2db5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "proxledger", - "version": "0.24.0", + "version": "0.24.1", "private": true, "scripts": { "dev": "next dev", diff --git a/src/server/agent/http.ts b/src/server/agent/http.ts index 33a415e..d9cca5e 100644 --- a/src/server/agent/http.ts +++ b/src/server/agent/http.ts @@ -4,7 +4,7 @@ import { tokenAusHeader } from "@/lib/tokens"; import { AgentApiFehler } from "./service"; /** Version des Agent-Skripts, die das Dashboard ausliefert (siehe #005). */ -export const AGENT_VERSION = "0.2.1"; +export const AGENT_VERSION = "0.2.2"; export function fehlerAntwort(status: number, code: string, nachricht: string, extra?: HeadersInit) { return Response.json({ ok: false, code, fehler: nachricht }, { status, headers: extra }); diff --git a/tests/agent-skript.test.ts b/tests/agent-skript.test.ts index a55c0ad..f450006 100644 --- a/tests/agent-skript.test.ts +++ b/tests/agent-skript.test.ts @@ -241,6 +241,25 @@ productname: Proxmox "VE" \\ Subscription mit Tab`, expect(report.host.clusterMitglieder).toEqual(["hv-rz5-pve-01", "hv-rz5-pve-02", "hv-rz5-pve-03"]); }); + it("liest die Knoten auch mit QDevice-Spalte richtig", () => { + // Mit einem QDevice steht zwischen Votes und Name dessen Zustand. Vorher landeten + // „A,V,NMW“ und „NR“ als Knotennamen im Report (#042). + befehlErsetzen("pvesubscription", PVE_SUBSCRIPTION); + befehlErsetzen( + "pvecm", + `Membership information +---------------------- + Nodeid Votes Qdevice Name + 1 1 A,V,NMW hv-pve-01 + 2 1 A,V,NMW hv-pve-02 (local) + 3 1 NR hv-pve-05 + 0 1 Qdevice (votes 1)`, + ); + + const report = reportErzeugen() as { host: { clusterMitglieder: string[] } }; + expect(report.host.clusterMitglieder).toEqual(["hv-pve-01", "hv-pve-02", "hv-pve-05"]); + }); + it("meldet eine leere Liste, wenn der Host in keinem Cluster ist", () => { befehlErsetzen("pvesubscription", PVE_SUBSCRIPTION);