homelab: Wächter und Geräteliste sehen volle Gastplatten (AdGuard stand bei 100 %)

Der Ausfuehrer schickt die Belegung der rootfs laufender Container mit; ab 85 % gelb, ab 95 % rot, in der
Geraeteliste als eigene Spalte und auf der Lampe des Geraets. Anlass: AdGuard (2 GB) war voll, das
Abfrageprotokoll schrieb nicht mehr und die Paketsuche scheiterte, ohne dass es eine Anzeige sagte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-24 21:00:44 +02:00
co-authored by Claude Opus 5.5
parent eed36e9764
commit c08a144006
30 changed files with 146 additions and 32 deletions
+1
View File
@@ -49,6 +49,7 @@ class ZielStand:
geprueft: float | None = None # Unix-Sekunden der letzten Prüfung
rueckweg: str | None = None # „Sicherung vor jedem Lauf", „Snapshot", „nur Backup"
rueckweg_art: str | None = None # Kurzform für die Oberfläche: „snapshot“, „sicherung“ oder „keiner“
platte: dict | None = None # {"belegt", "gesamt"} in Bytes, wo bekannt (laufende Container)
def als_dict(self) -> dict:
daten = asdict(self)
+1 -1
View File
@@ -210,7 +210,7 @@ def _gast_ziel(gast: dict, jetzt: float) -> ZielStand:
laeuft = gast.get("status") == "running"
ziel = ZielStand(id=zid, name=app.name if app else str(gast.get("name") or zid), art=art, instanz="homelab",
erreichbar=(erreichbar(url) if laeuft else False) if url else (laeuft or None),
rueckweg=_rueckweg(gast), rueckweg_art=_rueckweg_art(gast))
rueckweg=_rueckweg(gast), rueckweg_art=_rueckweg_art(gast), platte=gast.get("platte"))
if not laeuft:
ziel.bausteine.append(BausteinStand(id="status", name="Status", zustand="unbekannt",
grund=f"Der Gast ist {gast.get('status') or 'unbekannt'}."))
+32 -1
View File
@@ -558,12 +558,43 @@ def pruefe_gaeste() -> list[Befund]:
for e in inventar.erreichbarkeit() if not e["ok"]]
GAST_PLATTE_GELB = 0.85
GAST_PLATTE_ROT = 0.95
def _gb_komma(n: float) -> str:
return f"{n / 1e9:.1f}".replace(".", ",")
def pruefe_gast_platten() -> list[Befund]:
"""Homelab: Läuft die Platte eines Containers voll? Am 24.09.2026 stand AdGuard bei 100 %: Das Abfrageprotokoll
schrieb nicht mehr, die Paketsuche scheiterte — und keine Anzeige sagte es."""
from services.homelab import apps, kanal
daten = kanal.bericht()
befunde = []
for g in (daten or {}).get("bericht", {}).get("gaeste") or []:
platte = g.get("platte") or {}
gesamt, belegt = platte.get("gesamt"), platte.get("belegt") or 0
if not gesamt or belegt / gesamt < GAST_PLATTE_GELB:
continue
app = apps.app_fuer((g.get("app") or {}).get("kennung"), g.get("name"))
name = app.name if app else str(g.get("name") or g["vmid"])
anteil = belegt / gesamt
befunde.append(Befund(
id=f"gast-platte:{g['vmid']}", stufe="rot" if anteil >= GAST_PLATTE_ROT else "gelb",
titel=f"Platte von {name} zu {min(100, round(anteil * 100))} % voll",
text=(f"{_gb_komma(belegt)} von {_gb_komma(gesamt)} GB belegt (Container {g['vmid']}). Platz schaffen "
"oder in Proxmox die Platte vergrößern: Container → Ressourcen → Laufwerk → Größe ändern."),
quelle=f"ct-{g['vmid']}"))
return befunde
# Was jede Rolle prüft. Die KI-Box kennt Dienste, Timer, Hermes und ihren Kern; der Homelab-Teil
# den Ausführer auf dem Proxmox-Host und seine Gäste.
PRUEFUNGEN = {
"box": (pruefe_dienste, pruefe_timer_dienste, pruefe_hermes_jobs, pruefe_kern, pruefe_platte,
pruefe_festgehalten, pruefe_partner, pruefe_probe_wiederherstellung),
"homelab": (pruefe_platte, pruefe_partner, pruefe_ausfuehrer, pruefe_gaeste),
"homelab": (pruefe_platte, pruefe_partner, pruefe_ausfuehrer, pruefe_gaeste, pruefe_gast_platten),
}[ROLLE]
PRUEFUNGEN += (__import__("services.homelab.pflege").homelab.pflege.pruefe_paketlisten,) if ROLLE == "homelab" else ()
BETREFF_PROBLEM, BETREFF_OK = {"box": ("[Box-Problem]", "[Box wieder ok]"),
+22
View File
@@ -361,3 +361,25 @@ def test_ausfuehrer_erkennt_beide_formate_von_update():
assert a.app_kennung_aus(alt) == "adguard"
assert a.app_kennung_aus(neu) == "gitea"
assert a.app_kennung_aus("") is None
def test_waechter_und_uebersicht_sehen_volle_gastplatten(daten):
"""24.09.2026: AdGuard stand bei 100 % seiner 2 GB — jetzt sagen es Wächter und Geräteliste."""
from services import waechter
bericht = json.loads(json.dumps(BERICHT))
for g in bericht["gaeste"]:
if g["vmid"] == 100:
g["platte"] = {"belegt": 2_000_000_000, "gesamt": 2_040_000_000}
if g["vmid"] == 104:
g["platte"] = {"belegt": 1_800_000_000, "gesamt": 2_000_000_000}
if g["vmid"] == 101:
g["platte"] = {"belegt": 500_000_000, "gesamt": 2_000_000_000}
kanal.bericht_speichern(bericht)
befunde = {b.id: b for b in waechter.pruefe_gast_platten()}
assert set(befunde) == {"gast-platte:100", "gast-platte:104"}
assert (befunde["gast-platte:100"].stufe, befunde["gast-platte:104"].stufe) == ("rot", "gelb")
assert befunde["gast-platte:100"].titel == "Platte von AdGuard Home zu 98 % voll"
assert "2,0 von 2,0 GB" in befunde["gast-platte:100"].text
z = {z["id"]: z for z in inventar.ziele()["ziele"]}
assert z["ct-100"]["platte"] == {"belegt": 2_000_000_000, "gesamt": 2_040_000_000} and z["pve"]["platte"] is None
+1 -1
View File
@@ -220,7 +220,7 @@ from services import waechter
print(json.dumps({"pruefungen": [p.__name__ for p in waechter.PRUEFUNGEN], "daten": str(waechter.DATEN_DIR)}))
""")
assert ergebnis["pruefungen"] == ["pruefe_platte", "pruefe_partner", "pruefe_ausfuehrer", "pruefe_gaeste",
"pruefe_paketlisten"]
"pruefe_gast_platten", "pruefe_paketlisten"]
def test_homelab_rolle_hat_die_homelab_schnittstellen(tmp_path):