feat(dashboard): Hosts widget worst first, load against cores, one home per fact (#5251)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m19s
CI / unit (push) Failing after 1m31s
CI / publish (push) Skipped

The Hosts — Overview widget opens with a verdict ("VDNT-Util01: CPU high."
or "All 3 hosts healthy."), then one row per host under one header: CPU,
memory, disk / and load. Rows sort by host_glance: a down monitor, a
reading past critical, a quiet agent, a warning, then the healthy. Load
reads "16.8 / 8 cores", graded per core as Settings → Thresholds intends
(cores counted from the per-core CPU sub-resources, as on the host page).
A quiet agent's last figures are shown uncoloured, with when it last reported.

Ping and 24h uptime are gone from this widget: they belong to Monitors. So
are the unlabelled CPU sparkline and the 30-day uptime query it no longer
needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 13:15:01 -04:00
co-authored by Claude Opus 5.5
parent b5fcccba5f
commit 17abe450cc
5 changed files with 170 additions and 92 deletions
+5 -11
View File
@@ -27,21 +27,15 @@ def _ansible_source_names() -> list[str]:
@hosts_bp.get("/overview/widget")
@require_role(UserRole.viewer)
async def overview_widget():
"""Dashboard widget: unified per-host monitor + agent glance, linking to the hub."""
"""Dashboard widget: each host's resources, worst first (#5251). Uptime and
latency belong to the Monitors widget, so this one doesn't read them."""
async with current_app.db_sessionmaker() as db:
hosts = (await db.execute(select(Host).order_by(Host.name))).scalars().all()
host_status = await host_svc.host_status(db, [h.id for h in hosts])
uptime = await host_svc.compute_uptime(db)
host_names = [h.name for h in hosts]
agent = await host_svc.agent_overview_by_host(
db, host_names, host_svc.agent_stale_after_seconds(current_app.config))
cpu_sparks = await host_svc.agent_cpu_sparks_by_host(db, host_names)
return await render_template(
"hosts/overview_widget.html",
hosts=hosts, host_status=host_status,
uptime=uptime, agent=agent, cpu_sparks=cpu_sparks,
)
db, [h.name for h in hosts], host_svc.agent_stale_after_seconds(current_app.config))
rows = host_svc.host_glance(hosts, host_status, agent, current_app.config.get("THRESHOLDS", {}))
return await render_template("hosts/overview_widget.html", rows=rows)
@hosts_bp.get("/")