2022-07-04 01:03:24 +02:00
|
|
|
import datetime
|
|
|
|
from typing import Dict, List
|
|
|
|
|
2022-07-06 01:19:44 +02:00
|
|
|
import diskcache
|
2022-07-04 01:03:24 +02:00
|
|
|
import pytimeparse
|
|
|
|
import requests
|
2022-07-06 00:54:13 +02:00
|
|
|
from requests import RequestException
|
2022-07-04 01:03:24 +02:00
|
|
|
|
2022-07-06 00:54:13 +02:00
|
|
|
from matrix_alertbot.config import Config
|
|
|
|
from matrix_alertbot.errors import (
|
|
|
|
AlertmanagerError,
|
|
|
|
AlertNotFoundError,
|
|
|
|
SilenceNotFoundError,
|
|
|
|
)
|
2022-07-04 01:03:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
class AlertmanagerClient:
|
2022-07-06 01:19:44 +02:00
|
|
|
def __init__(self, config: Config) -> None:
|
|
|
|
url = config.alertmanager_url
|
2022-07-04 01:03:24 +02:00
|
|
|
self.api_url = f"{url}/api/v2"
|
2022-07-06 01:19:44 +02:00
|
|
|
self.cache = diskcache.Cache(config.cache_dir)
|
2022-07-04 01:03:24 +02:00
|
|
|
|
|
|
|
def get_alerts(self) -> List[Dict]:
|
2022-07-06 00:54:13 +02:00
|
|
|
try:
|
|
|
|
response = requests.get(f"{self.api_url}/alerts")
|
|
|
|
response.raise_for_status()
|
|
|
|
except RequestException as e:
|
|
|
|
raise AlertmanagerError(f"Cannot fetch alerts from Alertmanager") from e
|
2022-07-04 01:03:24 +02:00
|
|
|
return response.json()
|
|
|
|
|
2022-07-06 00:54:13 +02:00
|
|
|
def get_alert(self, fingerprint: str) -> Dict:
|
|
|
|
alerts = self.get_alerts()
|
|
|
|
return self._find_alert(fingerprint, alerts)
|
|
|
|
|
2022-07-04 01:03:24 +02:00
|
|
|
def get_alert_labels(self, fingerprint: str) -> Dict[str, str]:
|
2022-07-06 00:54:13 +02:00
|
|
|
alert = self.get_alert(fingerprint)
|
|
|
|
return alert["labels"]
|
2022-07-04 01:03:24 +02:00
|
|
|
|
|
|
|
def create_silence(self, fingerprint: str, duration: str, user: str) -> str:
|
|
|
|
labels = 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(),
|
2022-07-04 01:03:24 +02:00
|
|
|
"createdBy": user,
|
|
|
|
"comment": "Acknowledge alert from Matrix",
|
|
|
|
}
|
2022-07-06 00:54:13 +02:00
|
|
|
try:
|
|
|
|
response = requests.post(f"{self.api_url}/silences", json=silence)
|
|
|
|
response.raise_for_status()
|
|
|
|
except RequestException as e:
|
|
|
|
raise AlertmanagerError(
|
|
|
|
f"Cannot create silence for alert fingerprint {fingerprint}"
|
|
|
|
) from e
|
2022-07-04 01:03:24 +02:00
|
|
|
data = response.json()
|
|
|
|
return data["silenceID"]
|
|
|
|
|
2022-07-06 00:54:13 +02:00
|
|
|
def delete_silence(self, fingerprint: str) -> None:
|
|
|
|
alert = self.get_alert(fingerprint)
|
|
|
|
|
|
|
|
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:
|
|
|
|
try:
|
|
|
|
response = requests.delete(f"{self.api_url}/silence/{silence}")
|
|
|
|
response.raise_for_status()
|
|
|
|
except RequestException as e:
|
|
|
|
raise AlertmanagerError(
|
|
|
|
f"Cannot delete silence with ID {silence}"
|
|
|
|
) from e
|
|
|
|
|
2022-07-04 01:03:24 +02:00
|
|
|
@staticmethod
|
2022-07-06 00:54:13 +02:00
|
|
|
def _find_alert(fingerprint: str, alerts: List[Dict]) -> Dict:
|
2022-07-04 01:03:24 +02:00
|
|
|
for alert in alerts:
|
|
|
|
if alert["fingerprint"] == fingerprint:
|
|
|
|
return alert
|
|
|
|
raise AlertNotFoundError(f"Cannot find alert with fingerprint {fingerprint}")
|