Includes atomic migrations, per-rule reminder semantics, first-run helper script and the note on the undecided UI chat idea. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
48 lines
1.2 KiB
Python
48 lines
1.2 KiB
Python
from dataclasses import dataclass
|
|
|
|
from ..checkmk import FAILURE_UNEXPECTED, CheckmkError
|
|
|
|
FAILURE_THRESHOLD = 3
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class OutageNotice:
|
|
kind: str
|
|
since: int
|
|
brief: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RestoredNotice:
|
|
since: int
|
|
now: int
|
|
|
|
|
|
def classify(exc: Exception) -> tuple[str, str]:
|
|
if isinstance(exc, CheckmkError):
|
|
return exc.kind, exc.brief
|
|
return FAILURE_UNEXPECTED, type(exc).__name__
|
|
|
|
|
|
class OutageTracker:
|
|
def __init__(self, threshold: int = FAILURE_THRESHOLD):
|
|
self._threshold = threshold
|
|
self._failures = 0
|
|
self._since = 0
|
|
self._announced = False
|
|
|
|
def failed(self, kind: str, brief: str, now: int) -> OutageNotice | None:
|
|
if self._failures == 0:
|
|
self._since = now
|
|
self._failures += 1
|
|
if self._failures < self._threshold or self._announced:
|
|
return None
|
|
self._announced = True
|
|
return OutageNotice(kind, self._since, brief)
|
|
|
|
def succeeded(self, now: int) -> RestoredNotice | None:
|
|
notice = RestoredNotice(self._since, now) if self._announced else None
|
|
self._failures = 0
|
|
self._announced = False
|
|
return notice
|