feat(host_agent): at-a-glance disk = root (/) not "worst"; metric tooltips
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 8s
CI / integration (push) Successful in 2m19s
CI / publish (push) Successful in 6s

The "Disk (worst)" number was opaque at a glance. Show the root filesystem (/)
usage instead — what people actually care about — on the host panel and the
dashboard widget. "Worst" is kept only for the widget's health dot (so a full
/var or /data still warns) and on the full-metrics page (per-mount + worst
trend). Added hover tooltips defining CPU / Memory / Disk / Load.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 11:05:43 -04:00
parent f80f6c87e8
commit 36212dc58b
3 changed files with 21 additions and 10 deletions
+10 -2
View File
@@ -341,7 +341,10 @@ async def _fleet_rows(session) -> list[dict]:
)).scalars().all() )).scalars().all()
latest: dict[str, dict[str, float]] = {} latest: dict[str, dict[str, float]] = {}
root_disk: dict[str, float] = {} # host_name → root (/) disk %
for row in latest_rows: for row in latest_rows:
if row.resource_name.endswith(":/") and row.metric_name == "disk_used_pct":
root_disk[row.resource_name[:-2]] = row.value
if ":" in row.resource_name: if ":" in row.resource_name:
continue continue
latest.setdefault(row.resource_name, {})[row.metric_name] = row.value latest.setdefault(row.resource_name, {})[row.metric_name] = row.value
@@ -362,7 +365,8 @@ async def _fleet_rows(session) -> list[dict]:
"stale": stale, "stale": stale,
"cpu_pct": m.get("cpu_pct"), "cpu_pct": m.get("cpu_pct"),
"mem_used_pct": m.get("mem_used_pct"), "mem_used_pct": m.get("mem_used_pct"),
"disk_worst": m.get("disk_used_pct_worst"), "disk_worst": m.get("disk_used_pct_worst"), # health dot only
"disk_root": root_disk.get(host.name), # displayed at-a-glance
"load_1m": m.get("load_1m"), "load_1m": m.get("load_1m"),
"temp_max": m.get("temp_c_max"), "temp_max": m.get("temp_c_max"),
"net_rx_bps": m.get("net_rx_bps"), "net_rx_bps": m.get("net_rx_bps"),
@@ -547,6 +551,9 @@ async def host_panel(host_id: str):
AnsibleTarget.host_id == host_id))).scalar_one_or_none() AnsibleTarget.host_id == host_id))).scalar_one_or_none()
hostlvl = latest.get(host.name, {}) hostlvl = latest.get(host.name, {})
# At-a-glance disk = the root filesystem (what people actually care about),
# not the "worst" mount which is opaque. Worst stays on the full-metrics page.
disk_root = (latest.get(host.name + ":/", {}) or {}).get("disk_used_pct")
ls = reg.last_seen_at if reg else None ls = reg.last_seen_at if reg else None
# "Deployed" is gated on the agent actually checking in (last_seen_at), NOT on # "Deployed" is gated on the agent actually checking in (last_seen_at), NOT on
# the registration row — that row is minted before the playbook runs, so a # the registration row — that row is minted before the playbook runs, so a
@@ -557,7 +564,8 @@ async def host_panel(host_id: str):
ansible_cfg = current_app.config.get("ANSIBLE", {}) ansible_cfg = current_app.config.get("ANSIBLE", {})
return await render_template( return await render_template(
"panel.html", "panel.html",
host=host, reg=reg, hostlvl=hostlvl, reporting=reporting, stale=stale, target=target, host=host, reg=reg, hostlvl=hostlvl, disk_root=disk_root,
reporting=reporting, stale=stale, target=target,
ansible_available=has_capability("ansible.run_playbook"), ansible_available=has_capability("ansible.run_playbook"),
managed_key_set=bool((ansible_cfg.get("ssh_public_key") or "").strip()), managed_key_set=bool((ansible_cfg.get("ssh_public_key") or "").strip()),
) )
+9 -6
View File
@@ -18,15 +18,18 @@
<div style="display:flex;gap:2rem;flex-wrap:wrap;margin-bottom:0.9rem;"> <div style="display:flex;gap:2rem;flex-wrap:wrap;margin-bottom:0.9rem;">
{% set cpu = hostlvl.get('cpu_pct') %} {% set cpu = hostlvl.get('cpu_pct') %}
{% set mem = hostlvl.get('mem_used_pct') %} {% set mem = hostlvl.get('mem_used_pct') %}
{% set disk = hostlvl.get('disk_used_pct_worst') %}
{% set load1 = hostlvl.get('load_1m') %} {% set load1 = hostlvl.get('load_1m') %}
<div><div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">CPU</div> <div title="Average CPU utilization across all cores">
<div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">CPU</div>
<div style="font-weight:600;">{{ '%.0f%%'|format(cpu) if cpu is not none else '—' }}</div></div> <div style="font-weight:600;">{{ '%.0f%%'|format(cpu) if cpu is not none else '—' }}</div></div>
<div><div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">Memory</div> <div title="RAM in use (total minus available; cache/buffers count as free)">
<div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">Memory</div>
<div style="font-weight:600;">{{ '%.0f%%'|format(mem) if mem is not none else '—' }}</div></div> <div style="font-weight:600;">{{ '%.0f%%'|format(mem) if mem is not none else '—' }}</div></div>
<div><div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">Disk (worst)</div> <div title="Root filesystem (/) usage — see Full metrics for every mount">
<div style="font-weight:600;">{{ '%.0f%%'|format(disk) if disk is not none else '—' }}</div></div> <div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">Disk /</div>
<div><div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">Load 1m</div> <div style="font-weight:600;">{{ '%.0f%%'|format(disk_root) if disk_root is not none else '—' }}</div></div>
<div title="System load average over 1 minute (runnable + waiting processes). Compare to CPU core count.">
<div style="font-size:0.72rem;color:var(--text-muted);text-transform:uppercase;">Load 1m</div>
<div style="font-weight:600;">{{ '%.2f'|format(load1) if load1 is not none else '—' }}</div></div> <div style="font-weight:600;">{{ '%.2f'|format(load1) if load1 is not none else '—' }}</div></div>
</div> </div>
@@ -16,8 +16,8 @@
{% if r.mem_used_pct is not none %} {% if r.mem_used_pct is not none %}
<span title="Memory"><span style="color:var(--text-dim);">mem</span> {{ "%.0f"|format(r.mem_used_pct) }}%</span> <span title="Memory"><span style="color:var(--text-dim);">mem</span> {{ "%.0f"|format(r.mem_used_pct) }}%</span>
{% endif %} {% endif %}
{% if r.disk_worst is not none %} {% if r.disk_root is not none %}
<span title="Worst disk mount"><span style="color:var(--text-dim);">disk</span> {{ "%.0f"|format(r.disk_worst) }}%</span> <span title="Root filesystem (/) usage"><span style="color:var(--text-dim);">disk /</span> {{ "%.0f"|format(r.disk_root) }}%</span>
{% endif %} {% endif %}
{% if r.load_1m is not none %} {% if r.load_1m is not none %}
<span title="Load average 1m"><span style="color:var(--text-dim);">load</span> {{ "%.2f"|format(r.load_1m) }}</span> <span title="Load average 1m"><span style="color:var(--text-dim);">load</span> {{ "%.2f"|format(r.load_1m) }}</span>