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
+16 -4
View File
@@ -372,12 +372,13 @@ def test_patterns_page_adds_edits_and_removes_and_a_rename_moves_counts(app):
assert "at least one phrase" in await refused.get_data(as_text=True)
made = await admin.post("/plugins/syslog/patterns/new", headers=csrf,
form={"name": "Quota full", "phrases": "100%\nquota", "max_severity": "4",
"window_minutes": "60", "enabled": "on"})
"window_minutes": "60"})
assert made.status_code == 302
async with app.db_sessionmaker() as s:
rows = {p.key: p for p in (await s.execute(select(models.SyslogPattern))).scalars()}
assert set(rows) == {"nfs", "quota_full"}
assert (rows["quota_full"].phrases, rows["quota_full"].max_severity) == (["100%", "quota"], 4)
assert rows["quota_full"].enabled # a new pattern counts
assert rows["nfs"].phrases[0] == "nfsd:"
await patterns.record_counts(app, now)
@@ -391,13 +392,24 @@ def test_patterns_page_adds_edits_and_removes_and_a_rename_moves_counts(app):
)).get_data(as_text=True)
assert '<option value="syslog" selected>' in rule_form # aimed at the pattern's count
# Edited: the key, and so the metric, stay.
edited = await admin.post(f"/plugins/syslog/patterns/{rows['quota_full'].id}/edit", headers=csrf,
# Paused from the list (#6419), not from a tick box in the form.
quota = rows["quota_full"].id
assert (await admin.post(f"/plugins/syslog/patterns/{quota}/toggle", headers=csrf)).status_code == 302
page = " ".join((await (await admin.get("/plugins/syslog/patterns")).get_data(as_text=True)).split())
assert '<span class="badge badge-dim">Paused</span>' in page and ">Resume</button>" in page
form_page = await (await admin.get(f"/plugins/syslog/patterns/{quota}/edit")).get_data(as_text=True)
assert 'name="enabled"' not in form_page
# Edited: the key, and so the metric, stay, and so does the pause.
edited = await admin.post(f"/plugins/syslog/patterns/{quota}/edit", headers=csrf,
form={"name": "Quota", "phrases": "quota", "window_minutes": "15"})
assert edited.status_code == 302
async with app.db_sessionmaker() as s:
p = await s.get(models.SyslogPattern, rows["quota_full"].id)
p = await s.get(models.SyslogPattern, quota)
assert (p.key, p.name, p.enabled, p.max_severity) == ("quota_full", "Quota", False, None)
assert (await admin.post(f"/plugins/syslog/patterns/{quota}/toggle", headers=csrf)).status_code == 302
async with app.db_sessionmaker() as s:
assert (await s.get(models.SyslogPattern, quota)).enabled
# A renamed source takes its counts with it.
renamed = await admin.post(f"/plugins/syslog/sources/{src_id}/edit", headers=csrf,