homelab: Speicherpool, NAS und Sicherungen überwachen – mit Empfehlungen, was Platz bringt

User-Wunsch 25.09.: Speicherpool im Wächter (gelb ab 80 %, rot bei 10 % frei)
samt Empfehlungen, dazu das NAS (Backup-Ziel, per NFS auf dem Proxmox-Host).
- Ausführer: Thin-Pools mit echter Belegung je Volume (lvs), ungenutzte und
  verwaiste Platten, VM-Belegung über den Gast-Agenten, Discard je VM,
  Netzlaufwerke (df mit Zeitlimit, hängender harter NFS-Mount hält nichts auf),
  Sicherungsaufträge und jüngste Sicherung je Gast.
- Homelab-Teil: services/homelab/speicher.py (Auswertung, Empfehlungen,
  Wächter pruefe_speicher, NAS-Erreichbarkeit per TCP), GET /api/homelab/speicher;
  Gast-Plattenwächter nennt jetzt auch VMs richtig.
- Oberfläche: Karte „Speicher und Sicherungen“ im Homelab-Cockpit.
- deploy.sh und ausfuehrer-einrichten.sh warten, solange im Homelab ein Update
  oder „Alle aktualisieren“ läuft (der Neustart bräche den Lauf ab).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-25 11:15:06 +02:00
co-authored by Claude Opus 5.5
parent 9867affc5a
commit 903fbbfbb9
43 changed files with 1249 additions and 40 deletions
+11 -1
View File
@@ -10,7 +10,7 @@ import { melden } from "./meldungen"
import { stromVerbunden } from "./strom"
import type {
AktionsErgebnis, Altrest, AufraeumKandidat, BoxMesswerte, EinstellungenStand, Hinweis, HomelabEinstellungen,
HomelabLauf, HomelabMesswerte, HomelabStand, Instanz, InstanzMitStand, Job, Modell, Nutzung, PartnerStatus,
HomelabLauf, HomelabMesswerte, HomelabSpeicher, HomelabStand, Instanz, InstanzMitStand, Job, Modell, Nutzung, PartnerStatus,
ProtokollEintrag, Radar, SammelPlanSchritt, Sammellauf, Sicherung, Start, TelegramTest, UpdateDetails, ZielAktion,
ZielStand, Zeitraum,
} from "./typen"
@@ -423,6 +423,16 @@ export const useHomelabProtokoll = (aktiv: boolean, berichte: boolean) =>
retry: false,
})
/** Speicherpool, NAS und Sicherungen samt Empfehlungen — der Bericht dahinter kommt alle 10 Minuten. */
export const useHomelabSpeicher = (aktiv: boolean) =>
useQuery({
queryKey: ["homelab-speicher"],
queryFn: () => api<HomelabSpeicher>("/api/homelab/speicher"),
enabled: aktiv,
refetchInterval: takt(60_000),
retry: false,
})
export const useHomelabEinstellungen = (aktiv: boolean) =>
useQuery({
queryKey: ["homelab-einstellungen"],
+26
View File
@@ -0,0 +1,26 @@
import type { SpeicherStufe, SicherungsLage } from "@/lib/typen"
// Speicher und Sicherungen des Homelabs (seit 25.09.2026): die kleinen Texte der Karte im Homelab-Cockpit.
const komma = (n: number, stellen = 1) => n.toFixed(stellen).replace(".", ",")
/** „184 GB“, „5,2 GB“, „2,3 TB“ — ab 10 GB ohne Nachkommastelle. */
export function groesse(bytes: number | null | undefined): string {
if (bytes === null || bytes === undefined) return "–"
if (bytes >= 1e12) return `${komma(bytes / 1e12)} TB`
return bytes >= 10e9 ? `${Math.round(bytes / 1e9)} GB` : `${komma(bytes / 1e9)} GB`
}
/** Der Zeitplan eines Sicherungsauftrags in Worten: „02:30“ heißt bei Proxmox täglich um 02:30. */
export function zeitplanText(zeitplan: string | null): string {
if (!zeitplan) return "ohne Zeitplan"
return /^\s*\d{1,2}:\d{2}\s*$/.test(zeitplan) ? `täglich ${zeitplan.trim()}` : `Zeitplan „${zeitplan}“`
}
/** „täglich 02:30 auf pbs-qnap“ — alle Aufträge in einer Zeile. */
export function auftraegeText(s: SicherungsLage): string {
if (s.jobs.length === 0) return "Kein Sicherungsauftrag in Proxmox"
return s.jobs.map((j) => `${zeitplanText(j.zeitplan)} auf ${j.speicher ?? "?"}`).join(", ")
}
export const STUFE_TEXT: Record<SpeicherStufe, string> = { ok: "in Ordnung", gelb: "wird knapp", rot: "fast voll" }
+56
View File
@@ -461,3 +461,59 @@ export interface HomelabEinstellungen {
ausfuehrer: { verbunden: boolean; zuletzt: number | null; nur_lesen: boolean; sicherung_speicher: string | null }
}
// --- Speicher und Sicherungen des Homelabs (seit 25.09.2026) -------------------------------------------------
export type SpeicherStufe = "ok" | "gelb" | "rot"
export interface SpeicherPool {
speicher: string
belegt: number
gesamt: number
frei: number
anteil: number
meta_anteil: number | null
aktiv: boolean
stufe: SpeicherStufe
}
export interface NasStand {
server: string | null
ziel: string
quelle: string | null
art: string
eingebunden: boolean
zustand: "ok" | "haengt" | "fehler" | null
erreichbar: boolean | null
belegt: number | null
gesamt: number | null
anteil: number | null
stufe: SpeicherStufe
problem: string | null
}
export interface SicherungsLage {
jobs: { zeitplan: string | null; speicher: string | null; alle: boolean }[]
ziele: { speicher: string; art: string | null; aktiv: boolean | null; belegt: number | null; gesamt: number | null }[]
erfasst: number
fehlend: { ziel: string; name: string; zuletzt: number | null }[]
zuletzt: number | null
probleme: string[]
stufe: SpeicherStufe | "keine"
}
export interface SpeicherEmpfehlung {
art: "trim" | "trim-vm" | "ungenutzt" | "verwaist" | "snapshot"
titel: string
text: string
kurz: string
platz: number | null
}
export interface HomelabSpeicher {
bericht: boolean
pools: SpeicherPool[]
nas: NasStand[]
sicherungen: SicherungsLage
empfehlungen: SpeicherEmpfehlung[]
}