feat(hosts): one health header, graded by one set of thresholds (#5122)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 47s
CI / unit (push) Failing after 1m14s
CI / publish (push) Skipped

The host page's vitals strip becomes its health header: Live or Stale,
then one sentence — "Memory 93% is past its critical threshold", "Every
reading is within its thresholds", or, for a stale host, "No report since
20m ago; the readings below are from then" — and last seen as |ago. CPU,
memory, root disk and load per core follow as stat tiles graded by
Settings → Thresholds, with their sparklines.

The full-metrics page drew its bars and gauges at a hard-coded 70/90
(temperatures 70/85) while every other page used the configured cutoffs
(80/90 by default), so a 75 % CPU was amber here and fine everywhere else.
bar() now takes the metric kind and colours its .meter-fill through
status_class; gauges, temperatures and per-core bars do the same.

The host's monitor rows move onto the Monitors list's shapes: a status dot,
the name linking to the monitor's page, Ping/TCP/DNS/HTTP, pill-up/
pill-down, latency and uptime through status_class, labelled icon buttons.
HEX_CEILING drops to 54.

New formatting.worst_reading(readings, thresholds) picks the reading
furthest past its cutoff, the first of equals winning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 17:58:22 -04:00
co-authored by Claude Opus 5.5
parent 629c466fd9
commit 8c19f3c21f
8 changed files with 177 additions and 73 deletions
+18
View File
@@ -131,3 +131,21 @@ def required(value: Any, what: str) -> Any:
if value is None or not str(value).strip():
raise ValueError(f"{what} is required")
return value
_SEVERITY = {"crit": 2, "warn": 1}
def worst_reading(readings, thresholds: dict):
"""The reading furthest past its cutoff, as (label, value, grade), or None
when every reading is within its cutoffs or missing.
`readings` is (label, value, kind) triples, graded like status_class; the
first of equally bad readings wins, so list them in the order a reader
should hear about them."""
worst, rank = None, 0
for label, value, kind in readings:
grade = status_class(value, kind, thresholds)
if _SEVERITY.get(grade, 0) > rank:
worst, rank = (label, value, grade), _SEVERITY[grade]
return worst