Initial commit: Message Gateway project
- FastAPI приложение для отправки мониторинговых алертов в мессенджеры - Поддержка Telegram и MAX/VK - Интеграция с Grafana, Zabbix, AlertManager - Автоматическое создание тикетов в Jira - Управление группами мессенджеров через API - Декораторы для авторизации и скрытия эндпоинтов - Подробная документация в папке docs/ Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
16
app/models/__init__.py
Normal file
16
app/models/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Модели данных для приложения.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
from app.models.alertmanager import PrometheusAlert
|
||||
from app.models.grafana import GrafanaAlert, EvalMatch
|
||||
from app.models.zabbix import ZabbixAlert
|
||||
|
||||
__all__ = [
|
||||
"PrometheusAlert",
|
||||
"GrafanaAlert",
|
||||
"EvalMatch",
|
||||
"ZabbixAlert",
|
||||
]
|
||||
69
app/models/alertmanager.py
Normal file
69
app/models/alertmanager.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
Модели данных для AlertManager webhooks.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
from typing import Dict, Any, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class PrometheusAlert(BaseModel):
|
||||
"""Модель данных вебхука из AlertManager."""
|
||||
status: str = Field(..., description="Статус алерта (firing, resolved)", examples=["firing"])
|
||||
externalURL: str = Field(..., description="Внешний URL AlertManager", examples=["http://alertmanager.example.com"])
|
||||
commonLabels: Dict[str, str] = Field(..., description="Общие метки алерта")
|
||||
commonAnnotations: Dict[str, str] = Field(..., description="Общие аннотации алерта")
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"status": "firing",
|
||||
"externalURL": "http://alertmanager.example.com",
|
||||
"commonLabels": {
|
||||
"alertname": "PodCrashLooping",
|
||||
"severity": "warning",
|
||||
"namespace": "staging",
|
||||
"pod": "test-app-5f6g7h8i9-jkl456"
|
||||
},
|
||||
"commonAnnotations": {
|
||||
"summary": "Pod is crash looping",
|
||||
"description": "Pod test-app-5f6g7h8i9-jkl456 has restarted 5 times in the last 10 minutes"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
80
app/models/grafana.py
Normal file
80
app/models/grafana.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
Модели данных для Grafana webhooks.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
from typing import List, Optional, Dict, Any
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class EvalMatch(BaseModel):
|
||||
"""Модель для evalMatches из Grafana."""
|
||||
value: float = Field(..., description="Значение метрики", examples=[95.5, 87.2, 45.2])
|
||||
metric: str = Field(..., description="Название метрики", examples=["cpu_usage_percent", "memory_usage_percent", "disk_usage_percent"])
|
||||
tags: Optional[Dict[str, Any]] = Field(None, description="Теги метрики", examples=[{"host": "server01", "instance": "production"}, None])
|
||||
|
||||
|
||||
class GrafanaAlert(BaseModel):
|
||||
"""Модель данных вебхука из Grafana."""
|
||||
title: str = Field(..., description="Заголовок алерта", examples=["[Alerting] Test notification"])
|
||||
ruleId: int = Field(..., description="ID правила алерта", examples=[674180201771804383])
|
||||
ruleName: str = Field(..., description="Название правила", examples=["Test notification"])
|
||||
state: str = Field(..., description="Состояние алерта (alerting, ok, paused, pending, no_data)", examples=["alerting"])
|
||||
evalMatches: List[EvalMatch] = Field(default_factory=list, description="Совпадения метрик")
|
||||
orgId: int = Field(..., description="ID организации", examples=[0])
|
||||
dashboardId: int = Field(..., description="ID дашборда", examples=[1])
|
||||
panelId: int = Field(..., description="ID панели", examples=[1])
|
||||
tags: Dict[str, str] = Field(default_factory=dict, description="Теги алерта")
|
||||
ruleUrl: str = Field(..., description="URL правила алерта", examples=["http://grafana.cism-ms.ru/"])
|
||||
message: Optional[str] = Field(None, description="Сообщение алерта", examples=["Someone is testing the alert notification within Grafana."])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"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"}
|
||||
},
|
||||
{
|
||||
"value": 87.2,
|
||||
"metric": "memory_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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
106
app/models/group.py
Normal file
106
app/models/group.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Модели данных для управления группами мессенджеров.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
from typing import Optional, Dict, Any, Union
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class GroupInfo(BaseModel):
|
||||
"""Информация о группе."""
|
||||
name: str = Field(..., description="Имя группы", examples=["monitoring", "alerts", "devops"])
|
||||
messenger: Optional[str] = Field(None, description="Тип мессенджера (telegram, max)", examples=["telegram", "max"])
|
||||
chat_id: Optional[Union[int, str]] = Field(None, description="Chat ID группы (отображается только при наличии пароля)", examples=[-1001234567890, "123456789", None])
|
||||
thread_id: Optional[int] = Field(None, description="ID треда в группе (опционально, только для Telegram)", examples=[0, 123, 456])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"name": "monitoring",
|
||||
"messenger": "telegram",
|
||||
"chat_id": -1001234567890,
|
||||
"thread_id": 0
|
||||
},
|
||||
{
|
||||
"name": "alerts_max",
|
||||
"messenger": "max",
|
||||
"chat_id": "123456789",
|
||||
"thread_id": None
|
||||
},
|
||||
{
|
||||
"name": "devops",
|
||||
"messenger": None,
|
||||
"chat_id": None,
|
||||
"thread_id": None
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CreateGroupRequest(BaseModel):
|
||||
"""Запрос на создание группы."""
|
||||
group_name: str = Field(..., description="Имя группы", examples=["monitoring", "alerts", "devops"])
|
||||
chat_id: Union[int, str] = Field(..., description="ID чата (может быть int для Telegram или str для MAX/VK)", examples=[-1001234567890, "123456789"])
|
||||
messenger: str = Field("telegram", description="Тип мессенджера (telegram, max)", examples=["telegram", "max"])
|
||||
thread_id: int = Field(0, description="ID треда в группе (по умолчанию 0, только для Telegram)", examples=[0, 123, 456])
|
||||
config: Optional[Dict[str, Any]] = Field(None, description="Дополнительная конфигурация для мессенджера (опционально)", examples=[None, {"access_token": "..."}, {"api_version": "5.131"}])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"group_name": "monitoring",
|
||||
"chat_id": -1001234567890,
|
||||
"messenger": "telegram",
|
||||
"thread_id": 0,
|
||||
},
|
||||
{
|
||||
"group_name": "alerts_max",
|
||||
"chat_id": "123456789",
|
||||
"messenger": "max",
|
||||
"thread_id": 0,
|
||||
"config": {
|
||||
"access_token": "your_access_token",
|
||||
"api_version": "5.131"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class UpdateGroupRequest(BaseModel):
|
||||
"""Запрос на обновление группы."""
|
||||
chat_id: Optional[Union[int, str]] = Field(None, description="Новый Chat ID группы (можно получить через @userinfobot для Telegram)", examples=[-1001234567891, "123456789"])
|
||||
messenger: Optional[str] = Field(None, description="Новый тип мессенджера (telegram, max)", examples=["telegram", "max"])
|
||||
thread_id: Optional[int] = Field(None, description="Новый ID треда в группе (опционально, только для Telegram)", examples=[0, 123, 456])
|
||||
config: Optional[Dict[str, Any]] = Field(None, description="Новая дополнительная конфигурация для мессенджера (опционально)", examples=[None, {"access_token": "..."}, {"api_version": "5.131"}])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"chat_id": -1001234567891,
|
||||
"messenger": "telegram",
|
||||
"thread_id": 0,
|
||||
},
|
||||
{
|
||||
"chat_id": "123456789",
|
||||
"messenger": "max",
|
||||
"config": {
|
||||
"access_token": "your_access_token",
|
||||
"api_version": "5.131"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DeleteGroupRequest(BaseModel):
|
||||
"""Запрос на удаление группы."""
|
||||
pass
|
||||
73
app/models/jira.py
Normal file
73
app/models/jira.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Модели данных для Jira тикетов.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
from typing import Optional, Dict, Any, List
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class JiraMappingCondition(BaseModel):
|
||||
"""Условия для маппинга алерта в Jira тикет."""
|
||||
severity: Optional[str] = None
|
||||
namespace: Optional[str] = None
|
||||
state: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
tags: Optional[Dict[str, str]] = None
|
||||
event_severity: Optional[str] = Field(None, alias="event-severity")
|
||||
|
||||
|
||||
class JiraMapping(BaseModel):
|
||||
"""Маппинг алерта в Jira тикет."""
|
||||
conditions: JiraMappingCondition
|
||||
project: str = Field(..., description="Ключ проекта Jira")
|
||||
assignee: Optional[str] = Field(None, description="Email исполнителя")
|
||||
issue_type: str = Field("Bug", description="Тип задачи")
|
||||
priority: str = Field("High", description="Приоритет задачи")
|
||||
labels: Optional[List[str]] = Field(None, description="Метки задачи")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
class JiraSourceMapping(BaseModel):
|
||||
"""Маппинг для источника алертов."""
|
||||
default_project: str = Field(..., description="Проект по умолчанию")
|
||||
default_assignee: Optional[str] = Field(None, description="Исполнитель по умолчанию")
|
||||
default_issue_type: str = Field("Bug", description="Тип задачи по умолчанию")
|
||||
default_priority: str = Field("High", description="Приоритет по умолчанию")
|
||||
mappings: List[JiraMapping] = Field(default_factory=list, description="Список маппингов")
|
||||
|
||||
|
||||
class JiraMappingConfig(BaseModel):
|
||||
"""Конфигурация маппинга алертов в Jira тикеты."""
|
||||
alertmanager: Optional[JiraSourceMapping] = None
|
||||
grafana: Optional[JiraSourceMapping] = None
|
||||
zabbix: Optional[JiraSourceMapping] = None
|
||||
|
||||
|
||||
class JiraIssue(BaseModel):
|
||||
"""Модель Jira тикета."""
|
||||
project: str = Field(..., description="Ключ проекта")
|
||||
summary: str = Field(..., description="Заголовок тикета")
|
||||
description: str = Field(..., description="Описание тикета")
|
||||
issue_type: str = Field("Bug", description="Тип задачи")
|
||||
assignee: Optional[str] = Field(None, description="Email исполнителя")
|
||||
priority: Optional[str] = Field(None, description="Приоритет задачи")
|
||||
labels: Optional[List[str]] = Field(None, description="Метки задачи")
|
||||
components: Optional[List[str]] = Field(None, description="Компоненты проекта")
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [{
|
||||
"project": "MON",
|
||||
"summary": "[Critical] High CPU Usage - Production",
|
||||
"description": "**Alert:** High CPU Usage\n\n**Severity:** Critical\n\n**Namespace:** production",
|
||||
"issue_type": "Bug",
|
||||
"assignee": "devops-team@example.com",
|
||||
"priority": "Highest",
|
||||
"labels": ["critical", "production", "alertmanager"]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
195
app/models/message.py
Normal file
195
app/models/message.py
Normal file
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
Модели данных для отправки простых сообщений в Telegram.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class SendMessageRequest(BaseModel):
|
||||
"""Запрос на отправку текстового сообщения."""
|
||||
tg_group: str = Field(..., description="Имя группы Telegram из конфигурации", examples=["monitoring", "alerts", "devops"])
|
||||
tg_thread_id: int = Field(0, description="ID треда в группе Telegram (0 для основной группы)", examples=[0, 123, 456])
|
||||
text: str = Field(..., description="Текст сообщения", examples=["Привет! Это тестовое сообщение.", "<b>Важное уведомление</b>\n\nСистема работает нормально."])
|
||||
parse_mode: Optional[str] = Field("HTML", description="Режим парсинга (HTML, Markdown, MarkdownV2)", examples=["HTML", "Markdown", "MarkdownV2"])
|
||||
disable_web_page_preview: bool = Field(True, description="Отключить превью ссылок", examples=[True, False])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"tg_group": "monitoring",
|
||||
"tg_thread_id": 0,
|
||||
"text": "Привет! Это тестовое сообщение.",
|
||||
"parse_mode": "HTML",
|
||||
"disable_web_page_preview": True
|
||||
},
|
||||
{
|
||||
"tg_group": "alerts",
|
||||
"tg_thread_id": 123,
|
||||
"text": "<b>Критическое уведомление</b>\n\nСистема недоступна!\n\n<i>Время: 2024-02-08 16:49:44</i>",
|
||||
"parse_mode": "HTML",
|
||||
"disable_web_page_preview": False
|
||||
},
|
||||
{
|
||||
"tg_group": "monitoring",
|
||||
"tg_thread_id": 0,
|
||||
"text": "**Важное уведомление**\n\nСистема работает нормально.\n\n[Подробнее](https://example.com)",
|
||||
"parse_mode": "Markdown",
|
||||
"disable_web_page_preview": True
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SendPhotoRequest(BaseModel):
|
||||
"""Запрос на отправку фото."""
|
||||
tg_group: str = Field(..., description="Имя группы Telegram из конфигурации", examples=["monitoring", "alerts"])
|
||||
tg_thread_id: int = Field(0, description="ID треда в группе Telegram (0 для основной группы)", examples=[0, 123])
|
||||
photo: str = Field(..., description="URL фото или путь к файлу (поддерживается автоматическая загрузка с URL)", examples=["https://example.com/image.jpg", "https://grafana.example.com/render/dashboard-solo?panelId=1&width=1000&height=500"])
|
||||
caption: Optional[str] = Field(None, description="Подпись к фото", examples=["График производительности", "<b>График CPU</b>\n\nВремя: 2024-02-08 16:49:44"])
|
||||
parse_mode: Optional[str] = Field("HTML", description="Режим парсинга подписи (HTML, Markdown, MarkdownV2)", examples=["HTML", "Markdown"])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"tg_group": "monitoring",
|
||||
"tg_thread_id": 0,
|
||||
"photo": "https://example.com/image.jpg",
|
||||
"caption": "Описание фото",
|
||||
"parse_mode": "HTML"
|
||||
},
|
||||
{
|
||||
"tg_group": "alerts",
|
||||
"tg_thread_id": 123,
|
||||
"photo": "https://grafana.example.com/render/dashboard-solo?panelId=1&width=1000&height=500",
|
||||
"caption": "<b>График CPU</b>\n\n<i>Время: 2024-02-08 16:49:44</i>",
|
||||
"parse_mode": "HTML"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SendVideoRequest(BaseModel):
|
||||
"""Запрос на отправку видео."""
|
||||
tg_group: str = Field(..., description="Имя группы Telegram из конфигурации", examples=["monitoring", "alerts"])
|
||||
tg_thread_id: int = Field(0, description="ID треда в группе Telegram (0 для основной группы)", examples=[0, 123])
|
||||
video: str = Field(..., description="URL видео или путь к файлу (поддерживается автоматическая загрузка с URL)", examples=["https://example.com/video.mp4", "https://example.com/recording.webm"])
|
||||
caption: Optional[str] = Field(None, description="Подпись к видео", examples=["Запись экрана", "<b>Запись работы системы</b>\n\nДлительность: 60 сек"])
|
||||
parse_mode: Optional[str] = Field("HTML", description="Режим парсинга подписи (HTML, Markdown, MarkdownV2)", examples=["HTML", "Markdown"])
|
||||
duration: Optional[int] = Field(None, description="Длительность видео в секундах", examples=[60, 120, 300])
|
||||
width: Optional[int] = Field(None, description="Ширина видео в пикселях", examples=[1280, 1920])
|
||||
height: Optional[int] = Field(None, description="Высота видео в пикселях", examples=[720, 1080])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"tg_group": "monitoring",
|
||||
"tg_thread_id": 0,
|
||||
"video": "https://example.com/video.mp4",
|
||||
"caption": "Описание видео",
|
||||
"parse_mode": "HTML",
|
||||
"duration": 60,
|
||||
"width": 1280,
|
||||
"height": 720
|
||||
},
|
||||
{
|
||||
"tg_group": "alerts",
|
||||
"tg_thread_id": 123,
|
||||
"video": "https://example.com/recording.webm",
|
||||
"caption": "<b>Запись работы системы</b>\n\n<i>Длительность: 60 сек</i>",
|
||||
"parse_mode": "HTML",
|
||||
"duration": 60,
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SendAudioRequest(BaseModel):
|
||||
"""Запрос на отправку аудио."""
|
||||
tg_group: str = Field(..., description="Имя группы Telegram из конфигурации", examples=["monitoring", "alerts"])
|
||||
tg_thread_id: int = Field(0, description="ID треда в группе Telegram (0 для основной группы)", examples=[0, 123])
|
||||
audio: str = Field(..., description="URL аудио файла или путь к файлу (поддерживается автоматическая загрузка с URL)", examples=["https://example.com/audio.mp3", "https://example.com/notification.ogg"])
|
||||
caption: Optional[str] = Field(None, description="Подпись к аудио", examples=["Аудио уведомление", "<b>Аудио запись</b>\n\nДлительность: 3 мин"])
|
||||
parse_mode: Optional[str] = Field("HTML", description="Режим парсинга подписи (HTML, Markdown, MarkdownV2)", examples=["HTML", "Markdown"])
|
||||
duration: Optional[int] = Field(None, description="Длительность аудио в секундах", examples=[180, 300])
|
||||
performer: Optional[str] = Field(None, description="Исполнитель (для музыкальных файлов)", examples=["Artist Name", "System Notification"])
|
||||
title: Optional[str] = Field(None, description="Название трека (для музыкальных файлов)", examples=["Song Title", "Alert Notification"])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"tg_group": "monitoring",
|
||||
"tg_thread_id": 0,
|
||||
"audio": "https://example.com/audio.mp3",
|
||||
"caption": "Описание аудио",
|
||||
"parse_mode": "HTML",
|
||||
"duration": 180,
|
||||
"performer": "Artist Name",
|
||||
"title": "Song Title"
|
||||
},
|
||||
{
|
||||
"tg_group": "alerts",
|
||||
"tg_thread_id": 123,
|
||||
"audio": "https://example.com/notification.ogg",
|
||||
"caption": "<b>Аудио уведомление</b>\n\n<i>Система работает нормально</i>",
|
||||
"parse_mode": "HTML",
|
||||
"duration": 30,
|
||||
"performer": "System Notification",
|
||||
"title": "Alert Notification"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SendDocumentRequest(BaseModel):
|
||||
"""Запрос на отправку документа."""
|
||||
tg_group: str = Field(..., description="Имя группы Telegram из конфигурации", examples=["monitoring", "alerts"])
|
||||
tg_thread_id: int = Field(0, description="ID треда в группе Telegram (0 для основной группы)", examples=[0, 123])
|
||||
document: str = Field(..., description="URL документа или путь к файлу (поддерживается автоматическая загрузка с URL)", examples=["https://example.com/file.pdf", "https://example.com/report.xlsx"])
|
||||
caption: Optional[str] = Field(None, description="Подпись к документу", examples=["Отчет за неделю", "<b>Отчет</b>\n\nДата: 2024-02-08"])
|
||||
parse_mode: Optional[str] = Field("HTML", description="Режим парсинга подписи (HTML, Markdown, MarkdownV2)", examples=["HTML", "Markdown"])
|
||||
filename: Optional[str] = Field(None, description="Имя файла (если не указано, используется имя из URL)", examples=["document.pdf", "report_2024-02-08.xlsx"])
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"examples": [
|
||||
{
|
||||
"tg_group": "monitoring",
|
||||
"tg_thread_id": 0,
|
||||
"document": "https://example.com/file.pdf",
|
||||
"caption": "Описание документа",
|
||||
"parse_mode": "HTML",
|
||||
"filename": "document.pdf"
|
||||
},
|
||||
{
|
||||
"tg_group": "alerts",
|
||||
"tg_thread_id": 123,
|
||||
"document": "https://example.com/report.xlsx",
|
||||
"caption": "<b>Отчет за неделю</b>\n\n<i>Дата: 2024-02-08</i>",
|
||||
"parse_mode": "HTML",
|
||||
"filename": "report_2024-02-08.xlsx"
|
||||
},
|
||||
{
|
||||
"tg_group": "monitoring",
|
||||
"tg_thread_id": 0,
|
||||
"document": "https://example.com/logs.txt",
|
||||
"caption": "Логи системы",
|
||||
"parse_mode": "HTML",
|
||||
"filename": "system_logs_2024-02-08.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
81
app/models/zabbix.py
Normal file
81
app/models/zabbix.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Модели данных для Zabbix webhooks.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ZabbixAlert(BaseModel):
|
||||
"""Модель данных вебхука из Zabbix."""
|
||||
link: str = Field(..., description="Ссылка на событие в Zabbix", examples=["{$ZABBIX_URL}/tr_events.php?triggerid=42667&eventid=8819711"])
|
||||
status: str = Field(..., description="Статус события (OK, PROBLEM)", examples=["OK"])
|
||||
action_id: str = Field(..., alias="action-id", description="ID действия", examples=["7"])
|
||||
alert_subject: str = Field(..., alias="alert-subject", description="Тема алерта", examples=["Resolved in 1m 0s: High CPU utilization (over 90% for 5m)"])
|
||||
alert_message: str = Field(..., alias="alert-message", description="Сообщение алерта", examples=["Problem has been resolved at 16:49:44 on 2024.02.08"])
|
||||
event_id: str = Field(..., alias="event-id", description="ID события", examples=["8819711"])
|
||||
event_name: str = Field(..., alias="event-name", description="Название события", examples=["High CPU utilization (over 90% for 5m)"])
|
||||
event_nseverity: str = Field(..., alias="event-nseverity", description="Числовой уровень серьезности", examples=["2"])
|
||||
event_opdata: Optional[str] = Field(None, alias="event-opdata", description="Операционные данные события", examples=["Current utilization: 70.9 %"])
|
||||
event_recovery_date: Optional[str] = Field(None, alias="event-recovery-date", description="Дата восстановления", examples=["2024.02.08"])
|
||||
event_recovery_time: Optional[str] = Field(None, alias="event-recovery-time", description="Время восстановления", examples=["16:49:44"])
|
||||
event_duration: Optional[str] = Field(None, alias="event-duration", description="Длительность события", examples=["1m 0s"])
|
||||
event_recovery_name: Optional[str] = Field(None, alias="event-recovery-name", description="Название восстановленного события", examples=["High CPU utilization (over 90% for 5m)"])
|
||||
event_recovery_status: Optional[str] = Field(None, alias="event-recovery-status", description="Статус восстановления", examples=["RESOLVED"])
|
||||
event_recovery_tags: Optional[str] = Field(None, alias="event-recovery-tags", description="Теги восстановленного события", examples=["Application:CPU"])
|
||||
event_severity: Optional[str] = Field(None, alias="event-severity", description="Уровень серьезности (Disaster, High, Warning, Average, Information)", examples=["Warning"])
|
||||
host_name: str = Field(..., alias="host-name", description="Имя хоста", examples=["pnode28"])
|
||||
host_ip: str = Field(..., alias="host-ip", description="IP адрес хоста", examples=["10.14.253.38"])
|
||||
host_port: str = Field(..., alias="host-port", description="Порт хоста", examples=["10050"])
|
||||
|
||||
model_config = {
|
||||
"populate_by_name": True,
|
||||
"json_schema_extra": {
|
||||
"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-recovery-date": None,
|
||||
"event-recovery-time": None,
|
||||
"event-duration": None,
|
||||
"event-recovery-name": None,
|
||||
"event-recovery-status": None,
|
||||
"event-recovery-tags": None,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user