phase2b: Auftraege ueberleben einen Neustart von MC2
- jobengine: jeder Auftrag laeuft als eigene systemd-Einheit mc2-job-<id> (systemd-run, RuntimeMaxSec als Zeitlimit); Akte, Protokoll und Exit-Code liegen unter <Datenordner>/mc2-jobs; MC2 nimmt laufende Auftraege beim Start wieder auf - Geheimnisse (HF_TOKEN) ueber eine nur fuer den Nutzer lesbare Umgebungsdatei, die der Auftrag beim Start liest und loescht; nichts davon in Befehlszeile oder Einheit - benannte Nacharbeiten (wartung:nach_update, modell:rolle) statt Closures, laufen auch nach einem Neustart - ohne systemd (PC, Tests) weiter als Kindprozess - deploy.sh wartet nur noch auf Update-Auftraege; Downloads laufen weiter - 14 neue Tests; systemd-Weg auf der Box echt geprueft (Neustart, Abbruch, Zeitlimit, Geheimnis nicht sichtbar) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
75611be9a9
commit
e1d7d499f8
@@ -246,6 +246,7 @@ def updates() -> dict:
|
||||
return daten
|
||||
|
||||
|
||||
@jobengine.nacharbeit("wartung:nach_update")
|
||||
def _nach_update() -> None:
|
||||
"""Nach einem abgeschlossenen Update: Zwischenspeicher leeren und den Stand hochzählen."""
|
||||
global update_stand
|
||||
@@ -566,12 +567,12 @@ def logs(service: str, lines: int = 200) -> dict:
|
||||
return {"ok": r["ok"], "text": r["out"] or r["err"]}
|
||||
return {"ok": False, "text": "", "err": "Dienst nicht erlaubt."}
|
||||
|
||||
def _starten(args: list[str], label: str, on_done=None, zeitlimit_s: int | None = None) -> dict:
|
||||
def _starten(args: list[str], label: str, zeitlimit_s: int | None = None) -> dict:
|
||||
"""Wartungs-Riegel: nur EIN binär-/dienst-veränderndes Job gleichzeitig. Prüfen und Eintragen
|
||||
passieren unter einer Sperre (jobengine.start_job_exklusiv) — vorher lag zwischen Prüfung und
|
||||
Start ein langsamer Aufruf, und zwei Klicks konnten zwei Updates starten."""
|
||||
job_id, laeuft = jobengine.start_job_exklusiv("maintenance", args, label, on_done=on_done,
|
||||
zeitlimit_s=zeitlimit_s)
|
||||
job_id, laeuft = jobengine.start_job_exklusiv("maintenance", args, label, zeitlimit_s=zeitlimit_s,
|
||||
nacharbeit="wartung:nach_update")
|
||||
if job_id is None:
|
||||
return {"ok": False, "status": "busy", "running": (laeuft or {}).get("label"),
|
||||
"detail": f"Es läuft schon: {(laeuft or {}).get('label', 'ein Update')}."}
|
||||
@@ -584,7 +585,7 @@ def check_updates_job() -> dict:
|
||||
# apt-get update gehört in den Wartungs-Riegel: parallel zu einem apt upgrade kollidiert es mit
|
||||
# der dpkg-Sperre. In der Gruppe taucht der Job auch in der Oberfläche auf.
|
||||
return _starten(["bash", "-c", "sudo apt-get update"], "Nach Updates suchen",
|
||||
on_done=_nach_update, zeitlimit_s=15 * 60)
|
||||
zeitlimit_s=15 * 60)
|
||||
|
||||
|
||||
def os_update_job() -> dict:
|
||||
@@ -596,7 +597,7 @@ def os_update_job() -> dict:
|
||||
cmd = ("sudo apt-get update && "
|
||||
"sudo bash -c 'DEBIAN_FRONTEND=noninteractive apt-get upgrade -y' "
|
||||
f"&& bash {STACK_POSTCHECK}")
|
||||
return _starten(["bash", "-c", cmd], "OS-Update (apt)", on_done=_nach_update, zeitlimit_s=90 * 60)
|
||||
return _starten(["bash", "-c", cmd], "OS-Update (apt)", zeitlimit_s=90 * 60)
|
||||
|
||||
|
||||
def engine_update_job() -> dict | None:
|
||||
@@ -606,7 +607,7 @@ def engine_update_job() -> dict | None:
|
||||
# Stack (stack-postcheck.sh) und rollt bei Fehler selbst zurück. Exit 0 nur bei verifiziertem
|
||||
# neuen Build. KEIN sudo-Passwort-Gate: das Skript ist per sudoers NOPASSWD freigegeben.
|
||||
return _starten(["bash", "-c", ENGINE_UPDATE_CMD], "Engine-Update (llama.cpp Vulkan)",
|
||||
on_done=_nach_update, zeitlimit_s=60 * 60)
|
||||
zeitlimit_s=60 * 60)
|
||||
|
||||
|
||||
def swap_update_job() -> dict | None:
|
||||
@@ -615,7 +616,7 @@ def swap_update_job() -> dict | None:
|
||||
# update-swap.sh sichert die alte Binary, aktualisiert, startet llama-swap neu, prüft den Stack
|
||||
# und rollt bei Fehler selbst zurück. Exit 0 nur bei verifizierter neuer Version.
|
||||
return _starten(["bash", "-c", SWAP_UPDATE_CMD], "Router-Update (llama-swap)",
|
||||
on_done=_nach_update, zeitlimit_s=30 * 60)
|
||||
zeitlimit_s=30 * 60)
|
||||
|
||||
|
||||
def _hermes_update_cmd() -> str:
|
||||
@@ -666,7 +667,7 @@ def hermes_update_job() -> dict:
|
||||
den Gateway neu starten. Davor ein Sicherheits-Backup (unser deploy/backup.sh). Kein sudo
|
||||
(alles im User-Space). Läuft als Hintergrund-Job (kann einige Minuten dauern)."""
|
||||
return _starten(["bash", "-c", _hermes_update_cmd()], "Hermes-Agent-Update",
|
||||
on_done=_nach_update, zeitlimit_s=45 * 60)
|
||||
zeitlimit_s=45 * 60)
|
||||
|
||||
|
||||
def update_all_job() -> dict:
|
||||
@@ -711,7 +712,7 @@ def update_all_job() -> dict:
|
||||
|
||||
labels = " → ".join(label for label, _ in parts)
|
||||
ergebnis = _starten(["bash", "-c", cmd], f"Alle aktualisieren ({labels})",
|
||||
on_done=_nach_update, zeitlimit_s=3 * 3600)
|
||||
zeitlimit_s=3 * 3600)
|
||||
if ergebnis.get("ok"):
|
||||
ergebnis["parts"] = [label for label, _ in parts]
|
||||
return ergebnis
|
||||
|
||||
Reference in New Issue
Block a user