feat(dashboard): add cached cpu/mem stats fields to containers-dashboard
This commit is contained in:
@@ -17,6 +17,85 @@ PODMAN_API_BASE = "http+unix://%2Frun%2Fuser%2F1000%2Fpodman%2Fpodman.sock/v5.4.
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ALLOWLIST_FILE = os.getenv("ALLOWLIST_FILE", os.path.join(BASE_DIR, "allowed_units.txt"))
|
||||
WORKLOADS_DIR = "/app/workloads"
|
||||
|
||||
# --- STATS CACHE (contract-neutral; in-memory) ---
|
||||
# Poll Podman stats centrally and expose as optional dashboard fields.
|
||||
_STATS_CACHE_BY_NAME = {} # name -> {"cpu": float|None, "mem_usage": float|None, "mem_perc": float|None}
|
||||
_STATS_CACHE_TS = None
|
||||
_STATS_POLLER_TASK = None
|
||||
|
||||
def _norm_container_name(name) -> str:
|
||||
try:
|
||||
return str(name or "").lstrip("/")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _parse_stats_interval_seconds() -> float:
|
||||
raw = os.getenv("STATS_INTERVAL_SECONDS", "1.0")
|
||||
try:
|
||||
v = float(raw)
|
||||
except Exception:
|
||||
v = 1.0
|
||||
if v <= 0:
|
||||
v = 1.0
|
||||
if v < 0.5:
|
||||
v = 0.5
|
||||
if v > 30:
|
||||
v = 30
|
||||
return v
|
||||
|
||||
async def _stats_poller_loop():
|
||||
global _STATS_CACHE_BY_NAME, _STATS_CACHE_TS
|
||||
|
||||
interval = _parse_stats_interval_seconds()
|
||||
stats_url = f"{PODMAN_API_BASE}/libpod/containers/stats?all=true&stream=false"
|
||||
|
||||
def _to_float(x):
|
||||
try:
|
||||
return float(x)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
while True:
|
||||
try:
|
||||
data = SESSION.get(stats_url, timeout=5).json()
|
||||
stats_list = data.get("Stats") if isinstance(data, dict) else None
|
||||
if not isinstance(stats_list, list):
|
||||
stats_list = []
|
||||
|
||||
new_cache = {}
|
||||
for st in stats_list:
|
||||
if not isinstance(st, dict):
|
||||
continue
|
||||
key = _norm_container_name(st.get("Name"))
|
||||
if not key:
|
||||
continue
|
||||
cpu_val = st.get("CPUPerc")
|
||||
if cpu_val is None:
|
||||
cpu_val = st.get("CPU")
|
||||
if cpu_val is None:
|
||||
cpu_val = st.get("AvgCPU")
|
||||
new_cache[key] = {
|
||||
"cpu": _to_float(cpu_val),
|
||||
"mem_usage": _to_float(st.get("MemUsage")),
|
||||
"mem_perc": _to_float(st.get("MemPerc")),
|
||||
}
|
||||
|
||||
_STATS_CACHE_BY_NAME = new_cache
|
||||
_STATS_CACHE_TS = int(__import__("time").time())
|
||||
except Exception:
|
||||
# Keep last good cache; try again next tick.
|
||||
pass
|
||||
|
||||
await asyncio.sleep(interval)
|
||||
|
||||
@app.on_event("startup")
|
||||
async def _startup_stats_poller():
|
||||
global _STATS_POLLER_TASK
|
||||
if _STATS_POLLER_TASK and not _STATS_POLLER_TASK.done():
|
||||
return
|
||||
_STATS_POLLER_TASK = asyncio.create_task(_stats_poller_loop())
|
||||
|
||||
# --- ROUTERS ---
|
||||
# Images API lives in a dedicated module to keep this file from growing further.
|
||||
app.include_router(init_images_router(SESSION, PODMAN_API_BASE))
|
||||
@@ -384,6 +463,9 @@ def _make_defined_container_dashboard_row(name: str, relpath: str):
|
||||
"_dashboard_source": "systemd",
|
||||
"_dashboard_unit": f"{name}.service",
|
||||
"_dashboard_def_path": relpath,
|
||||
"_dashboard_cpu": None,
|
||||
"_dashboard_mem_usage": None,
|
||||
"_dashboard_mem_perc": None,
|
||||
}
|
||||
|
||||
|
||||
@@ -507,6 +589,8 @@ def containers_dashboard():
|
||||
# Cache zodat we niet voor elke container opnieuw systemctl doen
|
||||
unit_active_cache = {}
|
||||
|
||||
stats_by_name = _STATS_CACHE_BY_NAME
|
||||
|
||||
def _unit_is_active(unit):
|
||||
if not unit:
|
||||
return False
|
||||
@@ -528,6 +612,16 @@ def containers_dashboard():
|
||||
# Normaliseer naam: Podman kan "/name" geven
|
||||
rname = ((c.get("Names") or ["?"])[0] or "").lstrip("/")
|
||||
|
||||
# Optional live stats (always present; null on miss)
|
||||
c["_dashboard_cpu"] = None
|
||||
c["_dashboard_mem_usage"] = None
|
||||
c["_dashboard_mem_perc"] = None
|
||||
st = stats_by_name.get(rname)
|
||||
if isinstance(st, dict):
|
||||
c["_dashboard_cpu"] = st.get("cpu")
|
||||
c["_dashboard_mem_usage"] = st.get("mem_usage")
|
||||
c["_dashboard_mem_perc"] = st.get("mem_perc")
|
||||
|
||||
# 1) Managed: systemd als er een .container definitie bestaat
|
||||
if rname in defined:
|
||||
c["_dashboard_source"] = "systemd"
|
||||
|
||||
Reference in New Issue
Block a user