maj via codex
This commit is contained in:
@@ -1,10 +1,117 @@
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlmodel import Session, select
|
||||
from app.database import get_session
|
||||
from app.models.settings import UserSettings
|
||||
from app.config import UPLOAD_DIR
|
||||
|
||||
router = APIRouter(tags=["réglages"])
|
||||
|
||||
_PREV_CPU_USAGE_USEC: int | None = None
|
||||
_PREV_CPU_TS: float | None = None
|
||||
|
||||
|
||||
def _read_int_from_paths(paths: list[str]) -> int | None:
|
||||
for path in paths:
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
raw = f.read().strip().split()[0]
|
||||
return int(raw)
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def _read_cgroup_cpu_usage_usec() -> int | None:
|
||||
# cgroup v2
|
||||
try:
|
||||
with open("/sys/fs/cgroup/cpu.stat", "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.startswith("usage_usec "):
|
||||
return int(line.split()[1])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# cgroup v1
|
||||
ns = _read_int_from_paths(["/sys/fs/cgroup/cpuacct/cpuacct.usage"])
|
||||
if ns is not None:
|
||||
return ns // 1000
|
||||
return None
|
||||
|
||||
|
||||
def _cpu_quota_cores() -> float | None:
|
||||
# cgroup v2
|
||||
try:
|
||||
with open("/sys/fs/cgroup/cpu.max", "r", encoding="utf-8") as f:
|
||||
quota, period = f.read().strip().split()[:2]
|
||||
if quota == "max":
|
||||
return float(os.cpu_count() or 1)
|
||||
q, p = int(quota), int(period)
|
||||
if p > 0:
|
||||
return max(q / p, 0.01)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# cgroup v1
|
||||
quota = _read_int_from_paths(["/sys/fs/cgroup/cpu/cpu.cfs_quota_us"])
|
||||
period = _read_int_from_paths(["/sys/fs/cgroup/cpu/cpu.cfs_period_us"])
|
||||
if quota is not None and period is not None and quota > 0 and period > 0:
|
||||
return max(quota / period, 0.01)
|
||||
|
||||
return float(os.cpu_count() or 1)
|
||||
|
||||
|
||||
def _memory_stats() -> dict[str, Any]:
|
||||
used = _read_int_from_paths(
|
||||
[
|
||||
"/sys/fs/cgroup/memory.current", # cgroup v2
|
||||
"/sys/fs/cgroup/memory/memory.usage_in_bytes", # cgroup v1
|
||||
]
|
||||
)
|
||||
limit = _read_int_from_paths(
|
||||
[
|
||||
"/sys/fs/cgroup/memory.max", # cgroup v2
|
||||
"/sys/fs/cgroup/memory/memory.limit_in_bytes", # cgroup v1
|
||||
]
|
||||
)
|
||||
|
||||
# Certaines limites cgroup valent "max" ou des sentinelles tres grandes.
|
||||
if limit is not None and limit >= 9_000_000_000_000_000_000:
|
||||
limit = None
|
||||
|
||||
pct = None
|
||||
if used is not None and limit and limit > 0:
|
||||
pct = round((used / limit) * 100, 1)
|
||||
|
||||
return {"used_bytes": used, "limit_bytes": limit, "used_pct": pct}
|
||||
|
||||
|
||||
def _disk_stats() -> dict[str, Any]:
|
||||
target = "/data" if os.path.isdir("/data") else "/"
|
||||
total, used, free = shutil.disk_usage(target)
|
||||
uploads_size = None
|
||||
if os.path.isdir(UPLOAD_DIR):
|
||||
try:
|
||||
uploads_size = sum(
|
||||
os.path.getsize(os.path.join(root, name))
|
||||
for root, _, files in os.walk(UPLOAD_DIR)
|
||||
for name in files
|
||||
)
|
||||
except Exception:
|
||||
uploads_size = None
|
||||
return {
|
||||
"path": target,
|
||||
"total_bytes": total,
|
||||
"used_bytes": used,
|
||||
"free_bytes": free,
|
||||
"used_pct": round((used / total) * 100, 1) if total else None,
|
||||
"uploads_bytes": uploads_size,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/settings")
|
||||
def get_settings(session: Session = Depends(get_session)):
|
||||
@@ -23,3 +130,34 @@ def update_settings(data: dict, session: Session = Depends(get_session)):
|
||||
session.add(row)
|
||||
session.commit()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.get("/settings/debug/system")
|
||||
def get_debug_system_stats() -> dict[str, Any]:
|
||||
"""Stats runtime du conteneur (utile pour affichage debug UI)."""
|
||||
global _PREV_CPU_USAGE_USEC, _PREV_CPU_TS
|
||||
|
||||
now = time.monotonic()
|
||||
usage_usec = _read_cgroup_cpu_usage_usec()
|
||||
quota_cores = _cpu_quota_cores()
|
||||
cpu_pct = None
|
||||
|
||||
if usage_usec is not None and _PREV_CPU_USAGE_USEC is not None and _PREV_CPU_TS is not None:
|
||||
delta_usage = usage_usec - _PREV_CPU_USAGE_USEC
|
||||
delta_time_usec = (now - _PREV_CPU_TS) * 1_000_000
|
||||
if delta_time_usec > 0 and quota_cores and quota_cores > 0:
|
||||
cpu_pct = round((delta_usage / (delta_time_usec * quota_cores)) * 100, 1)
|
||||
|
||||
_PREV_CPU_USAGE_USEC = usage_usec
|
||||
_PREV_CPU_TS = now
|
||||
|
||||
return {
|
||||
"source": "container-cgroup",
|
||||
"cpu": {
|
||||
"usage_usec_total": usage_usec,
|
||||
"quota_cores": quota_cores,
|
||||
"used_pct": cpu_pct,
|
||||
},
|
||||
"memory": _memory_stats(),
|
||||
"disk": _disk_stats(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user