71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
import hashlib
|
|
import json
|
|
import time
|
|
from typing import Any
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.config import get_settings
|
|
from app.homelab.netdata import NetdataClient
|
|
from app.homelab.state import get_state, set_state
|
|
|
|
ALERT_COOLDOWN_SEC = 1800
|
|
|
|
|
|
def _alarm_key(alarm: dict[str, Any]) -> str:
|
|
return f"{alarm.get('name')}:{alarm.get('status')}"
|
|
|
|
|
|
def check_netdata_alerts(db: Session) -> list[str]:
|
|
settings = get_settings()
|
|
if not settings.netdata_alerts_enabled:
|
|
return []
|
|
|
|
result = NetdataClient().fetch_alarms()
|
|
if not result.get("ok"):
|
|
return []
|
|
|
|
alarms = result.get("alarms") or []
|
|
significant = [
|
|
a for a in alarms
|
|
if (a.get("status") or "").lower() in ("warning", "critical", "raised")
|
|
]
|
|
if not significant:
|
|
return []
|
|
|
|
prev_raw = get_state(db, "netdata_alarm_hashes") or "{}"
|
|
try:
|
|
prev_map: dict[str, float] = json.loads(prev_raw)
|
|
except json.JSONDecodeError:
|
|
prev_map = {}
|
|
|
|
now = time.time()
|
|
notices: list[str] = []
|
|
new_map = dict(prev_map)
|
|
|
|
for alarm in significant:
|
|
key = _alarm_key(alarm)
|
|
digest = hashlib.sha256(json.dumps(alarm, sort_keys=True).encode()).hexdigest()[:16]
|
|
state_key = f"netdata:{key}:{digest}"
|
|
last_sent = prev_map.get(state_key, 0)
|
|
if now - last_sent < ALERT_COOLDOWN_SEC:
|
|
continue
|
|
|
|
host = alarm.get("host") or "server"
|
|
value = alarm.get("value_string") or ""
|
|
info = alarm.get("info") or alarm.get("name") or "алерт"
|
|
status = alarm.get("status") or "alert"
|
|
link = settings.netdata_public_url
|
|
link_part = f" [Netdata]({link})" if link else ""
|
|
notices.append(
|
|
f"⚠️ **Netdata** · {host}: {info} — {status}"
|
|
+ (f" ({value})" if value else "")
|
|
+ link_part
|
|
)
|
|
new_map[state_key] = now
|
|
|
|
if notices:
|
|
set_state(db, "netdata_alarm_hashes", json.dumps(new_map))
|
|
|
|
return notices
|