Ajout section NAS (disques, docker, port VPN) via nas_monitor /api/status

This commit is contained in:
jerem
2026-06-15 14:58:56 +02:00
parent 8dad6454f5
commit ca8f725b3a
5 changed files with 124 additions and 2 deletions

View File

@@ -0,0 +1,96 @@
"""Statuts du NAS via le moniteur maison `nas_monitor` (`GET /api/status`).
Expose : occupation des disques de données (`/mnt/*`), santé des conteneurs Docker,
et l'état du port forwardé entre gluetun (VPN) et qBittorrent.
"""
from __future__ import annotations
from dataclasses import dataclass, field
import httpx
from config import config
@dataclass
class NasDisk:
label: str
percent: float
free_gb: float
@property
def free_human(self) -> str:
if self.free_gb >= 1024:
return f"{self.free_gb / 1024:.1f}".replace(".", ",") + " To"
return f"{self.free_gb:.0f} Go"
@dataclass
class NasStatus:
ok: bool
error: str | None = None
disks: list[NasDisk] = field(default_factory=list)
docker_running: int = 0
docker_total: int = 0
docker_unhealthy: int = 0
vpn_port: int | None = None
vpn_ok: bool = False
def _disk_label(mount: str) -> str:
"""`/mnt/disk1` -> `Disque 1`, sinon le dernier segment capitalisé."""
base = mount.rstrip("/").split("/")[-1]
if base.startswith("disk"):
return "Disque " + base[len("disk"):]
return base.capitalize() or mount
def _as_int(value: object) -> int | None:
try:
return int(value) # type: ignore[arg-type]
except (TypeError, ValueError):
return None
async def fetch_status() -> NasStatus:
if not config.nas_url:
return NasStatus(ok=False, error="NAS désactivé")
try:
async with httpx.AsyncClient(timeout=15) as client:
resp = await client.get(config.nas_url)
if resp.status_code != 200:
return NasStatus(ok=False, error=f"HTTP {resp.status_code}")
data = resp.json()
except httpx.HTTPError:
return NasStatus(ok=False, error="injoignable")
metrics = (data.get("host") or {}).get("metrics") or {}
disks: list[NasDisk] = []
for disk in metrics.get("disks") or []:
mount = str(disk.get("mount", ""))
if not mount.startswith("/mnt"): # on ne garde que les disques de données
continue
disks.append(NasDisk(
label=_disk_label(mount),
percent=float(disk.get("percent", 0) or 0),
free_gb=float(disk.get("free_gb", 0) or 0),
))
summary = (data.get("docker") or {}).get("summary") or {}
# Port VPN : OK quand le port forwardé par gluetun == port d'écoute qBittorrent.
qb = data.get("qbittorrent") or {}
listen = _as_int((qb.get("prefs") or {}).get("listen_port"))
fwd = _as_int(qb.get("forwarded_port_file"))
vpn_ok = listen is not None and fwd is not None and listen == fwd
return NasStatus(
ok=True,
disks=disks,
docker_running=int(summary.get("running", 0) or 0),
docker_total=int(summary.get("total", 0) or 0),
docker_unhealthy=int(summary.get("unhealthy", 0) or 0),
vpn_port=fwd if fwd is not None else listen,
vpn_ok=vpn_ok,
)