Traefik: routers are graded from their latest scrape (router_state: crit on any real 5xx share, warn on p95 past the latency critical cutoff or a high 4xx share). Failing and slow routers lead as cards that say why; healthy ones fold into one table that stays open or shut across polls. Proxy counts are stat tiles; certificates are graded by Settings -> Thresholds (tls_days). UniFi: WAN health is a stat strip (latency graded by the thresholds), devices carry status dots, event categories and firewall actions are badges, the alarm count reuses .count-badge (renamed from .nav-count). SNMP: each device wears a status badge and its readings are stat tiles. Agent fleet: the table comes first, with a Status column (Reporting / Silent / Never reported, by the host page's stale_after_seconds), silent agents first, hosts linked, last seen as |ago. Audit: entries name their entity (one query per type, steward/audit/ service.py) and link it; something deleted since keeps the name its entry recorded and says "(deleted)". Tokens: charts read colours through chart-colors.js (stewardColor resolves a token to the rgb the browser computed; Chart.js defaults take the page's text and border colours), so host, history, Traefik and SNMP charts drop their hex. Template hex colours reach 0 (HEX_CEILING = 0), the last rgba pills go, and heavy weights (600/700/bold, 60 of them) become 500, the house pair being 400/500; a guard keeps templates there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
40 lines
2.0 KiB
Python
40 lines
2.0 KiB
Python
"""The agent fleet page opens on the fleet itself: a status column, silent
|
|
agents first (#5126)."""
|
|
from datetime import datetime, timedelta, timezone
|
|
from types import SimpleNamespace as NS
|
|
|
|
from plugins.host_agent.routes import fleet_rows, fleet_state
|
|
from tests._templates import render_plugin_template
|
|
|
|
|
|
def _reg(host_id, seen_ago_s):
|
|
now = datetime.now(timezone.utc)
|
|
return NS(host_id=host_id, last_seen_at=None if seen_ago_s is None else now - timedelta(seconds=seen_ago_s),
|
|
host_ip="10.0.0.5", agent_version="1.4.0", distro="debian")
|
|
|
|
|
|
def test_fleet_state():
|
|
now = datetime.now(timezone.utc)
|
|
assert fleet_state(_reg("a", 30), now, 180) == "up"
|
|
assert fleet_state(_reg("a", 600), now, 180) == "stale"
|
|
assert fleet_state(_reg("a", None), now, 180) == "pending"
|
|
|
|
|
|
async def test_silent_agents_come_first_and_the_table_leads_the_page(app):
|
|
now = datetime.now(timezone.utc)
|
|
hosts = {i: NS(id=i, name=n) for i, n in [("h1", "web"), ("h2", "nas"), ("h3", "attic")]}
|
|
rows = fleet_rows([_reg("h1", 30), _reg("h2", 900), _reg("h3", None)], hosts, now, 180)
|
|
assert [r["name"] for r in rows] == ["nas", "attic", "web"]
|
|
|
|
async with app.test_request_context("/plugins/host_agent/fleet/"):
|
|
page = await render_plugin_template(app, "host_agent", "settings_list.html", registrations=rows,
|
|
new_token=None, install_url=None, ansible_available=False,
|
|
deploy_targets=[], deploy_groups=[], managed_key_set=False)
|
|
|
|
assert page.index("<th>Status</th>") < page.index('action="/plugins/host_agent/fleet/add-host"')
|
|
assert '<span class="badge badge-warn">Silent</span>' in page
|
|
assert '<span class="badge badge-warn">Never reported</span>' in page
|
|
assert '<span class="badge badge-ok">Reporting</span>' in page
|
|
assert page.index('href="/hosts/h2">nas</a>') < page.index('href="/hosts/h1">web</a>')
|
|
assert 'data-confirm="Delete the agent registration for nas?' in page
|