""" Эндпоинты для обработки webhooks из систем мониторинга (Grafana, Zabbix, AlertManager). Автор: Сергей Антропов Сайт: https://devops.org.ru """ import logging from fastapi import APIRouter, HTTPException, Path, Body, Query from typing import Dict, Optional from app.models.grafana import GrafanaAlert from app.models.zabbix import ZabbixAlert from app.models.alertmanager import PrometheusAlert from app.modules.grafana import send as grafana_send from app.modules.zabbix import send as zabbix_send from app.modules.alertmanager import send as alertmanager_send from app.core.metrics import metrics logger = logging.getLogger(__name__) router = APIRouter(tags=["monitoring"]) @router.post( "/grafana/{group_name}/{thread_id}", name="Отправка вебхуков из Grafana", response_model=Dict[str, str], summary="Отправить алерт из Grafana", description="Эндпоинт для обработки webhooks из Grafana. **Не требует авторизации.**", responses={ 200: { "description": "Сообщение успешно отправлено", "content": { "application/json": { "example": {"status": "ok", "message": "Сообщение отправлено"} } } }, 400: { "description": "Некорректные данные запроса", "content": { "application/json": { "example": {"detail": "Неверный формат данных для Grafana алерта"} } } }, 404: { "description": "Группа не найдена", "content": { "application/json": { "example": {"detail": "Группа 'monitoring' не найдена в конфигурации"} } } }, 500: { "description": "Ошибка сервера", "content": { "application/json": { "example": {"detail": "Ошибка отправки сообщения"} } } } } ) async def send_grafana_alert( group_name: str = Path( ..., description="Имя группы из конфигурации (config/groups.json)", examples=["monitoring", "alerts", "devops"] ), thread_id: int = Path( ..., description="ID треда в группе (0 для отправки в основную группу без треда, поддерживается только для Telegram)", examples=[0, 123, 456] ), messenger: Optional[str] = Query( None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"] ), alert: GrafanaAlert = Body( ..., description="Данные алерта из Grafana", examples=[ { "title": "[Alerting] High CPU Usage", "ruleId": 674180201771804383, "ruleName": "High CPU Usage Alert", "state": "alerting", "evalMatches": [ { "value": 95.5, "metric": "cpu_usage_percent", "tags": {"host": "server01", "instance": "production"} } ], "orgId": 1, "dashboardId": 123, "panelId": 456, "tags": {"severity": "critical", "environment": "production"}, "ruleUrl": "http://grafana.cism-ms.ru/alerting/list", "message": "CPU usage is above 90% threshold for more than 5 minutes" }, { "title": "[OK] High CPU Usage", "ruleId": 674180201771804383, "ruleName": "High CPU Usage Alert", "state": "ok", "evalMatches": [ { "value": 45.2, "metric": "cpu_usage_percent", "tags": {"host": "server01", "instance": "production"} } ], "orgId": 1, "dashboardId": 123, "panelId": 456, "tags": {"severity": "critical", "environment": "production"}, "ruleUrl": "http://grafana.cism-ms.ru/alerting/list", "message": "CPU usage has returned to normal levels" } ] ) ) -> Dict[str, str]: """ Отправить алерт из Grafana в мессенджер. Принимает webhook от Grafana и отправляет сообщение в указанную группу мессенджера. Не требует авторизации (API ключ не нужен). Подробная документация: см. docs/monitoring/grafana.md """ metrics.increment_api_endpoint("grafana") logger.info(f"Получен алерт Grafana для группы {group_name}, тред {thread_id}, мессенджер {messenger}") try: await grafana_send(group_name, thread_id, alert, messenger) return { "status": "ok", "message": "Сообщение отправлено" } except ValueError as e: logger.error(f"Ошибка валидации: {e}") raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Ошибка отправки алерта Grafana: {e}") raise HTTPException(status_code=500, detail=f"Ошибка отправки сообщения: {str(e)}") @router.post( "/zabbix/{group_name}/{thread_id}", name="Отправка вебхуков из Zabbix", response_model=Dict[str, str], summary="Отправить алерт из Zabbix", description="Эндпоинт для обработки webhooks из Zabbix. **Не требует авторизации.**", responses={ 200: { "description": "Сообщение успешно отправлено", "content": { "application/json": { "example": {"status": "ok", "message": "Сообщение отправлено"} } } }, 400: { "description": "Некорректные данные запроса", "content": { "application/json": { "example": {"detail": "Неверный формат данных для Zabbix алерта"} } } }, 404: { "description": "Группа не найдена", "content": { "application/json": { "example": {"detail": "Группа 'monitoring' не найдена в конфигурации"} } } }, 500: { "description": "Ошибка сервера", "content": { "application/json": { "example": {"detail": "Ошибка отправки сообщения"} } } } } ) async def send_zabbix_alert( group_name: str = Path( ..., description="Имя группы из конфигурации (config/groups.json)", examples=["monitoring", "alerts", "devops"] ), thread_id: int = Path( ..., description="ID треда в группе (0 для отправки в основную группу без треда, поддерживается только для Telegram)", examples=[0, 123, 456] ), messenger: Optional[str] = Query( None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"] ), alert: ZabbixAlert = Body( ..., description="Данные алерта из Zabbix", examples=[ { "link": "https://zabbix.example.com/tr_events.php?triggerid=42667&eventid=8819711", "status": "PROBLEM", "action-id": "7", "alert-subject": "Problem: High CPU utilization (over 90% for 5m)", "alert-message": "Problem started at 16:48:44 on 2024.02.08\r\nProblem name: High CPU utilization (over 90% for 5m)\r\nHost: pnode28\r\nSeverity: Warning\r\nCurrent utilization: 95.2 %\r\n", "event-id": "8819711", "event-name": "High CPU utilization (over 90% for 5m)", "event-nseverity": "2", "event-opdata": "Current utilization: 95.2 %", "event-severity": "Warning", "host-name": "pnode28", "host-ip": "10.14.253.38", "host-port": "10050" }, { "link": "https://zabbix.example.com/tr_events.php?triggerid=42667&eventid=8819711", "status": "OK", "action-id": "7", "alert-subject": "Resolved in 1m 0s: High CPU utilization (over 90% for 5m)", "alert-message": "Problem has been resolved at 16:49:44 on 2024.02.08\r\nProblem name: High CPU utilization (over 90% for 5m)\r\nProblem duration: 1m 0s\r\nHost: pnode28\r\nSeverity: Warning\r\nOriginal problem ID: 8819711\r\n", "event-id": "8819711", "event-name": "High CPU utilization (over 90% for 5m)", "event-nseverity": "2", "event-opdata": "Current utilization: 70.9 %", "event-recovery-date": "2024.02.08", "event-recovery-time": "16:49:44", "event-duration": "1m 0s", "event-recovery-name": "High CPU utilization (over 90% for 5m)", "event-recovery-status": "RESOLVED", "event-recovery-tags": "Application:CPU", "event-severity": "Warning", "host-name": "pnode28", "host-ip": "10.14.253.38", "host-port": "10050" } ] ) ) -> Dict[str, str]: """ Отправить алерт из Zabbix в мессенджер. Принимает webhook от Zabbix и отправляет сообщение в указанную группу мессенджера. Не требует авторизации (API ключ не нужен). Подробная документация: см. docs/monitoring/zabbix.md """ metrics.increment_api_endpoint("zabbix") logger.info(f"Получен алерт Zabbix для группы {group_name}, тред {thread_id}, мессенджер {messenger}") try: await zabbix_send(group_name, thread_id, alert, messenger) return { "status": "ok", "message": "Сообщение отправлено" } except ValueError as e: logger.error(f"Ошибка валидации: {e}") raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Ошибка отправки алерта Zabbix: {e}") raise HTTPException(status_code=500, detail=f"Ошибка отправки сообщения: {str(e)}") @router.post( "/alertmanager/{k8s_cluster}/{group_name}/{thread_id}", name="Отправка вебхуков из AlertManager", response_model=Dict[str, str], summary="Отправить алерт из AlertManager", description="Эндпоинт для обработки webhooks из AlertManager. **Не требует авторизации.**", responses={ 200: { "description": "Сообщение успешно отправлено", "content": { "application/json": { "example": {"status": "ok", "message": "Сообщение отправлено"} } } }, 400: { "description": "Некорректные данные запроса", "content": { "application/json": { "example": {"detail": "Неверный формат данных для AlertManager алерта"} } } }, 404: { "description": "Группа не найдена", "content": { "application/json": { "example": {"detail": "Группа 'monitoring' не найдена в конфигурации"} } } }, 500: { "description": "Ошибка сервера", "content": { "application/json": { "example": {"detail": "Ошибка отправки сообщения"} } } } } ) async def send_alertmanager_alert( k8s_cluster: str = Path( ..., description="Имя Kubernetes кластера (используется для формирования URL к Grafana/Prometheus)", examples=["production", "staging", "development"] ), group_name: str = Path( ..., description="Имя группы из конфигурации (config/groups.json)", examples=["monitoring", "alerts", "devops"] ), thread_id: int = Path( ..., description="ID треда в группе (0 для отправки в основную группу без треда, поддерживается только для Telegram)", examples=[0, 123, 456] ), messenger: Optional[str] = Query( None, description="Тип мессенджера (telegram, max). Если не указан, используется из конфигурации группы", examples=["telegram", "max"] ), alert: PrometheusAlert = Body( ..., description="Данные алерта из AlertManager", examples=[ { "status": "firing", "externalURL": "http://alertmanager.example.com", "commonLabels": { "alertname": "HighCPUUsage", "severity": "critical", "namespace": "production", "pod": "app-deployment-7d8f9b4c5-abc123", "container": "app-container" }, "commonAnnotations": { "summary": "High CPU usage detected in production namespace", "description": "CPU usage is above 90% for 5 minutes on pod app-deployment-7d8f9b4c5-abc123", "runbook_url": "https://wiki.example.com/runbooks/high-cpu-usage" } }, { "status": "resolved", "externalURL": "http://alertmanager.example.com", "commonLabels": { "alertname": "HighCPUUsage", "severity": "critical", "namespace": "production", "pod": "app-deployment-7d8f9b4c5-abc123", "container": "app-container" }, "commonAnnotations": { "summary": "High CPU usage resolved in production namespace", "description": "CPU usage has returned to normal levels on pod app-deployment-7d8f9b4c5-abc123" } } ] ) ) -> Dict[str, str]: """ Отправить алерт из AlertManager в мессенджер. Эндпоинт для обработки webhooks из AlertManager. Не требует авторизации (API ключ не нужен). Подробная документация: см. docs/monitoring/alertmanager.md """ metrics.increment_api_endpoint("alertmanager") logger.info(f"Получен алерт AlertManager для кластера {k8s_cluster}, группы {group_name}, тред {thread_id}, мессенджер {messenger}") try: if not isinstance(alert, PrometheusAlert): raise HTTPException(status_code=400, detail="Неверный формат данных для AlertManager алерта") await alertmanager_send(k8s_cluster, group_name, thread_id, alert, messenger) return { "status": "ok", "message": "Сообщение отправлено" } except HTTPException: raise except ValueError as e: logger.error(f"Ошибка валидации: {e}") raise HTTPException(status_code=404, detail=str(e)) except Exception as e: logger.error(f"Ошибка отправки алерта AlertManager: {e}") raise HTTPException(status_code=500, detail=f"Ошибка отправки сообщения: {str(e)}")