fix(prometheus, syslog): a target or pattern pauses with a Pause/Resume button, not an "on" tick box in its form (#6419)
CI / lint (push) Successful in 2s
CI / unit (push) Failing after 1m42s
CI / integration (push) Successful in 1m45s
CI / publish (push) Skipped

Monitors, schedules and maintenance plans already pause with a
Pause/Resume button and say "Paused" where they're listed. Prometheus
targets and syslog patterns only had a tick box inside the edit form,
so a target refused by its endpoint couldn't be stopped from its page.

- Both get a Pause/Resume button (target page and list; patterns list)
  posting to /toggle, recorded in the audit log, and a Paused badge.
- The forms lose the tick box. A new one starts running; an edit keeps
  whether it is paused.
- A target's "off" state is now "paused", in its pages and MCP tools.
  A resumed target reads "not scraped yet" until its next scrape, and its
  counter rates start over.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 17:48:31 -04:00
co-authored by Claude Opus 5.5
parent 0b787a0676
commit d20d78498e
15 changed files with 104 additions and 51 deletions
+17 -3
View File
@@ -381,13 +381,13 @@ async def add_preset(key: str):
@syslog_bp.get("/patterns/new")
@require_role(UserRole.admin)
async def new_pattern():
return await _render_pattern_form(None, {"window_minutes": 15, "enabled": True})
return await _render_pattern_form(None, {"window_minutes": 15})
def _typed_pattern(form) -> dict:
return {"name": form.get("name", ""), "phrases": form.get("phrases", ""),
"max_severity": form.get("max_severity", ""), "source_id": form.get("source_id", ""),
"window_minutes": form.get("window_minutes", 15, type=int), "enabled": "enabled" in form}
"window_minutes": form.get("window_minutes", 15, type=int)}
@syslog_bp.post("/patterns/new")
@@ -426,7 +426,7 @@ async def edit_pattern(pattern_id: str):
return await _render_pattern_form(p, {
"name": p.name, "phrases": "\n".join(p.phrases or []),
"max_severity": "" if p.max_severity is None else str(p.max_severity),
"source_id": p.source_id or "", "window_minutes": p.window_minutes, "enabled": p.enabled})
"source_id": p.source_id or "", "window_minutes": p.window_minutes})
@syslog_bp.post("/patterns/<pattern_id>/edit")
@@ -451,6 +451,20 @@ async def save_pattern(pattern_id: str):
return redirect(PATTERNS)
@syslog_bp.post("/patterns/<pattern_id>/toggle")
@require_role(UserRole.admin)
async def toggle_pattern(pattern_id: str):
"""Pause a pattern's counting, or resume it, from the patterns list."""
async with current_app.db_sessionmaker() as db:
async with db.begin():
p = await _pattern_or_404(db, pattern_id)
p.enabled = not p.enabled
enabled, name = p.enabled, p.name
await log_audit(current_app, *_actor(), "syslog.pattern_resumed" if enabled else "syslog.pattern_paused",
entity_type="syslog_pattern", entity_id=pattern_id, detail={"name": name})
return redirect(PATTERNS)
@syslog_bp.post("/patterns/<pattern_id>/delete")
@require_role(UserRole.admin)
async def delete_pattern(pattern_id: str):