Welle 1 · Ausführer: Zertifikats-Zwischenspeicher hieß wie seine Funktion; PVE/PBS mit Rechnernamen

Der erste echte Bericht trug statt der Zertifikate „'function' object is not subscriptable“: def _zertifikate()
überschrieb das gleichnamige Dict. Jetzt _zertifikate_stand, mit Test über genau diesen Weg (auf dem Host
nachgeprüft: 12 Zertifikate). Proxmox-Host und PBS heißen in der Liste nach ihrem Rechnernamen statt „localhost“.
OFFENE-FAEDEN: Welle 1 erledigt, AdGuard-Zugang offen beim User.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-25 16:59:00 +02:00
co-authored by Claude Opus 5.5
parent d063f745f6
commit 371dcc7ce3
4 changed files with 34 additions and 6 deletions
+4 -1
View File
@@ -78,8 +78,11 @@ def bewerten(z: dict, jetzt: float) -> dict:
elif alt: elif alt:
stufe, art, satz = "gelb", "alt", f"ausgeliefert wird noch das alte (bis {_datum(ausgeliefert)})" stufe, art, satz = "gelb", "alt", f"ausgeliefert wird noch das alte (bis {_datum(ausgeliefert)})"
namen = [n for n in z.get("namen") or [] if n] namen = [n for n in z.get("namen") or [] if n]
# Anzeigename: bei Proxmox-Host und PBS der Rechnername (ihr erster Name ist „localhost“), sonst der erste Name.
eigener = z.get("inhaber") if z.get("quelle") in WESSEN else None
name = eigener or next((n for n in namen if n != "localhost"), None) or z.get("inhaber") or z.get("name")
return {"quelle": z.get("quelle"), "wo": QUELLE.get(z.get("quelle"), z.get("quelle")), "gast": z.get("gast"), return {"quelle": z.get("quelle"), "wo": QUELLE.get(z.get("quelle"), z.get("quelle")), "gast": z.get("gast"),
"name": namen[0] if namen else z.get("inhaber") or z.get("name"), "namen": namen, "datei": z.get("name"), "name": name, "namen": namen, "datei": z.get("name"),
"aussteller": z.get("aussteller"), "von": von, "bis": bis, "rest_s": int(rest), "aussteller": z.get("aussteller"), "von": von, "bis": bis, "rest_s": int(rest),
"laufzeit_s": int(laufzeit) if laufzeit else None, "stufe": stufe, "art": art, "problem": satz} "laufzeit_s": int(laufzeit) if laufzeit else None, "stufe": stufe, "art": art, "problem": satz}
+15 -1
View File
@@ -90,12 +90,26 @@ def test_zertifikate_lesen(a, monkeypatch):
assert len(befehle) == 2 and "/opt/npmplus/tls/certbot/live/*/fullchain.pem" in befehle[0] assert len(befehle) == 2 and "/opt/npmplus/tls/certbot/live/*/fullchain.pem" in befehle[0]
def test_zertifikate_im_bericht_hoechstens_alle_30_minuten(a, monkeypatch):
"""Der Weg, den der Bericht nimmt: 25.09.2026 abends hieß der Zwischenspeicher wie die Funktion, und der erste
echte Bericht trug statt der Zertifikate „'function' object is not subscriptable“."""
gelesen: list[int] = []
monkeypatch.setattr(a, "_zertifikate_lesen", lambda gaeste: gelesen.append(len(gaeste)) or [{"quelle": "pve"}])
monkeypatch.setattr(a, "_zertifikate_stand", {"zeit": 0.0, "liste": None})
assert a._zertifikate([]) == [{"quelle": "pve"}]
assert a._zertifikate([]) == [{"quelle": "pve"}] and gelesen == [0] # gemerkt
a._zertifikate_stand["zeit"] -= a.ZERTIFIKATE_ALLE_S
a._zertifikate([{"vmid": 1}])
assert gelesen == [0, 1]
# --- Bewertung ----------------------------------------------------------------------------------------------------- # --- Bewertung -----------------------------------------------------------------------------------------------------
NPM = {"quelle": "npm", "gast": 101, "name": "npm-11", "von": _utc(2026, 9, 23, 4, 45, 2), NPM = {"quelle": "npm", "gast": 101, "name": "npm-11", "von": _utc(2026, 9, 23, 4, 45, 2),
"bis": _utc(2026, 9, 29, 20, 45, 1), "aussteller": "Let's Encrypt", "namen": ["git.tobisniceshomelab.ddnsfree.com"]} "bis": _utc(2026, 9, 29, 20, 45, 1), "aussteller": "Let's Encrypt", "namen": ["git.tobisniceshomelab.ddnsfree.com"]}
PVE = {"quelle": "pve", "gast": None, "name": "pve-ssl.pem", "von": _utc(2025, 8, 5, 16, 5, 14), PVE = {"quelle": "pve", "gast": None, "name": "pve-ssl.pem", "von": _utc(2025, 8, 5, 16, 5, 14),
"bis": _utc(2027, 8, 5, 16, 5, 14), "aussteller": "PVE Cluster Manager CA", "inhaber": "pve.fritz.box", "namen": []} "bis": _utc(2027, 8, 5, 16, 5, 14), "aussteller": "PVE Cluster Manager CA", "inhaber": "pve.fritz.box",
"namen": ["localhost", "pve", "pve.fritz.box"]}
def test_bewertung_nach_laufzeit(): def test_bewertung_nach_laufzeit():
+7 -4
View File
@@ -924,7 +924,9 @@ PBS_ZERTIFIKAT = "/etc/proxmox-backup/proxy.pem"
ZERTIFIKATE_ALLE_S = 1800 ZERTIFIKATE_ALLE_S = 1800
_MONATE = {m: i for i, m in enumerate(("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"), _MONATE = {m: i for i, m in enumerate(("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"),
start=1)} start=1)}
_zertifikate: dict = {"zeit": 0.0, "liste": None} # Zuletzt gelesene Zertifikate. Hieß bis 25.09.2026 abends wie die Funktion _zertifikate() — die überschrieb den
# Namen, und der erste echte Bericht meldete „'function' object is not subscriptable“ statt Zertifikaten.
_zertifikate_stand: dict = {"zeit": 0.0, "liste": None}
def openssl_zeit(text: str) -> int | None: def openssl_zeit(text: str) -> int | None:
@@ -1031,9 +1033,10 @@ def _zertifikate_lesen(gaeste: list[dict]) -> list[dict]:
def _zertifikate(gaeste: list[dict]) -> list[dict] | None: def _zertifikate(gaeste: list[dict]) -> list[dict] | None:
"""Die Zertifikate, höchstens alle ZERTIFIKATE_ALLE_S neu gelesen (sie ändern sich höchstens täglich).""" """Die Zertifikate, höchstens alle ZERTIFIKATE_ALLE_S neu gelesen (sie ändern sich höchstens täglich)."""
if _zertifikate["liste"] is None or time.time() - _zertifikate["zeit"] >= ZERTIFIKATE_ALLE_S: stand = _zertifikate_stand
_zertifikate.update(zeit=time.time(), liste=_zertifikate_lesen(gaeste)) if stand["liste"] is None or time.time() - stand["zeit"] >= ZERTIFIKATE_ALLE_S:
return _zertifikate["liste"] stand.update(zeit=time.time(), liste=_zertifikate_lesen(gaeste))
return stand["liste"]
def bericht() -> dict: def bericht() -> dict:
+8
View File
@@ -34,6 +34,14 @@ Freiheit (Sicherheitsupdates und Rückweg bei Rot selbst, Meldung danach; sonst
„Übernehmen“ bei neuen Geräten setzt das Etikett, Konfig-Sicherung geschwärzt in ein privates Gitea-Repo, kein „Übernehmen“ bei neuen Geräten setzt das Etikett, Konfig-Sicherung geschwärzt in ein privates Gitea-Repo, kein
Web-Push (Telegram bleibt), Arcane-Lücken-Scan sonntags 02:00, NAS-Platten über SNMP. Web-Push (Telegram bleibt), Arcane-Lücken-Scan sonntags 02:00, NAS-Platten über SNMP.
**Welle 1 erledigt (25.09.2026 abends, `main` `d063f74`):** Totmannschalter (Zeitpläne der Box, Hermes-Jobs,
Gitea-Ampel; Seite Dienste), „voll in etwa N Tagen“ (Pool, NAS, Systemplatte, Container, VMs, Platte der KI-Box;
gelb unter 14, rot unter 3 Tagen — Anzeigen ab etwa 27.09., wenn zwei Tage Verlauf da sind), Sicherungs-Abdeckung
(ohne Auftrag, Prüfung durch den PBS, wöchentliche Probe-Wiederherstellung je Gerät, Schätzung des PBS),
Zertifikats-Wache (Grenzen nach Laufzeit, NPMplus kurzlebig 6,7 Tage) und Live-Zahlen je Dienst auf den Kacheln.
Offen beim User: den **AdGuard-Zugang** unter Einstellungen → Homelab eintragen (am besten ein eigener Benutzer), dann
zeigt die Kachel „% geblockt“.
## Offene Einzelpunkte (Homelab) ## Offene Einzelpunkte (Homelab)
- **Zugang von außen:** Der Eintrag `mc.tobisniceshomelab.ddnsfree.com` in NPMplus (Proxy-Host 14 → Box :9001) hatte - **Zugang von außen:** Der Eintrag `mc.tobisniceshomelab.ddnsfree.com` in NPMplus (Proxy-Host 14 → Box :9001) hatte