feat(syslog): list patterns and add a ready-made one through MCP
CI / lint (push) Successful in 1s
CI / unit (push) Failing after 1m35s
CI / integration (push) Successful in 1m47s
CI / publish (push) Skipped

Patterns could only be added on the Syslog page, so an agent asked to map
them on the NASes couldn't. list_syslog_patterns gives each pattern's metric
and the ready-made ones not added; add_syslog_preset (admin, audited) adds one.
The key-picking helper moves beside the patterns for both callers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-30 19:04:34 -04:00
co-authored by Claude Opus 5.5
parent 4cce3f0d12
commit 45aae6d585
6 changed files with 99 additions and 16 deletions
+3 -12
View File
@@ -21,7 +21,7 @@ from steward.models.users import UserRole
from .models import SyslogLine, SyslogPattern, SyslogSource
from .network import on_own_network
from .parse import SEVERITIES, facility_name, severity_name
from .patterns import (PRESETS, PRESETS_BY_KEY, SOURCE_MODULE, WINDOWS, PatternError, key_for, parse_form,
from .patterns import (PRESETS, PRESETS_BY_KEY, SOURCE_MODULE, WINDOWS, PatternError, free_key, key_for, parse_form,
resource_of)
from .receiver import LISTENER, MAX_SENDERS, send_to, settings
from .service import (AROUND_LINES, AROUND_SEVERITY, MARK_SEVERITY, MAX_MARKS, PANEL_LINES, PANEL_SPAN,
@@ -339,15 +339,6 @@ async def patterns_index():
any_sources=any(s.state == "accepted" for s in sources.values()))
async def _free_key(db, wanted: str) -> str:
taken = set((await db.execute(select(SyslogPattern.key))).scalars())
key, n = wanted, 2
while key in taken:
key = f"{wanted[:44]}_{n}"
n += 1
return key
async def _accepted_sources(db) -> list:
return list((await db.execute(select(SyslogSource).where(SyslogSource.state == "accepted")
.order_by(SyslogSource.name))).scalars())
@@ -368,7 +359,7 @@ async def add_preset(key: str):
abort(404)
async with current_app.db_sessionmaker() as db:
async with db.begin():
pattern = SyslogPattern(key=await _free_key(db, preset.key), name=preset.name,
pattern = SyslogPattern(key=await free_key(db, preset.key), name=preset.name,
phrases=list(preset.phrases), max_severity=preset.max_severity)
db.add(pattern)
await db.flush()
@@ -402,7 +393,7 @@ async def create_pattern():
async with db.begin():
if values["source_id"] and await db.get(SyslogSource, values["source_id"]) is None:
values["source_id"] = None
pattern = SyslogPattern(key=await _free_key(db, key_for(values["name"])), **values)
pattern = SyslogPattern(key=await free_key(db, key_for(values["name"])), **values)
db.add(pattern)
await db.flush()
pattern_id = pattern.id