#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>
33 lines
1.2 KiB
Python
33 lines
1.2 KiB
Python
"""A rule can cover every host, or every host in an inventory group (#5464):
|
|
the covering rule keeps one child rule per host under it.
|
|
|
|
Revision ID: 0041_alert_rule_covers
|
|
Revises: 0040_alert_event_actor
|
|
Create Date: 2026-10-09
|
|
"""
|
|
from typing import Sequence, Union
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision: str = "0041_alert_rule_covers"
|
|
down_revision: Union[str, None] = "0040_alert_event_actor"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("alert_rules", sa.Column("covers", sa.String(16), nullable=False,
|
|
server_default="resource"))
|
|
op.add_column("alert_rules", sa.Column("cover_group", sa.String(128), nullable=True))
|
|
op.add_column("alert_rules", sa.Column(
|
|
"parent_id", sa.String(36), sa.ForeignKey("alert_rules.id", ondelete="CASCADE"),
|
|
nullable=True))
|
|
op.create_index("ix_alert_rules_parent_id", "alert_rules", ["parent_id"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("ix_alert_rules_parent_id", table_name="alert_rules")
|
|
op.drop_column("alert_rules", "parent_id")
|
|
op.drop_column("alert_rules", "cover_group")
|
|
op.drop_column("alert_rules", "covers")
|