#5464 "Group rules: one rule covers every host, or every host in a group", step 2 of milestone 499 "Alerts you can trust". Each rule named one host, so the live instance carried six rules for three hosts, and a new host had no agent-offline or disk alert until someone copied them. - alert_rules.covers / cover_group / parent_id (migration 0041). A rule on host_agent may cover "all_hosts" or a "group" (an inventory group: the Hosts ruling, #5294). A covering rule has no state; Steward keeps one ordinary child rule per covered host, named "<rule>: <host>", so each host's alert keeps its own state, history and acknowledgement. - alerts.service.reconcile_children: a covered host that has reported in the last 7 days gets a child; a host no longer covered loses its child; children take the parent's condition, checks, notify setting, action and on/off. Run on save and each minute (alert_reconcile). - A child is changed through its parent: edit, toggle and delete of a child are refused (409 / PartOfCoveringRule); its edit page opens the parent. - Rule form: a Covers choice for host sources, with the group picker; fixed once the rule exists. Rules list: a covering rule reads "every host in swarm · 3 hosts" with its hosts' rules nested under it, each opening its history. The history picker and the rule count leave covering rules out. - MCP: create_alert_rule takes covers/group; update_alert_rule takes group and passes changes down; list_alert_rules shows covers, hosts and part_of. - docs/core/alerting.md: "Rules that cover hosts". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
3.6 KiB
HTML
68 lines
3.6 KiB
HTML
{# alerts/_rule_fields.html — the resource and metric pickers for one source;
|
|
the rule form includes it and swaps it in again when the source changes.
|
|
For a source whose resources are hosts, the rule can instead cover every
|
|
host, or every host in an inventory group (#5464): Steward then keeps one
|
|
rule per host under it. What a rule covers is fixed once it exists. #}
|
|
{% set covers = current_covers|default("resource") %}
|
|
{% set one = covers == "resource" %}
|
|
{% if host_source|default(false) %}
|
|
<div class="form-group">
|
|
<label for="rule-covers">Covers</label>
|
|
{% if covers_locked|default(false) %}
|
|
<input type="hidden" name="covers" value="{{ covers }}">
|
|
<p style="margin:0;">{% if one %}One host, or one part of a host{% elif covers == "all_hosts" %}Every host{% else %}Every host in a group{% endif %}
|
|
<span class="tone-dim">· fixed once the rule exists</span></p>
|
|
{% else %}
|
|
<select id="rule-covers" name="covers" data-covers>
|
|
<option value="resource" {% if one %}selected{% endif %}>One host, or one part of a host</option>
|
|
<option value="all_hosts" {% if covers == "all_hosts" %}selected{% endif %}>Every host</option>
|
|
<option value="group" {% if covers == "group" %}selected{% endif %} {% if not groups %}disabled{% endif %}>Every host in a group{% if not groups %} (no inventory groups yet){% endif %}</option>
|
|
</select>
|
|
{% endif %}
|
|
<p class="field-hint">Covering hosts keeps one alert per host under this rule, each named
|
|
"rule: host". A host that starts reporting, or joins the group, is covered within a minute;
|
|
one that leaves loses its alert and that alert's history.</p>
|
|
</div>
|
|
<div class="form-group" data-cover-group {% if covers != "group" %}hidden{% endif %}>
|
|
<label for="rule-group">Group</label>
|
|
<select id="rule-group" name="cover_group" {% if covers != "group" %}disabled{% endif %}>
|
|
{% for g in groups %}
|
|
<option value="{{ g }}" {% if g == current_group|default("") %}selected{% endif %}>{{ g }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<p class="field-hint">A host is in a group through its linked Ansible target (Ansible → Inventory).</p>
|
|
</div>
|
|
{% endif %}
|
|
<div class="form-group" data-cover-resource {% if not one %}hidden{% endif %}>
|
|
<label>Resource</label>
|
|
{% if resources %}
|
|
<select name="resource_name" required {% if not one %}disabled{% endif %}>
|
|
{% if not current_resource %}<option value="" disabled selected>— pick a resource —</option>{% endif %}
|
|
{% for r in resources %}
|
|
<option value="{{ r }}" {% if r == current_resource %}selected{% endif %}>{{ r }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% else %}
|
|
<select name="resource_name" required disabled data-none="1">
|
|
<option value="">Nothing has reported from this source yet</option>
|
|
</select>
|
|
<p class="field-hint">
|
|
The list fills from readings this source has recorded. Come back once it has reported.
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Metric</label>
|
|
<select name="metric_name" required data-metric-picker>
|
|
{% if not current_metric %}<option value="" disabled selected>— pick a metric —</option>{% endif %}
|
|
{% for m in metrics %}
|
|
<option value="{{ m }}" data-unit="{{ metric_unit(m) }}" data-about="{{ metric_about(m) }}" {% if m in ("is_up", "is_healthy") %}data-binary{% endif %}
|
|
{% if m == current_metric %}selected{% endif %}>{{ metric_label(m) }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if current_metric and metric_about(current_metric) %}
|
|
<p class="field-hint" data-metric-about>{{ metric_about(current_metric) }}</p>
|
|
{% else %}<p class="field-hint" data-metric-about hidden></p>{% endif %}
|
|
</div>
|