feat(settings): configurable monitoring thresholds
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 8s
CI / integration (push) Successful in 2m19s
CI / publish (push) Successful in 1m3s

Move the hardcoded warn/crit cutoffs into Settings -> Thresholds (DB-backed,
live, no restart). New thresholds.* keys + to_thresholds_cfg() + a
threshold_style(value, kind) jinja global that reads them; latency reuses the
existing ping good/warn keys, uptime is direction-aware (floors).

Replace the _macros metric_style/uptime_style macros (now removed) with the
global across Hosts-Overview, host_agent fleet + panel, Uptime/SLA widget, and
the ping page uptime column — all now honor the configured cutoffs. Uptime keeps
its green 'good' look when not degraded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 20:52:58 -04:00
parent eefa38cc75
commit 591706bd39
11 changed files with 187 additions and 50 deletions
+4 -5
View File
@@ -1,5 +1,4 @@
{# Per-host agent panel — embedded into /hosts/<id> via HTMX. Self-contained. #}
{% from "_macros.html" import metric_style %}
{% set scope = "steward:target:" ~ target.id if target else "" %}
<div class="card">
<div style="display:flex;align-items:baseline;justify-content:space-between;margin-bottom:0.75rem;gap:1rem;flex-wrap:wrap;">
@@ -22,20 +21,20 @@
<div style="display:flex;gap:1.5rem;flex-wrap:wrap;margin-bottom:0.6rem;">
<div title="Average CPU utilization across all cores">
<div style="{{ lbl }}">CPU</div>
<div style="font-weight:600;{{ metric_style(cpu) }}">{{ '%.0f%%'|format(cpu) if cpu is not none else '—' }}</div>
<div style="font-weight:600;{{ threshold_style(cpu, 'cpu') }}">{{ '%.0f%%'|format(cpu) if cpu is not none else '—' }}</div>
{{ sparks.cpu | safe }}</div>
<div title="RAM in use (total minus available; cache/buffers count as free)">
<div style="{{ lbl }}">Memory</div>
<div style="font-weight:600;{{ metric_style(mem) }}">{{ '%.0f%%'|format(mem) if mem is not none else '—' }}</div>
<div style="font-weight:600;{{ threshold_style(mem, 'mem') }}">{{ '%.0f%%'|format(mem) if mem is not none else '—' }}</div>
{{ sparks.mem | safe }}</div>
<div title="Root filesystem (/) usage — see Full metrics for every mount">
<div style="{{ lbl }}">Disk /</div>
<div style="font-weight:600;{{ metric_style(disk_root) }}">{{ '%.0f%%'|format(disk_root) if disk_root is not none else '—' }}</div>
<div style="font-weight:600;{{ threshold_style(disk_root, 'disk') }}">{{ '%.0f%%'|format(disk_root) if disk_root is not none else '—' }}</div>
{{ sparks.disk | safe }}</div>
{# Load /core: 100% = run queue matches CPU capacity, so warn 80 / crit 100. #}
<div title="1-minute load average ÷ {{ cores or '?' }} CPU cores (100% = run queue matches capacity). Raw 1m load: {{ '%.2f'|format(load1) if load1 is not none else '—' }}.">
<div style="{{ lbl }}">Load /core</div>
<div style="font-weight:600;{{ metric_style(load_per_core, warn=80, crit=100) }}">{{ '%d%%'|format(load_per_core) if load_per_core is not none else ('%.2f'|format(load1) if load1 is not none else '—') }}</div>
<div style="font-weight:600;{{ threshold_style(load_per_core, 'load') }}">{{ '%d%%'|format(load_per_core) if load_per_core is not none else ('%.2f'|format(load1) if load1 is not none else '—') }}</div>
{{ sparks.load | safe }}</div>
</div>
{% if psi.cpu is not none or psi.mem is not none or psi.io is not none %}