Files
mission-control-v2/backend/kern/ziele.py
T
HitonabiandClaude Opus 5.5 1c66daad0b Welle 2 · Sicherheits-Markierung: Debian-Sicherheitsupdates, Alpine über secdb, Lücken-Scans aus Arcane
Der Ausführer zählt je Gast, wie viele offene Updates aus einer Sicherheits-Suite kommen („-security“ in apt list),
beim Proxmox-Host ebenso; für Alpine (NPMplus) schickt er die Paketliste, und der Homelab-Teil gleicht sie mit Alpines
Sicherheitsdatenbank ab (im Hintergrund geladen, zwölf Stunden gemerkt, Alpine-Versionsvergleich). Die Update-Tabelle,
die Kacheln und die Übersicht zeigen „· 3 Sicherheit“, die Rückfrage nennt es. Je Docker-Container steht, was Arcanes
Lücken-Scan (Trivy, sonntags 02:00) im Image fand, kritische und hohe. Der Ausführer braucht ausfuehrer-einrichten.sh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 18:02:33 +02:00

65 lines
3.2 KiB
Python

"""Ziele und Bausteine (Phase 2, 24.09.2026): ein Modell für alles, was Updates bekommt.
Ein Ziel ist ein Gerät: die KI-Box, der Proxmox-Host, ein Container, die Arcane-VM. Es besteht aus
Bausteinen (Betriebssystem, Motor, die App im Container, Docker-Images …). Jeder Baustein sagt, wie
sein Stand ist — neu, aktuell, unbekannt (mit Grund) oder festgehalten — und wie man ihn einspielt.
Die KI-Box meldet ihre Bausteine über den Box-Adapter (services/box_updates.py), der Homelab-Teil
ab Phase 3 über Proxmox- und Arcane-Adapter. So wird aus beiden Instanzen EINE Liste offener Updates
(User-Entscheid 24.09.: eine Anlaufstelle für Updates im ganzen Homelab).
"""
from dataclasses import asdict, dataclass, field
from typing import Literal
Zustand = Literal["neu", "aktuell", "unbekannt", "festgehalten", "wird-geprueft"]
# Reihenfolge für die Anzeige: was Aufmerksamkeit braucht, zuerst.
_RANG = {"unbekannt": 0, "festgehalten": 1, "neu": 2, "wird-geprueft": 3, "aktuell": 4}
@dataclass
class Aktion:
"""Wie ein Update angestoßen wird: eine Schnittstelle der Instanz, die das Ziel pflegt."""
methode: str # "POST"
pfad: str # "/api/maintenance/engine-update"
frage: str # Rückfrage vor dem Klick
label: str = "Jetzt updaten"
@dataclass
class BausteinStand:
id: str # "engine"
name: str # "Motor (llama.cpp)"
zustand: Zustand
installiert: str | None = None
verfuegbar: str | None = None
kurz: str = "" # „10 Pakete", „b11160 → b11172", „743 Änderungen"
grund: str | None = None # bei „unbekannt" und „festgehalten": warum
aktion: Aktion | None = None # None = kein Knopf (aktuell, festgehalten, unbekannt)
sicherheit: int | None = None # davon Sicherheitsupdates (seit 25.09.2026; None = unbekannt)
@dataclass
class ZielStand:
id: str # "ki-box"
name: str # "KI-Box"
art: str # "ki-box" | "proxmox-host" | "container" | "vm"
instanz: str # welche Instanz es pflegt: "box" | "homelab"
erreichbar: bool | None = None # None = nicht geprüft
bausteine: list[BausteinStand] = field(default_factory=list)
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)
kennzahl: dict | None = None # eine Live-Zahl für die Kachel {"text", "zeit"?, "stufe"?} (seit 25.09.2026)
def als_dict(self) -> dict:
daten = asdict(self)
daten["bausteine"].sort(key=lambda b: _RANG.get(b["zustand"], 9))
zaehlung: dict[str, int] = {}
for b in self.bausteine:
zaehlung[b.zustand] = zaehlung.get(b.zustand, 0) + 1
daten["zaehlung"] = zaehlung
daten["offen"] = zaehlung.get("neu", 0)
return daten