matrix-alertbot/matrix_alertbot/alertmanager.py

106 lines
3.5 KiB
Python
Raw Normal View History

2022-07-08 22:37:09 +02:00
from __future__ import annotations
import datetime
from typing import Any, Dict, List
import aiohttp
import pytimeparse
from aiohttp import ClientError
from diskcache import Cache
2022-07-06 00:54:13 +02:00
from matrix_alertbot.errors import (
AlertmanagerError,
AlertNotFoundError,
SilenceNotFoundError,
)
2022-07-08 22:46:04 +02:00
class AlertmanagerClient:
def __init__(self, url: str, cache: Cache) -> None:
self.api_url = f"{url}/api/v2"
self.cache = cache
self.session = aiohttp.ClientSession()
async def close(self) -> None:
await self.session.close()
async def get_alerts(self) -> List[Dict]:
2022-07-06 00:54:13 +02:00
try:
2022-07-08 23:23:38 +02:00
async with self.session.get(f"{self.api_url}/alerts") as response:
response.raise_for_status()
return await response.json()
except ClientError as e:
2022-07-06 00:54:13 +02:00
raise AlertmanagerError(f"Cannot fetch alerts from Alertmanager") from e
async def get_alert(self, fingerprint: str) -> Dict:
alerts = await self.get_alerts()
2022-07-06 00:54:13 +02:00
return self._find_alert(fingerprint, alerts)
async def get_alert_labels(self, fingerprint: str) -> Dict[str, str]:
alert = await self.get_alert(fingerprint)
2022-07-06 00:54:13 +02:00
return alert["labels"]
async def create_silence(self, fingerprint: str, duration: str, user: str) -> str:
labels = await self.get_alert_labels(fingerprint)
matchers = []
for label_name, label_value in labels.items():
matchers.append(
{"name": label_name, "value": label_value, "isRegex": False}
)
start_time = datetime.datetime.now()
duration_seconds = pytimeparse.parse(duration)
duration_delta = datetime.timedelta(seconds=duration_seconds)
end_time = start_time + duration_delta
silence = {
"matchers": matchers,
2022-07-05 23:35:19 +02:00
"startsAt": start_time.isoformat(),
"endsAt": end_time.isoformat(),
"createdBy": user,
"comment": "Acknowledge alert from Matrix",
}
2022-07-06 00:54:13 +02:00
try:
async with self.session.post(
f"{self.api_url}/silences", json=silence
) as response:
response.raise_for_status()
data = await response.json()
except ClientError as e:
2022-07-06 00:54:13 +02:00
raise AlertmanagerError(
f"Cannot create silence for alert fingerprint {fingerprint}"
) from e
return data["silenceID"]
2022-07-09 00:08:51 +02:00
async def delete_silence(self, fingerprint: str) -> List[str]:
alert = await self.get_alert(fingerprint)
2022-07-06 00:54:13 +02:00
alert_state = alert["status"]["state"]
if alert_state != "suppressed":
raise SilenceNotFoundError(
f"Cannot find silences for alert fingerprint {fingerprint} in state {alert_state}"
)
silences = alert["status"]["silencedBy"]
for silence in silences:
await self._delete_silence_by_id(silence)
2022-07-09 00:08:51 +02:00
return silences
async def _delete_silence_by_id(self, silence: str) -> None:
try:
async with self.session.delete(
f"{self.api_url}/silence/{silence}"
) as response:
2022-07-06 00:54:13 +02:00
response.raise_for_status()
except ClientError as e:
raise AlertmanagerError(f"Cannot delete silence with ID {silence}") from e
2022-07-06 00:54:13 +02:00
@staticmethod
2022-07-06 00:54:13 +02:00
def _find_alert(fingerprint: str, alerts: List[Dict]) -> Dict:
for alert in alerts:
if alert["fingerprint"] == fingerprint:
return alert
raise AlertNotFoundError(f"Cannot find alert with fingerprint {fingerprint}")