{% extends "base.html" %} {% from "_macros.html" import crumbs %} {% block title %}{% if rule %}Edit Rule{% else %}New Alert Rule{% endif %} — Steward{% endblock %} {% block breadcrumb %}{{ crumbs([("Alerts", "/alerts/"), ("Edit rule" if rule else "New rule", "")]) }}{% endblock %} {% block content %}

{% if rule %}Edit Rule{% else %}New Alert Rule{% endif %}

{{ csrf_field() }}
{# ── Source module ───────────────────────────────────────────────────── #}
{# ── Dynamic resource + metric ────────────────────────────────────────── #}
{# Inline the same partial logic so the page is fully usable without JS #}
{% if resources %} {% else %}

Resource names populate from live metric data. Start the scheduler and try again.

{% endif %}
{# ── Condition ───────────────────────────────────────────────────────── #}

A firing or acknowledged alert resolves only after this many checks in a row are back within the threshold. An acknowledgement holds until then.

Silence is always shown as NO DATA on the Alerts page: no sample for three of the metric's usual gaps (at least 5 minutes). This also sends a notification when it starts and when data resumes. A firing alert that goes silent stays firing.

{# ── Metric reference ────────────────────────────────────────────────── #}
Metric reference
{% set descs = { "icmp": [("is_up", "1.0 = reachable, 0.0 = unreachable"), ("response_ms", "round-trip latency in milliseconds")], "tcp": [("is_up", "1.0 = port open, 0.0 = unreachable"), ("response_ms", "TCP connect time in milliseconds")], "dns": [("is_up", "1.0 = resolved (and matches expected IP), 0.0 = failed"), ("response_ms", "resolution time in milliseconds")], "traefik": [("request_rate", "requests/sec for this router"), ("error_rate_4xx_pct", "% of responses that are 4xx"), ("error_rate_5xx_pct", "% of responses that are 5xx"), ("latency_p50_ms", "median response time ms"), ("latency_p95_ms", "p95 response time ms"), ("latency_p99_ms", "p99 response time ms"), ("response_bytes_rate", "bytes/sec of responses"), ("cert_expiry_days", "days until TLS cert expires")], "unifi": [("is_up", "1.0 = WAN up, 0.0 = down"), ("latency_ms", "WAN latency in ms"), ("total_clients", "number of connected clients")], "docker": [("cpu_pct", "container CPU usage %"), ("mem_pct", "container memory usage %")], "http": [("is_up", "1.0 = check passed, 0.0 = failed"), ("response_ms", "HTTP response time in ms")], "host_agent": [("seconds_since_seen", "seconds since the agent last reported; > 180 = offline"), ("cpu_pct", "host CPU usage %"), ("mem_used_pct", "host memory usage %"), ("disk_used_pct_worst", "fullest disk, % used"), ("disk_used_pct", "one mount, % used (resource host:mount)")], } %} {% for src, pairs in descs.items() %}
{{ src }}
{% for metric, desc in pairs %}
{{ metric }} {{ desc }}
{% endfor %}
{% endfor %}
{# ── Ansible action (admin only) ─────────────────────────────────────── #} {% if session.get("user_role") == "admin" %} {% set act = rule.ansible_action if rule and rule.ansible_action else None %}

Admin only. Runs once when the alert transitions into FIRING. steward_alert_* context vars (resource, metric, value…) are injected automatically.

{% endif %} {% if not rule %} {% endif %}
Cancel
{% endblock %}