From 7c23360bc7b813f3ff8b18ddb711f5b7430dd985 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 14 Oct 2026 16:20:08 -0400 Subject: [PATCH] fix(norms): templates grade by the cutoffs when handed no grade; the parts test reads inside transactions (#5742) CI 9128: the host vitals and monitor templates required the new grades, so callers (and tests) that render them without one failed; they now fall back to status_class. The new integration test read outside begin(), which autobegan the session. Co-Authored-By: Claude Opus 5.5 --- plugins/host_agent/templates/_host_vitals.html | 9 +++++---- steward/templates/monitors/rows.html | 2 +- steward/templates/monitors/widget.html | 5 +++-- tests/integration/test_anomalies.py | 12 ++++++++---- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/plugins/host_agent/templates/_host_vitals.html b/plugins/host_agent/templates/_host_vitals.html index f34b29f..c5f3528 100644 --- a/plugins/host_agent/templates/_host_vitals.html +++ b/plugins/host_agent/templates/_host_vitals.html @@ -5,6 +5,7 @@ reports (the Agent panel below then shows provisioning). #} {% from "_macros.html" import status_badge, stat_tile %} {% macro pct(v) %}{{ '%.0f%%'|format(v) if v is not none else '—' }}{% endmacro %} +{% set grades = grades or {} %} {% if reporting %}
@@ -22,10 +23,10 @@
- {{ stat_tile("CPU", pct(cpu), state=grades.cpu[0], hint=sparks.cpu|safe) }} - {{ stat_tile("Memory", pct(mem), state=grades.mem[0], hint=sparks.mem|safe) }} - {{ stat_tile("Disk /", pct(disk_root), state=grades.disk[0], hint=sparks.disk|safe) }} - {{ stat_tile("Load per core", pct(load_per_core), state=grades.load[0], hint=sparks.load|safe) }} + {{ stat_tile("CPU", pct(cpu), state=(grades.cpu or [status_class(cpu, 'cpu')])[0], hint=sparks.cpu|safe) }} + {{ stat_tile("Memory", pct(mem), state=(grades.mem or [status_class(mem, 'mem')])[0], hint=sparks.mem|safe) }} + {{ stat_tile("Disk /", pct(disk_root), state=(grades.disk or [status_class(disk_root, 'disk')])[0], hint=sparks.disk|safe) }} + {{ stat_tile("Load per core", pct(load_per_core), state=(grades.load or [status_class(load_per_core, 'load')])[0], hint=sparks.load|safe) }}
{% if usual and not stale %}

{{ usual | join("; ") | capitalize }}.

diff --git a/steward/templates/monitors/rows.html b/steward/templates/monitors/rows.html index d8074c5..d223d3b 100644 --- a/steward/templates/monitors/rows.html +++ b/steward/templates/monitors/rows.html @@ -51,7 +51,7 @@ {% if d.state in ('down', 'paused', 'pending') %}{{ status_badge(d.state) }} {% elif latest.response_ms is not none %} {% if d.spark %}{{ d.spark|safe }}{% endif %} - {{ "%.0f"|format(latest.response_ms) }} ms + {{ "%.0f"|format(latest.response_ms) }} ms {% elif m.type == 'dns' and latest.resolved_ip %}{{ latest.resolved_ip }} {% else %}{{ status_badge('up') }}{% endif %} diff --git a/steward/templates/monitors/widget.html b/steward/templates/monitors/widget.html index d078280..35c56ff 100644 --- a/steward/templates/monitors/widget.html +++ b/steward/templates/monitors/widget.html @@ -26,10 +26,11 @@ {{- status_dot(d.state) -}} {% 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 == 'crit' %}slow replies{% else %}checks failing now and then{% 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 %} - + {% 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 %} + {%- if latest and latest.is_up and latest.response_ms is not none %}{{ "%.0f"|format(latest.response_ms) }} ms{% else %}—{% endif -%} {% for k in ("24h", "7d", "30d") %}{% set u = d.uptime.get(k) %} diff --git a/tests/integration/test_anomalies.py b/tests/integration/test_anomalies.py index 9691a71..09d9c34 100644 --- a/tests/integration/test_anomalies.py +++ b/tests/integration/test_anomalies.py @@ -360,15 +360,19 @@ def test_a_metric_learned_without_its_parts_ignores_them(app, src): s.add_all(rows) async with s.begin(): await rebuild_norms(s, NOW) - learned = await norms_at(s, [(src, host, METRIC), (src, part, METRIC)], NOW.replace(hour=14)) + async with s.begin(): + learned = await norms_at(s, [(src, host, METRIC), (src, part, METRIC)], + NOW.replace(hour=14)) async with s.begin(): await _report(s, src, host, at, 90.0) await _report(s, src, part, at, 90.0) - levels = await levels_now(s, at) + async with s.begin(): + levels = await levels_now(s, at) async with s.begin(): await detect(s, at) - opened = list((await s.execute(select(MetricAnomaly.resource_name).where( - MetricAnomaly.source_module == src))).scalars()) + async with s.begin(): + opened = list((await s.execute(select(MetricAnomaly.resource_name).where( + MetricAnomaly.source_module == src))).scalars()) return learned, levels, opened learned, levels, opened = asyncio.run(_go())