Lesender Selbsttest (Version, Services, Hosts, Downtimes) beim Start und täglich 04:00, Seite /selftest, Teilcheck checkmk_api in /health, Chat-Meldung bei Wechsel. Toleranter Antwort-Parser (value/members, state_type int/String), Fallbacks für Host-Abfrage und Downtime-Liste, Fehlerhinweise je HTTP-Status. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
57 lines
2.7 KiB
Python
57 lines
2.7 KiB
Python
import asyncio
|
|
import time
|
|
import uuid
|
|
from typing import Callable, Iterable, Sequence
|
|
|
|
from ..chat import Target
|
|
from ..models import KIND_SYSTEM, STATE_CRIT, STATE_OK, Event, Key, StateRow
|
|
from ..poller.outage import OutageNotice, RestoredNotice
|
|
from .delivery import AdapterProvider, Deliverer
|
|
from .extras import AlertActions
|
|
from .repository import AlertRepository
|
|
from .sendlimit import SendLimiter
|
|
from .system_text import (SERVICE_BASELINE, SERVICE_CHECKMK, SERVICE_SELFTEST, SYSTEM_HOST, baseline_message,
|
|
baseline_plain, outage_plain, restored_plain, wrap)
|
|
|
|
|
|
class SystemNotifier:
|
|
def __init__(self, repo: AlertRepository, adapters: AdapterProvider, limiter: SendLimiter,
|
|
send_limit: Callable[[], int], actions: AlertActions | None = None,
|
|
on_baseline: Callable[[Iterable[Key]], None] | None = None,
|
|
clock: Callable[[], float] = time.time):
|
|
self._repo = repo
|
|
self._adapters = adapters
|
|
self._deliverer = Deliverer(adapters)
|
|
self._limiter = limiter
|
|
self._send_limit = send_limit
|
|
self._actions = actions
|
|
self._on_baseline = on_baseline
|
|
self._clock = clock
|
|
|
|
async def outage(self, notice: OutageNotice) -> None:
|
|
await self._post(SERVICE_CHECKMK, STATE_CRIT, outage_plain(notice))
|
|
|
|
async def restored(self, notice: RestoredNotice) -> None:
|
|
await self._post(SERVICE_CHECKMK, STATE_OK, restored_plain(notice))
|
|
|
|
async def baseline(self, rows: Sequence[StateRow]) -> None:
|
|
if self._on_baseline is not None:
|
|
self._on_baseline([row.key for row in rows])
|
|
await self._post(SERVICE_BASELINE, STATE_OK, baseline_plain(rows), baseline_message(rows))
|
|
|
|
async def selftest(self, ok: bool, plain: str) -> None:
|
|
await self._post(SERVICE_SELFTEST, STATE_OK if ok else STATE_CRIT, plain)
|
|
|
|
async def _post(self, service: str, state: int, plain: str, message: str | None = None) -> None:
|
|
event = Event(ts=int(self._clock()), host=SYSTEM_HOST, service=service, kind=KIND_SYSTEM, state=state,
|
|
output=plain, ai_enabled=False)
|
|
ids = await asyncio.to_thread(self._repo.insert_alerts, [event], uuid.uuid4().hex)
|
|
targets = [Target(a.name, a.default_target) for a in await self._adapters()]
|
|
for target in targets:
|
|
self._limiter.acquire(target, self._send_limit(), force=True)
|
|
extras = (lambda backend: self._actions.build(backend, [event], ids)) if self._actions else None
|
|
refs = await self._deliverer.send(message or wrap(plain), targets, extras)
|
|
for ref in refs:
|
|
for alert_id in ids:
|
|
await asyncio.to_thread(self._repo.add_delivery, alert_id, ref)
|