Ajout section NAS (disques, docker, port VPN) via nas_monitor /api/status
This commit is contained in:
96
backend/integrations/nas.py
Normal file
96
backend/integrations/nas.py
Normal 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,
|
||||
)
|
||||
Reference in New Issue
Block a user