Welle 1 · Nachschliff: Proxmox-Kachel zählt die Gäste wie die Homelab-Karte, der eigene Container heißt lesbar
„8 von 8 Gästen an“ neben „7 Gäste“: Die Kachel zählte den eigenen Container (Etikett mc2) mit, die Karte nicht. In „Sicherungen je Gerät“ heißt er „Homelab Orchestrator (dieser Container)“ statt homelab-orchestrator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
371dcc7ce3
commit
f2bffc7203
@@ -13,6 +13,7 @@ höchstens alle zwei Minuten gefragt.
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from services.homelab import adguard, apps, arcane, zertifikate
|
from services.homelab import adguard, apps, arcane, zertifikate
|
||||||
|
from services.homelab.inventar import EIGENES_ETIKETT
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
NPM = {"npmplus", "nginxproxymanager"}
|
NPM = {"npmplus", "nginxproxymanager"}
|
||||||
@@ -36,7 +37,9 @@ def _npm(vmid: int) -> dict | None:
|
|||||||
def fuer_bericht(bericht: dict) -> dict[str, dict]:
|
def fuer_bericht(bericht: dict) -> dict[str, dict]:
|
||||||
"""Ziel-ID → Kennzahl. Ein Fehler bei einem Gerät kostet nur dessen Zahl."""
|
"""Ziel-ID → Kennzahl. Ein Fehler bei einem Gerät kostet nur dessen Zahl."""
|
||||||
zahlen: dict[str, dict] = {}
|
zahlen: dict[str, dict] = {}
|
||||||
gaeste = [g for g in bericht.get("gaeste") or [] if isinstance(g, dict) and "vmid" in g]
|
# Der eigene Container (Etikett „mc2“) zählt nicht mit — wie überall, wo die Geräte des Homelabs stehen.
|
||||||
|
gaeste = [g for g in bericht.get("gaeste") or [] if isinstance(g, dict) and "vmid" in g
|
||||||
|
and EIGENES_ETIKETT not in (g.get("etiketten") or [])]
|
||||||
if gaeste:
|
if gaeste:
|
||||||
laufend = sum(1 for g in gaeste if g.get("status") == "running")
|
laufend = sum(1 for g in gaeste if g.get("status") == "running")
|
||||||
zahlen["pve"] = {"text": f"{laufend} von {len(gaeste)} Gästen an"}
|
zahlen["pve"] = {"text": f"{laufend} von {len(gaeste)} Gästen an"}
|
||||||
|
|||||||
@@ -375,8 +375,10 @@ def _je_geraet(bericht: dict, ziele: dict, fehlend: set[str], ohne: set[int], pr
|
|||||||
zuletzt = max([t for z in ziele.values() if (t := (z.get("neueste") or {}).get(vmid))], default=None)
|
zuletzt = max([t for z in ziele.values() if (t := (z.get("neueste") or {}).get(vmid))], default=None)
|
||||||
pruefungen = [d["geprueft"] for d in details if d.get("geprueft")]
|
pruefungen = [d["geprueft"] for d in details if d.get("geprueft")]
|
||||||
eigene_probe = ((probe or {}).get("gaeste") or {}).get(vmid)
|
eigene_probe = ((probe or {}).get("gaeste") or {}).get(vmid)
|
||||||
|
eigener = EIGENES_ETIKETT in (g.get("etiketten") or [])
|
||||||
zeilen.append({
|
zeilen.append({
|
||||||
"ziel": _zid(g), "name": _name(g), "vmid": g["vmid"], "auftrag": g["vmid"] not in ohne,
|
"ziel": _zid(g), "name": "Homelab Orchestrator (dieser Container)" if eigener else _name(g),
|
||||||
|
"vmid": g["vmid"], "auftrag": g["vmid"] not in ohne,
|
||||||
"zuletzt": zuletzt, "frisch": bool(zuletzt) and g["vmid"] not in ohne and _zid(g) not in fehlend,
|
"zuletzt": zuletzt, "frisch": bool(zuletzt) and g["vmid"] not in ohne and _zid(g) not in fehlend,
|
||||||
"geprueft": max(pruefungen, key=lambda p: (p["zeit"], p["sicherung"]), default=None),
|
"geprueft": max(pruefungen, key=lambda p: (p["zeit"], p["sicherung"]), default=None),
|
||||||
"defekt": sorted({t for d in details for t in d.get("defekt") or []}),
|
"defekt": sorted({t for d in details for t in d.get("defekt") or []}),
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ def test_zahlen_je_geraet(monkeypatch):
|
|||||||
monkeypatch.setattr(zertifikate, "lage", lambda jetzt=None: {"zertifikate": [npm] * 10})
|
monkeypatch.setattr(zertifikate, "lage", lambda jetzt=None: {"zertifikate": [npm] * 10})
|
||||||
monkeypatch.setattr(arcane, "container_zahlen", lambda url: {"laufend": 11, "gesamt": 12})
|
monkeypatch.setattr(arcane, "container_zahlen", lambda url: {"laufend": 11, "gesamt": 12})
|
||||||
monkeypatch.setattr(adguard, "geblockt", lambda: {"anfragen": 84_211, "geblockt": 10_523, "anteil": 12.5})
|
monkeypatch.setattr(adguard, "geblockt", lambda: {"anfragen": 84_211, "geblockt": 10_523, "anteil": 12.5})
|
||||||
zahlen = kennzahlen.fuer_bericht(_bericht())
|
b = _bericht()
|
||||||
|
b["gaeste"].append({"vmid": 107, "art": "lxc", "status": "running", "etiketten": ["mc2"]}) # der eigene zählt nicht
|
||||||
|
zahlen = kennzahlen.fuer_bericht(b)
|
||||||
laufend = sum(1 for g in BERICHT["gaeste"] if g["status"] == "running")
|
laufend = sum(1 for g in BERICHT["gaeste"] if g["status"] == "running")
|
||||||
assert zahlen["pve"] == {"text": f"{laufend} von {len(BERICHT['gaeste'])} Gästen an"}
|
assert zahlen["pve"] == {"text": f"{laufend} von {len(BERICHT['gaeste'])} Gästen an"}
|
||||||
assert zahlen["ct-105"] == {"text": "letzte Sicherung", "zeit": 1790296281}
|
assert zahlen["ct-105"] == {"text": "letzte Sicherung", "zeit": 1790296281}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ def _bericht(**host) -> dict:
|
|||||||
"neueste": {k: v["zeit"] for k, v in details.items()},
|
"neueste": {k: v["zeit"] for k, v in details.items()},
|
||||||
"schaetzung": {"voll_am": 1887231717, "belegt": 1_517_685_571_584,
|
"schaetzung": {"voll_am": 1887231717, "belegt": 1_517_685_571_584,
|
||||||
"gesamt": 3_847_503_609_856}}},
|
"gesamt": 3_847_503_609_856}}},
|
||||||
"nicht_gesichert": [104],
|
"nicht_gesichert": [104], # 107 (der eigene) ist gesichert
|
||||||
"sicherung_probe": {"zeit": 1790330000, "gaeste": {
|
"sicherung_probe": {"zeit": 1790330000, "gaeste": {
|
||||||
"102": {"speicher": "pbs-qnap", "sicherung": 1790296227, "ok": True, "fehler": None},
|
"102": {"speicher": "pbs-qnap", "sicherung": 1790296227, "ok": True, "fehler": None},
|
||||||
"106": {"speicher": "pbs-qnap", "sicherung": 1790296281, "ok": False,
|
"106": {"speicher": "pbs-qnap", "sicherung": 1790296281, "ok": False,
|
||||||
@@ -130,7 +130,8 @@ def _bericht(**host) -> dict:
|
|||||||
return {"host": {**basis, **host}, "gaeste": [
|
return {"host": {**basis, **host}, "gaeste": [
|
||||||
{"vmid": 102, "art": "lxc", "name": "netbird", "app": {"kennung": "netbird"}},
|
{"vmid": 102, "art": "lxc", "name": "netbird", "app": {"kennung": "netbird"}},
|
||||||
{"vmid": 104, "art": "lxc", "name": "gitea", "app": {"kennung": "gitea"}},
|
{"vmid": 104, "art": "lxc", "name": "gitea", "app": {"kennung": "gitea"}},
|
||||||
{"vmid": 106, "art": "qemu", "name": "arcane"}]}
|
{"vmid": 106, "art": "qemu", "name": "arcane"},
|
||||||
|
{"vmid": 107, "art": "lxc", "name": "homelab-orchestrator", "etiketten": ["mc2"]}]}
|
||||||
|
|
||||||
|
|
||||||
def test_abdeckung_je_geraet():
|
def test_abdeckung_je_geraet():
|
||||||
@@ -144,6 +145,7 @@ def test_abdeckung_je_geraet():
|
|||||||
"probe": {"speicher": "pbs-qnap", "sicherung": 1790296227, "ok": True, "fehler": None,
|
"probe": {"speicher": "pbs-qnap", "sicherung": 1790296227, "ok": True, "fehler": None,
|
||||||
"zeit": 1790330000}}
|
"zeit": 1790330000}}
|
||||||
assert zeilen["vm-106"]["defekt"] == [1789864278] and zeilen["vm-106"]["probe"]["ok"] is False
|
assert zeilen["vm-106"]["defekt"] == [1789864278] and zeilen["vm-106"]["probe"]["ok"] is False
|
||||||
|
assert zeilen["ct-107"]["name"] == "Homelab Orchestrator (dieser Container)"
|
||||||
# Die letzte Prüfung von Arcane fand Beschädigtes: rot, auch wenn alles frisch gesichert ist.
|
# Die letzte Prüfung von Arcane fand Beschädigtes: rot, auch wenn alles frisch gesichert ist.
|
||||||
assert (s["stufe_frische"], s["stufe"]) == ("ok", "rot")
|
assert (s["stufe_frische"], s["stufe"]) == ("ok", "rot")
|
||||||
assert s["pruefung"] == {"zuletzt": GEPRUEFT, "noetig": False} # älteste Sicherung erst 5,5 Tage alt
|
assert s["pruefung"] == {"zuletzt": GEPRUEFT, "noetig": False} # älteste Sicherung erst 5,5 Tage alt
|
||||||
@@ -194,7 +196,7 @@ def test_aelterer_ausfuehrer_ohne_neue_angaben():
|
|||||||
b["host"].pop(schluessel)
|
b["host"].pop(schluessel)
|
||||||
b["host"]["sicherungsziele"]["pbs-qnap"].pop("details")
|
b["host"]["sicherungsziele"]["pbs-qnap"].pop("details")
|
||||||
s = speicher.sicherungen(b, JETZT)
|
s = speicher.sicherungen(b, JETZT)
|
||||||
assert [g["ziel"] for g in s["ohne_auftrag"]] == ["ct-104"] # selbst ausgerechnet aus den Aufträgen
|
assert [g["ziel"] for g in s["ohne_auftrag"]] == ["ct-104", "ct-107"] # selbst ausgerechnet aus den Aufträgen
|
||||||
assert s["stufe"] == "gelb" and s["probe"] is None
|
assert s["stufe"] == "gelb" and s["probe"] is None
|
||||||
assert all(g["geprueft"] is None and g["probe"] is None for g in s["geraete"])
|
assert all(g["geprueft"] is None and g["probe"] is None for g in s["geraete"])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user