feat(docker): readable names, CPU in cores and memory in bytes, failures listed with since-times (#5209)
#5209 "Docker views: readable names, CPU/memory in cores and bytes, failures listed with since-time" (milestone 477 "UI problems first"). - A swarm task reads as its service and slot ("codex_db · 1", or the node for a global service), with the raw name in the title: Docker page, host panel, busiest widget, container detail. - CPU reads in cores (Docker's 100% = one core), toned by its share of the host's CPUs from the agent's per-core series. Memory reads in bytes, against a limit only when one is really set (Docker reports host RAM otherwise). - Exit 137/143 without an OOM kill is a stop, not a failure: no exited_nonzero, no crash-loop death, no "failed (24h)", dim exit code. - "Failed (24h)" is a list (Docker page #failures, linked from the widget and the tile): plain containers by their deaths, swarm services by Swarm's failed tasks; a second manager's copy of a task death counts once. - Failing swarm services lead Needs attention; services sort problems first with since-times on the page, the widget and the Swarm page; nodes that are down or drained sort first. - Busiest widget orders by a 5-minute CPU average so the list holds still. - Host panel: a table, problems first with what is wrong and since when; replaced swarm tasks hidden and counted. - Container detail: node hostname, I/O labelled "since start", Restarts hidden for swarm tasks with a note on where task failures are. - Widget names: "Docker — Services & failures", "Docker — Busiest containers". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -350,3 +350,31 @@ def build_swarm_services(containers, services, node_hostname: dict, host_name: d
|
||||
standalone = sorted(groups.values(), key=lambda g: g["host"].lower())
|
||||
|
||||
return {"services": out_services, "standalone": standalone, "superseded": superseded}
|
||||
|
||||
|
||||
def service_trouble(s: dict) -> int | None:
|
||||
"""How bad a built service is, for sorting and for Needs attention: 0 a
|
||||
crash loop, 1 no replica running, 2 short of replicas, 3 healthy but with
|
||||
failed tasks in the last 24h; None when nothing is wrong."""
|
||||
if s.get("crash_looping"):
|
||||
return 0
|
||||
if s.get("state") == "down":
|
||||
return 1
|
||||
if s.get("state") == "degraded":
|
||||
return 2
|
||||
if s.get("failures_24h"):
|
||||
return 3
|
||||
return None
|
||||
|
||||
|
||||
def services_problems_first(services: list[dict]) -> list[dict]:
|
||||
"""Services in trouble first, worst first, then the rest by name."""
|
||||
return sorted(services, key=lambda s: (
|
||||
4 if service_trouble(s) is None else service_trouble(s), s["name"].lower()))
|
||||
|
||||
|
||||
def failing_services(services: list[dict]) -> list[dict]:
|
||||
"""The services that belong in Needs attention: a crash loop, or short of
|
||||
their desired replicas. A healthy service whose failed tasks Swarm has
|
||||
already replaced is history, shown on the service, not a problem now."""
|
||||
return [s for s in services_problems_first(services) if (service_trouble(s) or 9) <= 2]
|
||||
|
||||
Reference in New Issue
Block a user