Welle 1 · Vorhersage „voll in etwa N Tagen“: Pool, NAS, Systemplatte, Container, VMs und die Platte der KI-Box

Aus dem Verlauf der Woche (Tagesmittel, Schutz gegen einmalige Sprünge, schneller werdendes Volllaufen zählt sofort):
gelb unter 14, rot unter 3 Tagen. Der Wächter meldet es, auch wenn der Füllstand selbst noch unauffällig ist;
Speicherkarte, Geräte-Tabelle, Instrumente der Übersicht und die KI-Box-Seite zeigen es an. Jeder Bericht des
Ausführers schreibt dafür die Belegungen in die Messreihe „speicher“.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-25 16:21:38 +02:00
co-authored by Claude Opus 5.5
parent 34360c9644
commit f7a9b8ad50
19 changed files with 691 additions and 72 deletions
+9 -7
View File
@@ -24,7 +24,7 @@ import time
from kern import messreihen
from services.homelab import apps, inventar, kanal
from services.homelab import apps, inventar, kanal, speicher
HOST = "pve"
_GAST = re.compile(r"(ct|vm)-\d+")
@@ -199,18 +199,20 @@ def _geraete(von: float, bis: float) -> list[dict]:
def abfrage(zeitraum: str, jetzt: float | None = None) -> dict:
"""Für GET /api/homelab/messwerte: {"zeitraum", "schritt_s", "geraete": [{"id", "name", "art", "reihen",
"aktuell"}]}. temp_cpu gibt es als Reihe nur beim Host; in „aktuell“ stehen temp_cpu und load1 bei Gästen auf null."""
"aktuell", "prognose"}]}. temp_cpu gibt es als Reihe nur beim Host; in „aktuell“ stehen temp_cpu und load1 bei
Gästen auf null. prognose.platte: „voll in etwa N Tagen“ (kern/prognose.py, seit 25.09.2026)."""
dauer, schritt = messreihen.zeitraum_pruefen(zeitraum)
jetzt = time.time() if jetzt is None else jetzt
zeit = time.time() if jetzt is None else jetzt
geraete = []
for geraet in _geraete(jetzt - dauer, jetzt):
for geraet in _geraete(zeit - dauer, zeit):
host = geraet["id"] == HOST
daten = messreihen.lesen(geraet["id"], zeitraum, HOST_REIHEN if host else GAST_REIHEN, jetzt)
letzter = messreihen.letzter_punkt(geraet["id"], jetzt, AKTUELL_MAX_S) or {}
daten = messreihen.lesen(geraet["id"], zeitraum, HOST_REIHEN if host else GAST_REIHEN, zeit)
letzter = messreihen.letzter_punkt(geraet["id"], zeit, AKTUELL_MAX_S) or {}
aktuell = {name: letzter.get(name) for name in AKTUELL}
if not host:
aktuell["temp_cpu"] = aktuell["load1"] = None
geraete.append({**geraet, "reihen": daten["reihen"], "aktuell": aktuell})
geraete.append({**geraet, "reihen": daten["reihen"], "aktuell": aktuell,
"prognose": {"platte": speicher.platten_vorhersage(geraet["id"], jetzt)}})
return {"zeitraum": zeitraum, "schritt_s": schritt, "geraete": geraete}
+127 -25
View File
@@ -11,20 +11,27 @@ Alles aus dem Bericht des Ausführers (alle 10 Minuten), dazu eine eigene Prüfu
(täglicher Auftrag: höchstens 26 Stunden alt, sonst 8 Tage), und das Ziel ist erreichbar.
• Systemplatte des Proxmox-Hosts (Stufen wie oben): Dort liegen die Kernel, die Sicherungen vor Updates (local) und
alles vom Host selbst. Alte Kernel lassen sich per Knopf entfernen (Ausführer: kernel_aufraeumen).
• Tempo (seit 25.09.2026, kern/prognose.py): Läuft etwas in weniger als 14 Tagen voll, ist es gelb, unter 3 Tagen
rot — auch wenn der Füllstand selbst noch unauffällig ist. Den Verlauf dafür schreibt jeder Bericht in die
Messreihe VERLAUF (verlauf_merken); Host und Container haben ihn schon in den Minutenwerten.
Hinweise gehen über den Wächter (pruefe_speicher), die Oberfläche zeigt alles als Karte „Speicher und Sicherungen“ (lage).
"""
import logging
import re
import socket
import threading
import time
from datetime import datetime
from kern import messreihen, prognose
from kern.zeit import LOCAL_TZ
from services.homelab import apps, kanal
from services.homelab.inventar import EIGENES_ETIKETT
log = logging.getLogger(__name__)
GELB, ROT = 0.80, 0.90
EMPFEHLUNG_AB = 1_000_000_000 # kleinere Gewinne sind keine eigene Empfehlung wert
TRIM_AB = 250_000_000 # ein Container zählt beim gemeinsamen TRIM erst ab hier mit
@@ -320,60 +327,155 @@ def sicherungen(bericht: dict, jetzt: float) -> dict:
"erfasst": erfasst, "fehlend": fehlend, "zuletzt": zuletzt, "probleme": probleme, "stufe": stufe}
# --- Vorhersage „voll in etwa N Tagen“ (seit 25.09.2026) --------------------------------------------------------
VERLAUF = "speicher" # Quelle in kern/messreihen.py: die Belegungen aus jedem Bericht (alle zehn Minuten)
def _prozent(belegt: object, gesamt: object) -> float | None:
if not (messreihen.ist_zahl(belegt) and messreihen.ist_zahl(gesamt)) or gesamt <= 0:
return None
return round(belegt / gesamt * 100, 2)
def verlauf_merken(bericht: dict, jetzt: float | None = None) -> None:
"""Die Belegungen eines Berichts als ein Punkt der Messreihe VERLAUF (in %): je Speicherpool Daten
(„pool:<name>“) und Metadaten („pool-meta:<name>“), je Netzlaufwerk („nas:<ziel>“), je Sicherungsziel
(„ziel:<name>“) und die Platte jeder VM („vm-<vmid>“ — die Minutenwerte kennen sie nicht). Ein kaputter Bericht
kostet nur diesen Punkt."""
try:
host = bericht.get("host") if isinstance(bericht.get("host"), dict) else {}
werte: dict[str, float | None] = {}
for p in pools(bericht):
werte[f"pool:{p['speicher']}"] = round(p["anteil"] * 100, 2)
if messreihen.ist_zahl(p["meta_anteil"]):
werte[f"pool-meta:{p['speicher']}"] = round(p["meta_anteil"] * 100, 2)
for m in host.get("netzlaufwerke") or []:
platz = m.get("platz") or {}
werte[f"nas:{m.get('ziel')}"] = _prozent(platz.get("belegt"), platz.get("gesamt"))
for name, z in (host.get("sicherungsziele") or {}).items():
werte[f"ziel:{name}"] = _prozent(z.get("belegt"), z.get("gesamt"))
for g in bericht.get("gaeste") or []:
if g.get("art") == "qemu":
platte = g.get("platte") or {}
werte[f"vm-{g['vmid']}"] = _prozent(platte.get("belegt"), platte.get("gesamt"))
werte = {name: wert for name, wert in werte.items() if wert is not None}
if werte:
messreihen.schreiben(VERLAUF, werte, jetzt)
except Exception:
log.warning("speicher: Verlauf der Belegungen nicht geschrieben", exc_info=True)
def platten_reihe(geraet_id: str) -> tuple[str, str, float] | None:
"""Wo der Verlauf der Platte eines Geräts liegt: (Quelle, Name, voll bei %). Host und Container in den
Minutenwerten; VMs im VERLAUF, denn ihre Platte kennt nur der Bericht (über den Gast-Agenten)."""
if geraet_id == "pve":
return "pve", "platte", prognose.VOLL_EXT4
if re.fullmatch(r"ct-\d+", geraet_id):
return geraet_id, "platte", prognose.VOLL_EXT4
if re.fullmatch(r"vm-\d+", geraet_id):
return VERLAUF, geraet_id, prognose.VOLL_EXT4
return None
def platten_vorhersage(geraet_id: str, jetzt: float | None = None) -> dict | None:
reihe = platten_reihe(geraet_id)
return prognose.aus_messreihe(*reihe, jetzt=jetzt) if reihe else None
def _vorhersage(name: str, jetzt: float | None) -> dict:
return prognose.aus_messreihe(VERLAUF, name, jetzt=jetzt)
def pool_vorhersage(speicher: str, jetzt: float | None = None) -> tuple[dict, bool]:
"""Die Vorhersage eines Pools — Daten oder Metadaten, was früher voll ist — und ob es die Metadaten sind."""
daten, meta = _vorhersage(f"pool:{speicher}", jetzt), _vorhersage(f"pool-meta:{speicher}", jetzt)
return (meta, True) if prognose.bis_voll(meta) < prognose.bis_voll(daten) else (daten, False)
# --- Für Oberfläche und Wächter ----------------------------------------------------------------------------------
def lage(jetzt: float | None = None) -> dict:
"""Für GET /api/homelab/speicher. Pools, Systemplatte, NAS und Sicherungsziele tragen seit 25.09.2026 je eine
„vorhersage“ (kern/prognose.vorhersage). jetzt nur für Tests: Dann wird die Vorhersage nicht gemerkt."""
b = _bericht()
jetzt = time.time() if jetzt is None else jetzt
return {"bericht": bool(b), "pools": pools(b), "systemplatte": systemplatte(b), "nas": nas(b),
"sicherungen": sicherungen(b, jetzt), "empfehlungen": empfehlungen(b)}
zeit = time.time() if jetzt is None else jetzt
ergebnis = {"bericht": bool(b), "pools": pools(b), "systemplatte": systemplatte(b), "nas": nas(b),
"sicherungen": sicherungen(b, zeit), "empfehlungen": empfehlungen(b)}
for p in ergebnis["pools"]:
p["vorhersage"] = pool_vorhersage(p["speicher"], jetzt)[0]
if ergebnis["systemplatte"]:
ergebnis["systemplatte"]["vorhersage"] = platten_vorhersage("pve", jetzt)
for n in ergebnis["nas"]:
n["vorhersage"] = None if n["problem"] else _vorhersage(f"nas:{n['ziel']}", jetzt)
for z in ergebnis["sicherungen"]["ziele"]:
z["vorhersage"] = _vorhersage(f"ziel:{z['speicher']}", jetzt)
return ergebnis
def pruefe_speicher(jetzt: float | None = None) -> list:
"""Befunde für den Wächter. Ohne Bericht keine — das Schweigen des Ausführers meldet pruefe_ausfuehrer()."""
"""Befunde für den Wächter. Ohne Bericht keine — das Schweigen des Ausführers meldet pruefe_ausfuehrer().
Pool, Systemplatte und NAS melden sich auch nach dem Tempo (siehe oben); bestimmt die Vorhersage die Stufe, steht
„in etwa N Tagen voll“ im Titel, sonst hängt der Text sie an."""
from services.waechter import Befund # der Wächter registriert diese Prüfung, also erst hier
b = _bericht()
if not b:
return []
jetzt = time.time() if jetzt is None else jetzt
zeit = time.time() if jetzt is None else jetzt
befunde = []
tipps = empfehlungen(b)
platz_tipp = "; ".join([e["kurz"] for e in tipps if e.get("ort") == "pool"][:3])
for p in pools(b):
if p["stufe"] == "ok":
v, meta = pool_vorhersage(p["speicher"], jetzt)
stufe, kommend = prognose.schlimmer(p["stufe"], v)
if stufe == "ok":
continue
anteil = max(p["anteil"], p["meta_anteil"] or 0)
was = "Metadaten des Speicherpools" if (p["meta_anteil"] or 0) > p["anteil"] else "Speicherpool"
befunde.append(Befund(
id=f"pool:{p['speicher']}", stufe=p["stufe"],
titel=f"{was} {p['speicher']} zu {min(100, round(anteil * 100))} % voll",
text=(f"Noch {_groesse(p['frei'])} frei. Läuft er voll, stehen alle Gäste darauf still. "
+ (f"Platz bringt: {platz_tipp}. " if platz_tipp else "")
+ "Alles dazu im Homelab-Cockpit unter „Speicher und Sicherungen“."),
quelle="pve"))
rest = ("Läuft er voll, stehen alle Gäste darauf still. " + (f"Platz bringt: {platz_tipp}. " if platz_tipp else "")
+ "Alles dazu im Homelab-Cockpit unter „Speicher und Sicherungen“.")
if kommend:
was = "Metadaten des Speicherpools" if meta else "Speicherpool"
titel = f"{was} {p['speicher']} {prognose.in_tagen(v['tage'])} voll"
text = f"{prognose.tempo(v)} Noch {_groesse(p['frei'])} frei. {rest}"
else:
anteil = max(p["anteil"], p["meta_anteil"] or 0)
was = "Metadaten des Speicherpools" if (p["meta_anteil"] or 0) > p["anteil"] else "Speicherpool"
titel = f"{was} {p['speicher']} zu {min(100, round(anteil * 100))} % voll"
text = f"Noch {_groesse(p['frei'])} frei.{prognose.dazu(v)} {rest}"
befunde.append(Befund(id=f"pool:{p['speicher']}", stufe=stufe, titel=titel, text=text, quelle="pve"))
sp = systemplatte(b)
if sp and sp["stufe"] != "ok":
v = platten_vorhersage("pve", jetzt) if sp else None
stufe, kommend = prognose.schlimmer(sp["stufe"], v) if sp else ("ok", False)
if sp and stufe != "ok":
kernel = next((e for e in tipps if e["art"] == "kernel"), None)
befunde.append(Befund(
id="host-platte", stufe=sp["stufe"],
titel=f"Systemplatte des Proxmox-Hosts zu {min(100, round(sp['anteil'] * 100))} % voll",
text=(f"Noch {_groesse(sp['frei'])} frei. Läuft sie voll, scheitern Updates und die Sicherungen vor Updates. "
id="host-platte", stufe=stufe,
titel=(f"Systemplatte des Proxmox-Hosts {prognose.in_tagen(v['tage'])} voll" if kommend
else f"Systemplatte des Proxmox-Hosts zu {min(100, round(sp['anteil'] * 100))} % voll"),
text=((f"{prognose.tempo(v)} " if kommend else "") + f"Noch {_groesse(sp['frei'])} frei."
+ ("" if kommend else prognose.dazu(v))
+ " Läuft sie voll, scheitern Updates und die Sicherungen vor Updates. "
+ (f"Platz bringt: {kernel['kurz']} (Knopf im Homelab-Cockpit unter „Speicher und Sicherungen“)."
if kernel else "Alte Sicherungen unter /var/lib/vz/dump und ISO-Abbilder prüfen.")),
quelle="pve"))
for n in nas(b):
if n["stufe"] == "ok":
v = None if n["problem"] else _vorhersage(f"nas:{n['ziel']}", jetzt)
stufe, kommend = prognose.schlimmer(n["stufe"], v)
if stufe == "ok":
continue
frei = _groesse((n["gesamt"] or 0) - (n["belegt"] or 0))
platz = ("Die Sicherungen brauchen jede Nacht Platz: im PBS die Aufbewahrung kürzen (Datastore → Prune) oder "
"alte Dateien auf dem NAS aufräumen.")
if n["problem"]:
titel, text = n["problem"], ("Die nächtlichen Sicherungen landen dort; bis das behoben ist, scheitern sie. "
"Strom und Netz des NAS prüfen, dann in dessen Oberfläche nachsehen.")
elif kommend:
titel = f"NAS ({n['server']}) {prognose.in_tagen(v['tage'])} voll"
text = f"{prognose.tempo(v)} Noch {frei} frei. {platz}"
else:
titel = f"NAS ({n['server']}) zu {min(100, round((n['anteil'] or 0) * 100))} % voll"
text = (f"Noch {_groesse((n['gesamt'] or 0) - (n['belegt'] or 0))} frei. Die Sicherungen brauchen jede "
"Nacht Platz: im PBS die Aufbewahrung kürzen (Datastore → Prune) oder alte Dateien auf dem NAS "
"aufräumen.")
befunde.append(Befund(id=f"nas:{n['ziel']}", stufe=n["stufe"], titel=titel, text=text, quelle="nas"))
s = sicherungen(b, jetzt)
text = f"Noch {frei} frei.{prognose.dazu(v)} {platz}"
befunde.append(Befund(id=f"nas:{n['ziel']}", stufe=stufe, titel=titel, text=text, quelle="nas"))
s = sicherungen(b, zeit)
if s["stufe"] in ("gelb", "rot"):
teile = list(s["probleme"])
if s["fehlend"]:
+9 -7
View File
@@ -25,7 +25,7 @@ from collections.abc import Callable
import psutil
from config import MODELS_DIR
from kern import messreihen
from kern import messreihen, prognose
from services import system, token_stats
@@ -176,9 +176,11 @@ ZEITRAEUME = messreihen.ZEITRAEUME
def abfrage(zeitraum: str, jetzt: float | None = None) -> dict:
"""Für GET /api/messwerte: {"zeitraum", "schritt_s", "reihen": {…}, "aktuell": {…}}. aktuell ist der letzte
Minutenpunkt, solange er höchstens AKTUELL_MAX_S alt ist, sonst lauter null."""
jetzt = time.time() if jetzt is None else jetzt
daten = messreihen.lesen(QUELLE, zeitraum, REIHEN, jetzt)
letzter = messreihen.letzter_punkt(QUELLE, jetzt, AKTUELL_MAX_S) or {}
return {**daten, "aktuell": {name: letzter.get(name) for name in REIHEN}}
"""Für GET /api/messwerte: {"zeitraum", "schritt_s", "reihen": {…}, "aktuell": {…}, "prognose": {"platte": …}}.
aktuell ist der letzte Minutenpunkt, solange er höchstens AKTUELL_MAX_S alt ist, sonst lauter null; prognose
die Vorhersage „voll in etwa N Tagen“ des Modell-Laufwerks (kern/prognose.py, seit 25.09.2026)."""
zeit = time.time() if jetzt is None else jetzt
daten = messreihen.lesen(QUELLE, zeitraum, REIHEN, zeit)
letzter = messreihen.letzter_punkt(QUELLE, zeit, AKTUELL_MAX_S) or {}
return {**daten, "aktuell": {name: letzter.get(name) for name in REIHEN},
"prognose": {"platte": prognose.aus_messreihe(QUELLE, "platte", jetzt=jetzt)}}
+42 -22
View File
@@ -9,7 +9,7 @@ gesehen. Dieser Wächter schaut deshalb breiter hin:
• Timer-Läufe Einmal-Dienste hinter Timern (projekte-sync, Sicherung)
• Hermes-Jobs Status der Cron-Jobs + Werkzeugfehler im letzten Lauf (errors.log)
• Kern Engine, Hirn, Hermes, Hör-Dienst, MC2, Gateway antworten per HTTP
• Platte Füllstand des Modell-Laufwerks
• Platte Füllstand des Modell-Laufwerks, seit 25.09.2026 auch das Tempo („in etwa N Tagen voll“)
• Updates Bausteine, die der Sonntags-Lauf nach einem Fehlschlag festhält
• Probe monatliche Probe-Wiederherstellung der Sicherung (rot oder zu alt = gelb)
@@ -44,7 +44,7 @@ from pathlib import Path
import httpx
import psutil
from config import HERMES_API_KEY, HERMES_API_URL, HERMES_HOME, VOICE_SERVICE_URL
from kern import partner
from kern import partner, prognose
from kern.einstellungen import einstellungen
from kern.zeit import LOCAL_TZ
@@ -454,19 +454,29 @@ def pruefe_kern() -> list[Befund]:
def pruefe_platte() -> list[Befund]:
"""Füllstand der eigenen Platte (gelb ab DISK_GELB_PCT, rot ab DISK_ROT_PCT). Auf der KI-Box seit 25.09.2026
auch das Tempo des Modell-Laufwerks (kern/prognose.py): in weniger als 14 Tagen voll = gelb, unter 3 Tagen rot —
als eigener Hinweis, wenn er ernster ist als der Füllstand."""
befunde, stufe = [], "ok"
try:
pct = psutil.disk_usage(str(DATEN_DIR) if DATEN_DIR.exists() else os.getcwd()).percent
except Exception:
return []
if pct >= DISK_ROT_PCT:
stufe = "rot"
elif pct >= DISK_GELB_PCT:
stufe = "gelb"
else:
return []
return [Befund(id="platte", stufe=stufe, titel=f"Die Platte ist zu {pct:.0f} % voll",
text="Es wird eng für Modelle und Sicherungen. Alte Modelldateien löschen hilft am meisten.",
quelle="platte", sofort=True)]
pct = None
if pct is not None and pct >= DISK_GELB_PCT:
stufe = "rot" if pct >= DISK_ROT_PCT else "gelb"
befunde.append(Befund(id="platte", stufe=stufe, titel=f"Die Platte ist zu {pct:.0f} % voll",
text="Es wird eng für Modelle und Sicherungen. Alte Modelldateien löschen hilft am meisten.",
quelle="platte", sofort=True))
if ROLLE == "box":
from services import messwerte
v = prognose.aus_messreihe(messwerte.QUELLE, "platte")
kuenftig, kommend = prognose.schlimmer(stufe, v)
if kommend:
befunde.append(Befund(
id="platte:tempo", stufe=kuenftig, titel=f"Die Platte ist {prognose.in_tagen(v['tage'])} voll",
text=f"{prognose.tempo(v)} Alte Modelldateien löschen hilft am meisten: KI-Box → Modelle → Aufräumen.",
quelle="platte"))
return befunde
def pruefe_probe_wiederherstellung() -> list[Befund]:
@@ -563,28 +573,38 @@ def _gb_komma(n: float) -> str:
def pruefe_gast_platten() -> list[Befund]:
"""Homelab: Läuft die Platte eines Containers voll? Am 24.09.2026 stand AdGuard bei 100 %: Das Abfrageprotokoll
schrieb nicht mehr, die Paketsuche scheiterte — und keine Anzeige sagte es."""
from services.homelab import apps, kanal
schrieb nicht mehr, die Paketsuche scheiterte — und keine Anzeige sagte es. Seit 25.09.2026 auch nach dem Tempo
(kern/prognose.py): in weniger als 14 Tagen voll = gelb, unter 3 Tagen rot."""
from services.homelab import apps, kanal, speicher
daten = kanal.bericht()
befunde = []
for g in (daten or {}).get("bericht", {}).get("gaeste") or []:
platte = g.get("platte") or {}
gesamt, belegt = platte.get("gesamt"), platte.get("belegt") or 0
if not gesamt or belegt / gesamt < GAST_PLATTE_GELB:
if not gesamt:
continue
app = apps.app_fuer((g.get("app") or {}).get("kennung"), g.get("name"))
name = app.name if app else str(g.get("name") or g["vmid"])
anteil = belegt / gesamt
# Seit 25.09.2026 kennt der Bericht auch die Platte der VMs (über den Gast-Agenten).
vm = g.get("art") != "lxc"
zid = f"{'vm' if vm else 'ct'}-{g['vmid']}"
v = speicher.platten_vorhersage(zid)
jetzt_stufe = "rot" if anteil >= GAST_PLATTE_ROT else "gelb" if anteil >= GAST_PLATTE_GELB else "ok"
stufe, kommend = prognose.schlimmer(jetzt_stufe, v)
if stufe == "ok":
continue
app = apps.app_fuer((g.get("app") or {}).get("kennung"), g.get("name"))
name = app.name if app else str(g.get("name") or g["vmid"])
vergroessern = ("VM → Hardware → Festplatte → Aktion → Größe ändern, danach in der VM das Dateisystem "
"mitwachsen lassen" if vm else "Container → Ressourcen → Laufwerk → Größe ändern")
befunde.append(Befund(
id=f"gast-platte:{g['vmid']}", stufe="rot" if anteil >= GAST_PLATTE_ROT else "gelb",
titel=f"Platte von {name} zu {min(100, round(anteil * 100))} % voll",
text=(f"{_gb_komma(belegt)} von {_gb_komma(gesamt)} GB belegt ({'VM' if vm else 'Container'} {g['vmid']}). "
f"Platz schaffen oder in Proxmox die Platte vergrößern: {vergroessern}."),
quelle=f"{'vm' if vm else 'ct'}-{g['vmid']}"))
id=f"gast-platte:{g['vmid']}", stufe=stufe,
titel=(f"Platte von {name} {prognose.in_tagen(v['tage'])} voll" if kommend
else f"Platte von {name} zu {min(100, round(anteil * 100))} % voll"),
text=((f"{prognose.tempo(v)} " if kommend else "")
+ f"{_gb_komma(belegt)} von {_gb_komma(gesamt)} GB belegt ({'VM' if vm else 'Container'} {g['vmid']})."
+ ("" if kommend else prognose.dazu(v))
+ f" Platz schaffen oder in Proxmox die Platte vergrößern: {vergroessern}."),
quelle=zid))
return befunde