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:
co-authored by
Claude Opus 5.5
parent
eed36e9764
commit
c08a144006
@@ -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)
|
||||
|
||||
@@ -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'}."))
|
||||
|
||||
@@ -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]"),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user