feat(alerts): a rule can fire when a reading rises, "rose by N" (#6724)
A rule compared a reading with a fixed number, so Synology Disk 1's bad sectors going from 3,160 to 3,392 could say nothing: a count that only grows is soon past any line. "rose by" compares each reading with the one before it, kept on the alert's state and only for such a rule. A fall (a replaced disk) doesn't fire. On the rule form, through MCP, and worded in emails, the alert sentence, history and the timeline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ QUIET = frozenset({"inactive", "resolved"})
|
||||
# The worst thing an episode went through, most severe first.
|
||||
_SEVERITY = ("firing", "acknowledged", "no_data", "pending")
|
||||
_WORDS = {">": "above", ">=": "at or above", "<": "below", "<=": "at or below",
|
||||
"==": "at", "!=": "other than"}
|
||||
"==": "at", "!=": "other than", "rose": "on a rise of"}
|
||||
|
||||
|
||||
def fires_when(rule) -> str:
|
||||
|
||||
@@ -15,7 +15,7 @@ from steward.models.users import UserRole
|
||||
|
||||
alerts_bp = Blueprint("alerts", __name__, url_prefix="/alerts")
|
||||
|
||||
_OPERATORS = [(op.value, op.value) for op in AlertOperator]
|
||||
_OPERATORS = [(op.value, op.said) for op in AlertOperator]
|
||||
|
||||
def _form_condition(form) -> dict:
|
||||
"""The operator and threshold the form asks for. An up/down metric's form
|
||||
|
||||
@@ -154,7 +154,7 @@ def condition_text(metric: str, operator: AlertOperator | str, threshold: float)
|
||||
if _is_breached(v, operator, threshold)]
|
||||
if len(fires_on) == 1:
|
||||
return f"is {fires_on[0]}"
|
||||
return f"{metric_label(metric)} {operator.value} {metric_value(metric, threshold)}"
|
||||
return f"{metric_label(metric)} {operator.said} {metric_value(metric, threshold)}"
|
||||
|
||||
|
||||
def alert_sentence(rule: AlertRule, label: str, value: float | None,
|
||||
@@ -175,7 +175,7 @@ def alert_sentence(rule: AlertRule, label: str, value: float | None,
|
||||
# not how long ago someone acknowledged it (the page says that apart).
|
||||
word = "breaching" if label == "acknowledged" else label
|
||||
for_how_long = f", {word} for {held}" if held else f", {word}"
|
||||
condition = f" ({rule.operator.value} {metric_value(metric, rule.threshold)})"
|
||||
condition = f" ({rule.operator.said} {metric_value(metric, rule.threshold)})"
|
||||
counted = count_words(metric, value)
|
||||
if counted and rule.resource_name in WHOLE_RESOURCES:
|
||||
# The count is the whole of it, and links where the swarm is shown.
|
||||
@@ -599,6 +599,8 @@ async def rule_fields(db, *, name: str, source_module: str, resource_name: str,
|
||||
threshold = float(threshold)
|
||||
except (TypeError, ValueError):
|
||||
raise ValueError(f"threshold must be a number, got {threshold!r}.") from None
|
||||
if op == AlertOperator.rose and threshold <= 0:
|
||||
raise ValueError("A rule on a rise needs how much it rises by, above 0.")
|
||||
try:
|
||||
consecutive = (1 if consecutive_failures_required in (None, "")
|
||||
else int(consecutive_failures_required))
|
||||
|
||||
Reference in New Issue
Block a user