feat(alerts): one rule can cover every host, or every host in a group (#5464)
#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>
This commit is contained in:
@@ -1,16 +1,49 @@
|
||||
{# 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. #}
|
||||
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>
|
||||
<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>
|
||||
<select name="resource_name" required disabled data-none="1">
|
||||
<option value="">Nothing has reported from this source yet</option>
|
||||
</select>
|
||||
<p class="field-hint">
|
||||
|
||||
Reference in New Issue
Block a user