- karenz.py: ct/<app>.sh juenger als MC_HOMELAB_KARENZ_H (Standard 48 h) -> Baustein "neu" ohne Knopf, updates.starten lehnt mit demselben Satz ab (Berliner Zeit). GitHub stumm -> nicht blockieren, die Rueckfrage sagt es. - Ausfuehrer: neue Aktionen sichern, sicherung_zurueck, sicherung_loeschen. vzdump auf den ersten lokalen Speicher mit Inhalt backup (oder sicherung_speicher aus /etc/mc2-ausfuehrer.json), nie auf pbs; vorher Platz pruefen (frei > belegt x 1,2); Notiz mc2-sicherung, nur solche werden zurueckgespielt/geloescht; Rueckweg: stoppen, pct restore --force auf den bisherigen rootfs-Speicher, starten. Bericht mit host.sicherung, sicherung_moeglich/_grund, eigenen Sicherungen und nur_lesen. Unerwartete Fehler werden beantwortet statt verschluckt; vzdump/restore beim Zeitlimit erst SIGTERM. - updates.py: Sicherung, wo kein Snapshot geht; bei Rot zurueckspielen, nach Gruen aeltere Sicherungen weg. Scheitert Snapshot oder Sicherung, beginnt das Update nicht (nicht dringend gemeldet). - pflege.py: "suchen" fuer Gaeste mit Paketlisten aelter als 7 Tage, nachts 02-05 Uhr (sonst nachholen), einmal je Gast und Tag, nie neben einem Update oder offenen Auftrag; gelber Waechter-Hinweis, wenn es zweimal hintereinander scheitert. Eine Registrierungszeile in waechter.py. - kanal.py: Auftragsliste unter flock, weil jetzt auch der Steward Auftraege anlegt. - inventar.py: Rueckweg und Rueckfrage fuer die Sicherung; Gaeste mitten im Update-Lauf nicht in der Webpruefung des Waechters (sonst zweiter Alarm beim Zurueckspielen). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.8 KiB
Python
62 lines
2.8 KiB
Python
"""Wartezeit nach einer Änderung am Update-Skript (24.09.2026).
|
|
|
|
„Jetzt updaten“ lässt im Gast `update` laufen. Das lädt ct/<kennung>.sh ungepinnt von GitHub
|
|
(community-scripts/ProxmoxVE, Zweig main) und führt es als root aus. Eine Änderung, die erst ein paar Stunden
|
|
alt ist, hat noch kaum jemand im Betrieb gesehen. Deshalb gilt für Apps mit dem Weg „skript“: Ist die letzte
|
|
Änderung an ct/<kennung>.sh jünger als MC_HOMELAB_KARENZ_H Stunden (Standard 48), bleibt der Baustein „neu“,
|
|
aber ohne Knopf, und updates.starten() lehnt ab — beide mit demselben Satz.
|
|
|
|
Wann das Skript zuletzt geändert wurde, sagt die GitHub-API (kern/github.py, 15 Minuten gemerkt). Lässt sich das
|
|
nicht abfragen, wird nicht blockiert; die Rückfrage vor dem Update sagt es dann (NICHT_GEPRUEFT).
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import time
|
|
from datetime import datetime
|
|
|
|
from kern.github import github_json
|
|
from kern.zeit import LOCAL_TZ
|
|
|
|
REPO = "community-scripts/ProxmoxVE"
|
|
STANDARD_H = 48.0
|
|
NICHT_GEPRUEFT = "Ob das Skript kürzlich geändert wurde, ließ sich nicht prüfen."
|
|
_KENNUNG = re.compile(r"^[a-z0-9-]+$")
|
|
|
|
|
|
def karenz_s() -> float:
|
|
"""Die Wartezeit in Sekunden (MC_HOMELAB_KARENZ_H; 0 schaltet sie ab)."""
|
|
try:
|
|
stunden = float(os.environ.get("MC_HOMELAB_KARENZ_H", "") or STANDARD_H)
|
|
except ValueError:
|
|
stunden = STANDARD_H
|
|
return max(0.0, stunden) * 3600
|
|
|
|
|
|
def skript_geaendert(kennung: str) -> float:
|
|
"""Wann ct/<kennung>.sh auf main zuletzt geändert wurde (Unix-Sekunden). Wirft, wenn es sich nicht sagen lässt."""
|
|
if not _KENNUNG.match(kennung or ""):
|
|
raise ValueError(f"ungültige Kennung {kennung!r}")
|
|
daten = github_json(f"repos/{REPO}/commits?path=ct/{kennung}.sh&sha=main&per_page=1")
|
|
# Das Datum, an dem die Änderung auf main landete (committer, nicht author: ein Pull-Request kann älter sein).
|
|
datum = str(daten[0]["commit"]["committer"]["date"])
|
|
return datetime.fromisoformat(datum.replace("Z", "+00:00")).timestamp()
|
|
|
|
|
|
def sperrtext(geaendert: float, frei_ab: float) -> str:
|
|
a, b = datetime.fromtimestamp(geaendert, LOCAL_TZ), datetime.fromtimestamp(frei_ab, LOCAL_TZ)
|
|
return f"Das Update-Skript wurde am {a:%d.%m.} geändert; zur Sicherheit erst ab {b:%d.%m. %H:%M}."
|
|
|
|
|
|
def pruefen(kennung: str, jetzt: float | None = None) -> dict:
|
|
"""{"gesperrt": Satz oder None, "geprueft": bool}. Gesperrt, solange die letzte Änderung am Skript jünger ist
|
|
als die Wartezeit. Scheitert die Abfrage: nicht gesperrt, aber geprueft=False."""
|
|
try:
|
|
geaendert = skript_geaendert(kennung)
|
|
except Exception:
|
|
return {"gesperrt": None, "geprueft": False}
|
|
frei_ab = geaendert + karenz_s()
|
|
if (time.time() if jetzt is None else jetzt) >= frei_ab:
|
|
return {"gesperrt": None, "geprueft": True}
|
|
return {"gesperrt": sperrtext(geaendert, frei_ab), "geprueft": True}
|