feat(api): add cached container cpu/mem fields on containers-dashboard
This commit is contained in:
@@ -70,6 +70,7 @@ async def _stats_poller_loop():
|
||||
key = _norm_container_name(st.get("Name"))
|
||||
if not key:
|
||||
continue
|
||||
# CPUPerc returned by Podman is already percentage (0.10 == 0.10%)
|
||||
cpu_val = st.get("CPUPerc")
|
||||
if cpu_val is None:
|
||||
cpu_val = st.get("CPU")
|
||||
|
||||
+49
-45
@@ -501,9 +501,9 @@
|
||||
if (tab === "images") {
|
||||
loadImages();
|
||||
}
|
||||
// Start/stop live stats alleen in Containers tab
|
||||
if (tab === 'containers') startContainersStatsStream();
|
||||
else stopContainersStatsStream();
|
||||
// Start/stop live stats alleen in Containers tab (polling via /containers-dashboard)
|
||||
if (tab === 'containers') startContainersDashboardStatsPoll();
|
||||
else stopContainersDashboardStatsPoll();
|
||||
|
||||
refreshActive();
|
||||
}
|
||||
@@ -526,9 +526,9 @@
|
||||
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState !== "visible") {
|
||||
stopContainersStatsStream();
|
||||
stopContainersDashboardStatsPoll();
|
||||
} else if (currentTab === "containers") {
|
||||
startContainersStatsStream();
|
||||
startContainersDashboardStatsPoll();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -814,58 +814,69 @@
|
||||
renderContainersGrouped(list, tbody, podStatus);
|
||||
}
|
||||
|
||||
let containersStatsES = null;
|
||||
let containersDashboardStatsTimer = null;
|
||||
let containersDashboardStatsInFlight = false;
|
||||
|
||||
function startContainersDashboardStatsPoll() {
|
||||
if (containersDashboardStatsTimer) return;
|
||||
// immediate tick, daarna elke 1s
|
||||
pollContainersDashboardStatsOnce();
|
||||
containersDashboardStatsTimer = setInterval(pollContainersDashboardStatsOnce, 1000);
|
||||
}
|
||||
|
||||
function startContainersStatsStream() {
|
||||
if (containersStatsES) return;
|
||||
function stopContainersDashboardStatsPoll() {
|
||||
if (!containersDashboardStatsTimer) return;
|
||||
clearInterval(containersDashboardStatsTimer);
|
||||
containersDashboardStatsTimer = null;
|
||||
resetPodTotals();
|
||||
}
|
||||
|
||||
containersStatsES = new EventSource("/api/containers/stats/stream?interval=1");
|
||||
async function pollContainersDashboardStatsOnce() {
|
||||
if (containersDashboardStatsInFlight) return;
|
||||
containersDashboardStatsInFlight = true;
|
||||
try {
|
||||
const containers = await api('/containers-dashboard', 'GET');
|
||||
const list = Array.isArray(containers) ? containers : (containers?.containers || []);
|
||||
|
||||
containersStatsES.addEventListener("stats", (ev) => {
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(ev.data);
|
||||
} catch (e) {
|
||||
// Slechte SSE chunk -> negeren zodat de stream niet "stilvalt"
|
||||
return;
|
||||
}
|
||||
const statsList = payload?.data?.Stats || [];
|
||||
// totals per pod voor deze SSE tick
|
||||
// totals per pod voor deze poll tick
|
||||
const podCpu = new Map(); // podName -> cpuPct sum
|
||||
const podMem = new Map(); // podName -> memBytes sum
|
||||
const podMemPct = new Map(); // podName -> memPct sum
|
||||
|
||||
for (const st of statsList) {
|
||||
const cname = normalizeContainerName(st?.Name);
|
||||
for (const c of (list || [])) {
|
||||
const cname = normalizeContainerName((c?.Names && c.Names[0]) ? c.Names[0] : (c?.Names || c?.Name || c?.name || ''));
|
||||
if (!cname) continue;
|
||||
|
||||
const key = cssSafeId(cname);
|
||||
|
||||
const cpuPct = Number(st?.CPUPerc ?? st?.CPU ?? st?.AvgCPU ?? 0);
|
||||
const memBytes = Number(st?.MemUsage ?? 0);
|
||||
const memPct = Number(st?.MemPerc ?? 0);
|
||||
const cpuRaw = c?._dashboard_cpu;
|
||||
const memBytesRaw = c?._dashboard_mem_usage;
|
||||
const memPctRaw = c?._dashboard_mem_perc;
|
||||
|
||||
const cpuPct = Number(cpuRaw);
|
||||
const memBytes = Number(memBytesRaw);
|
||||
const memPct = Number(memPctRaw);
|
||||
|
||||
const pod = containersC2P.get(cname);
|
||||
if (pod) {
|
||||
podCpu.set(pod, (podCpu.get(pod) || 0) + cpuPct);
|
||||
podMem.set(pod, (podMem.get(pod) || 0) + memBytes);
|
||||
podMemPct.set(pod, (podMemPct.get(pod) || 0) + memPct);
|
||||
if (Number.isFinite(cpuPct)) podCpu.set(pod, (podCpu.get(pod) || 0) + cpuPct);
|
||||
if (Number.isFinite(memBytes)) podMem.set(pod, (podMem.get(pod) || 0) + memBytes);
|
||||
if (Number.isFinite(memPct)) podMemPct.set(pod, (podMemPct.get(pod) || 0) + memPct);
|
||||
}
|
||||
|
||||
// CPU: jouw API geeft CPU als fractie (0.00384 -> 0.384%)
|
||||
const cpuEl = document.getElementById(`cpu-${key}`);
|
||||
if (cpuEl) cpuEl.textContent = cpuPct.toFixed(2) + "%";
|
||||
if (cpuEl) cpuEl.textContent = Number.isFinite(cpuPct) ? (cpuPct.toFixed(2) + "%") : "-";
|
||||
|
||||
// MEM: bytes + percentage
|
||||
const memEl = document.getElementById(`mem-${key}`);
|
||||
if (memEl) {
|
||||
const mem = formatBytes(memBytes);
|
||||
memEl.textContent = `${mem} (${memPct.toFixed(1)}%)`;
|
||||
if (Number.isFinite(memBytes) && Number.isFinite(memPct)) {
|
||||
memEl.textContent = `${formatBytes(memBytes)} (${memPct.toFixed(1)}%)`;
|
||||
} else {
|
||||
memEl.textContent = "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
// NA de container-loop: pod totals renderen
|
||||
|
||||
for (const [pod, cpuSum] of podCpu.entries()) {
|
||||
const el = document.getElementById(`podcpu-${cssSafeId(pod)}`);
|
||||
if (el) {
|
||||
@@ -878,22 +889,15 @@
|
||||
const el = document.getElementById(`podmem-${cssSafeId(pod)}`);
|
||||
if (el) {
|
||||
const mp = podMemPct.get(pod) || 0;
|
||||
el.textContent = `${formatBytes(memSum)} (${mp.toFixed(1)}%)`;
|
||||
el.textContent = `${formatBytes(memSum)} (${Number(mp).toFixed(1)}%)`;
|
||||
el.classList.remove('stale');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
containersStatsES.onerror = () => {
|
||||
} catch (e) {
|
||||
resetPodTotals();
|
||||
};
|
||||
}
|
||||
|
||||
function stopContainersStatsStream() {
|
||||
if (!containersStatsES) return;
|
||||
containersStatsES.close();
|
||||
containersStatsES = null;
|
||||
resetPodTotals();
|
||||
} finally {
|
||||
containersDashboardStatsInFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetPodTotals() {
|
||||
|
||||
Reference in New Issue
Block a user