Initial commit: Message Gateway project

- FastAPI приложение для отправки мониторинговых алертов в мессенджеры
- Поддержка Telegram и MAX/VK
- Интеграция с Grafana, Zabbix, AlertManager
- Автоматическое создание тикетов в Jira
- Управление группами мессенджеров через API
- Декораторы для авторизации и скрытия эндпоинтов
- Подробная документация в папке docs/

Автор: Сергей Антропов
Сайт: https://devops.org.ru
This commit is contained in:
2025-11-12 20:25:11 +03:00
commit b90def35ed
72 changed files with 10609 additions and 0 deletions

16
app/models/__init__.py Normal file
View 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",
]

View 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
View 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
View 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
View 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
View 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
View 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"
}
]
}
}