diff --git a/plugins/host_agent/routes.py b/plugins/host_agent/routes.py index 813b62a..17037e0 100644 --- a/plugins/host_agent/routes.py +++ b/plugins/host_agent/routes.py @@ -864,6 +864,10 @@ def _split_host_metrics(host_name: str, latest: dict) -> tuple: else: mounts[suffix] = metrics cores.sort(key=lambda c: c[0]) + # Fullest first, by the worse of space and inodes (#5810): a full + # /var/lib/docker must not sort after /boot. + mounts = dict(sorted(mounts.items(), key=lambda kv: ( + -max(kv[1].get("disk_used_pct") or 0, kv[1].get("inodes_used_pct") or 0), kv[0]))) return hostlvl, cores, nets, disks_io, temps, mounts diff --git a/steward/core/status.py b/steward/core/status.py index bf12894..186c1f8 100644 --- a/steward/core/status.py +++ b/steward/core/status.py @@ -1,11 +1,11 @@ # steward/core/status.py """Unified monitor-status aggregation. -A single readable "is everything up?" surface (the Status page + dashboard +A single readable "is everything up?" surface (the dashboard Overview widget) is assembled from heterogeneous monitors. The core unified Monitor entity (ping/dns/http) contributes via `monitor_status_source`; plugins may register their own sources from setup(). Each source is an async callable(db) -returning normalised StatusEntry objects so the Status page never imports +returning normalised StatusEntry objects so the Overview never imports plugin tables directly. """ from __future__ import annotations @@ -62,7 +62,7 @@ async def collect_status(db) -> list[StatusEntry]: """Gather entries from every registered source, down-first then by name. A failing source is logged and skipped so one broken plugin can't blank - the whole Status page. + the whole Overview. """ entries: list[StatusEntry] = [] for src in list(_SOURCES): @@ -210,7 +210,7 @@ def _heartbeat_title(mtype: str, r) -> str: async def monitor_status_source(db) -> list[StatusEntry]: - """Contribute every enabled Monitor (all types) to the Status page.""" + """Contribute every enabled Monitor (all types) to the Overview.""" from steward.models.monitors import Monitor monitors = list((await db.execute( diff --git a/steward/core/widgets.py b/steward/core/widgets.py index 1be750b..ed1b710 100644 --- a/steward/core/widgets.py +++ b/steward/core/widgets.py @@ -65,7 +65,7 @@ WIDGET_REGISTRY: dict[str, dict] = { "status_overview": { "key": "status_overview", "label": "Overview", - "description": "What needs attention across Steward: hosts, monitors and firing alerts, problems listed with since-times", + "description": "What needs attention across Steward: firing alerts, monitors down and metric anomalies, each with since when", "hx_url": "/status/widget", "detail_url": "/monitors/", "plugin": None, diff --git a/steward/templates/monitors/widget.html b/steward/templates/monitors/widget.html index 35c56ff..d96064d 100644 --- a/steward/templates/monitors/widget.html +++ b/steward/templates/monitors/widget.html @@ -27,7 +27,7 @@ {% if session.user_id %}{{ m.name }}{% else %}{{ m.name }}{% endif %} {%- if d.state == 'down' %}went down {{ d.down_since|ago if d.down_since else '' }}{% if d.down_reason %} · {{ d.down_reason }}{% endif %} {%- elif d.state == 'degraded' %}{% if (d.latency or status_class(latest.response_ms, 'latency') if latest else none) == 'crit' %}slow replies{% else %}checks failing now and then{% endif %} - {%- elif d.state == 'pending' %}not checked yet{% endif %} + {%- elif d.state == 'pending' %}not checked yet{% if m.created_at %} · added {{ m.created_at|ago }}{% endif %}{% endif %} {% set lat = (d.latency or status_class(latest.response_ms, 'latency')) if latest and latest.is_up and latest.response_ms is not none else none %}