ses-monitor/README.md
Maksim Totmin c066ae687a feat: separate ses-test CLI install via subdirectory
Add cli/pyproject.toml so analysts can install only ses-test
without the monitoring daemon or Zabbix integration.

pip install "ses-test @ git+https://...#subdirectory=cli"
→ only ses-test binary, no ses-monitor in PATH

Root pyproject.toml unchanged — daemon install still provides both.

docs: rename section to 'Установка ses-test'
2026-06-24 10:50:47 +07:00

430 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SES Monitor
Система непрерывного мониторинга диалоговой сценарной машины **SES** (Service Engine Server).
## Что делает
Watchdog-демон, который:
- **Проверяет доступность** SES API каждые 30 секунд
- **Имитирует диалоги пользователей** по сценариям, описанным в YAML-файлах
- **Отправляет метрики в Zabbix** через zabbix_sender или UserParameter
SES — универсальная сценарная машина, поэтому мониторинг не привязан к конкретному проекту. Один и тот же демон может обслуживать любой сценарий: голосового ассистента, чат-бота, IVR, диалогов поддержки и любых других.
## Архитектура
```
┌─────────────────────────────────────────────────────┐
│ ses-monitor (watchdog-демон) │
│ │
│ ┌──────────────┐ ┌────────────────┐ │
│ │ LivenessCheck │ │ ScenarioRunner │ │
│ │ (30 сек) │ │ (по YAML) │ │
│ │ │ │ │ │
│ │ GET /hostname│ │ POST /ses/ask │ │
│ │ GET /license │ │ шаги+expect │ │
│ └──────┬───────┘ └───────┬─────────┘ │
│ │ │ │
│ ┌──────▼──────────────────▼──────┐ │
│ │ Zabbix (sender / UP) │ │
│ └──────────────┬─────────────────┘ │
└─────────────────┼────────────────────────────────────┘
│ TCP 10051 / JSON file
┌───────────▼───────────┐
│ Zabbix Server │
│ (триггеры, алерты) │
└───────────────────────┘
```
## Режимы доставки в Zabbix
Монитор поддерживает два режима отправки метрик:
### sender (по умолчанию) — zabbix_sender (активный push)
Монитор запускает утилиту `zabbix_sender` и передаёт метрики через stdin.
Данные отправляются на Zabbix сервер или прокси (порт 10051, trapper).
В Zabbix элементы данных имеют тип "Zabbix траппер".
Требуется: установленный zabbix_sender на сервере с монитором.
### userparameter — Zabbix агент (пассивный опрос)
Монитор пишет JSON со всеми метриками в файл (`/var/tmp/ses_monitor/metrics.json`).
Zabbix агент по запросу сервера запускает скрипт `scripts/zbx_get.py`,
который возвращает содержимое JSON-файла.
В Zabbix используется master-элемент `ses.metrics` (тип "Zabbix агент", текст)
и зависимые элементы с препроцессингом JSONPath.
Настройка:
1. Импортировать шаблон в Zabbix:
- Zabbix 6.4: `zabbix/zbx_ses_template_up.xml`
- Zabbix 5.0: `zabbix/zbx_ses_template_5.0_up.xml`
2. Привязать шаблон к хосту (Configuration → Hosts → выберите хост → Templates)
3. В конфиге монитора `config/config.yaml` установить `mode: "userparameter"`
4. При старте демон автоматически создаёт конфиг агента:
`/opt/ses-monitor/config/zbx_agentd_ses.conf`
5. В `/etc/zabbix/zabbix_agentd.conf` добавить строку (или скопировать конфиг в `/etc/zabbix/zabbix_agentd.d/`):
```
Include=/opt/ses-monitor/config/zbx_agentd_ses.conf
```
6. Перезапустить Zabbix агента: `sudo systemctl restart zabbix-agent`
## Структура проекта
```
/opt/ses-monitor/
├── src/
│ └── ses_monitor/ # Python-пакет (src-layout)
│ ├── __init__.py # Версия, метаданные
│ ├── __main__.py # Точка входа: python -m ses_monitor
│ ├── main.py # Главный asyncio watchdog-цикл
│ ├── config.py # Загрузка YAML-конфигурации
│ ├── checker.py # Liveness-проверки (API, лицензия, роботы)
│ ├── session_client.py # HTTP-клиент для SES API
│ ├── scenario_runner.py # Движок выполнения сценариев
│ ├── log_client.py # Чтение логов SES (задел на будущее)
│ ├── zabbix.py # Интеграция с Zabbix (sender / UserParameter)
│ └── utils.py # Утилиты (логирование, JSON-пути, проверки)
├── config/
│ ├── config.yaml.example # Шаблон конфигурации (скопировать в config.yaml)
│ └── scenarios/
│ ├── example_liveness.yaml # Пример: быстрая проверка
│ └── example_dialog.yaml # Пример: многошаговый диалог
├── zabbix/
│ ├── zbx_ses_template.xml # Шаблон Zabbix 6.4 для sender (trapper)
│ ├── zbx_ses_template_up.xml # Шаблон Zabbix 6.4 для UserParameter
│ ├── zbx_ses_template_5.0.xml # Шаблон Zabbix 5.0 для sender (trapper)
│ ├── zbx_ses_template_5.0_up.xml # Шаблон Zabbix 5.0 для UserParameter
│ └── zbx_ses_monitor.conf # Резервный конфиг UserParameter
├── scripts/
│ └── zbx_get.py # Скрипт чтения JSON для UserParameter
├── systemd/
│ └── ses-monitor.service # Systemd unit
├── tests/
│ ├── __init__.py
│ └── test_scenario_runner.py # 48 тестов
├── pyproject.toml # Зависимости и метаданные пакета
└── README.md
```
## Установка на сервере
### 1. Требования
- **Python 3.9+**
- **Git** — для клонирования репозитория
- **Zabbix Agent** с утилитой `zabbix_sender`
- Доступ к SES API (`http://<ses-host>:6190`)
- Доступ к Zabbix Server по порту 10051
### 2. Установка пакета
```bash
# Клонируем репозиторий на сервер
git clone https://git.totmin.ru/en2zmax/ses-monitor.git /opt/ses-monitor
# Переходим в каталог проекта
cd /opt/ses-monitor
# Создаём виртуальное окружение и устанавливаем пакет
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
# Создаём каталог для логов
mkdir -p /var/log/ses-monitor
```
### 3. Настройка конфигурации
Скопируйте шаблон и отредактируйте:
```bash
cp config/config.yaml.example config/config.yaml
nano config/config.yaml
```
Основные поля:
```yaml
ses:
base_url: "http://YOUR_SES_HOST:6190" # URL вашего SES API
channel: "monitoring" # Канал для мониторинговых сессий
request_timeout: 30
zabbix:
enabled: true
mode: "userparameter" # или "sender"
host: "my-server-name" # Имя хоста в Zabbix
server: "127.0.0.1" # IP Zabbix сервера (только sender)
port: 10051
sender_path: "/usr/bin/zabbix_sender"
metrics_file: "/var/tmp/ses_monitor/metrics.json" # (только userparameter)
```
### 4. Создание сценария
Скопируйте пример и адаптируйте под ваш проект:
```bash
cp config/scenarios/example_liveness.yaml config/scenarios/my_project.yaml
```
### 5. Запуск
```bash
# Проверка вручную:
cd /opt/ses-monitor
.venv/bin/python -m ses_monitor
# Установка как systemd-сервиса:
cp systemd/ses-monitor.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now ses-monitor
# Просмотр логов:
journalctl -u ses-monitor -f
```
### 6. Импорт шаблона Zabbix
В зависимости от версии Zabbix и режима доставки:
| Zabbix | sender (trapper) | userparameter |
|---|---|---|
| 6.4 | `zabbix/zbx_ses_template.xml` | `zabbix/zbx_ses_template_up.xml` |
| 5.0 | `zabbix/zbx_ses_template_5.0.xml` | `zabbix/zbx_ses_template_5.0_up.xml` |
1. Zabbix Frontend → **Configuration → Templates → Import**
2. Выбрать файл шаблона
3. Нажать Import
4. Привязать шаблон "SES Monitor" к нужному хосту
## Установка ses-test
Если нужна только утилита `ses-test` для отладки YAML-сценариев (без демона и Zabbix):
```bash
pip install "ses-test @ git+https://git.totmin.ru/en2zmax/ses-monitor.git#subdirectory=cli"
```
После установки доступна одна команда — `ses-test`:
```bash
ses-test --help
ses-test my_scenario.yaml --ses-url http://ses-host:6190
ses-test my_scenario.yaml --ses-url http://ses-host:6190 --dry-run
```
Подробнее — см. раздел [«Как адаптировать под ваш проект»](#как-адаптировать-под-ваш-проект).
## Формат сценариев
Сценарии описываются в YAML-файлах в каталоге `config/scenarios/`. Это гибкий DSL, который не зависит от конкретного проекта SES.
### Полный формат
```yaml
# Имя сценария (используется в логах)
name: "example: проверка диалога"
# Описание для документации
description: "Проверяет что бот корректно отвечает на вопросы"
# Включён ли сценарий (false — отключён)
enabled: true
# Интервал запуска в секундах
interval: 300
# Таймаут HTTP-запроса в секундах
timeout: 30
# ID робота (обязательно). Узнать: GET /ses/robot/listall
robot_id: "uuid-вашего-робота"
# Канал (если не указан — берётся из config.yaml)
channel: "monitoring"
# Не переоткрывать закрытые сессии (false — создаём новую сессию каждый раз)
restart_closed: false
# Шаги сценария
steps:
- send: "Привет"
expect:
# Каждое ожидание проверяет значение в JSON-ответе
- path: "error"
equals: 0
- path: "answer[0].messages[0]"
contains: "Здрав"
- send: "Как дела?"
expect:
- path: "error"
equals: 0
- path: "answer"
not_empty: true
```
### Условия проверки (expect)
| Условие | Описание | Пример |
|---|---|---|
| `equals` | Точное совпадение | `equals: 0` |
| `contains` | Подстрока содержится (без учёта регистра) | `contains: "здравствуйте"` |
| `not_contains` | Подстрока отсутствует | `not_contains: "ошибка"` |
| `not_empty` | Поле не пустое | `not_empty: true` |
| `regex` | Регулярное выражение | `regex: "\\d{10,}"` |
| `exists` | Поле существует и не None | `exists` (без значения) |
### Пути JSON (path)
Формат: `ключ.подключ[индекс].поле`
```yaml
# Корневое поле
path: "error"
# Массив → первый элемент → поле
path: "answer[0].messages[0]"
# Глубокая вложенность
path: "request.data[0].reply.confidence"
```
## Как адаптировать под ваш проект
1. **Узнайте robot_id** вашего сценария:
```bash
curl -s http://<ses-host>:6190/ses/robot/listall
```
2. **Пропишите robot_id** в файле сценария (поле `robot_id` в YAML)
3. **Создайте файл сценария**, описав шаги диалога:
```bash
cp config/scenarios/example_dialog.yaml config/scenarios/my_project.yaml
vim config/scenarios/my_project.yaml
```
4. **Протестируйте сценарий** утилитой `ses-test`:
```bash
# Базовый прогон
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190
# С детальным выводом JSON-ответов (режим отладки)
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 -v
# Только валидация YAML без запросов к SES
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 --dry-run
# С переопределением robot_id (если не указан в YAML)
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 --robot-id abc-123
# С фиксированной сессией для повторных прогонов
ses-test config/scenarios/my_project.yaml --ses-url http://ses-host:6190 --session debug-1
```
**Примеры вывода:**
Успешный прогон (без `-v`):
```
=== Сценарий: 'test: МФЦ диалог' ===
SES: http://ses-host:6190
Robot ID: abc-123-def-456
Канал: monitoring
Шагов: 2
Сессия: monitoring_test_1782271424
── Шаг 1/2 ────────────────────────────────────────
→ Привет
← Время ответа: 176ms
[PASS] path=error equals=0
[PASS] path=answer[0].messages[0] not_empty
── Шаг 2/2 ────────────────────────────────────────
→ Записаться на приём
← Время ответа: 382ms
[PASS] path=error equals=0
[PASS] path=answer[0].messages[0] not_empty
──────────────────────────────────────────────────
РЕЗУЛЬТАТ: УСПЕШНО (2/2 шагов)
```
Проваленный шаг:
```
── Шаг 1/1 ────────────────────────────────────────
→ Привет
← Время ответа: 177ms
[PASS] path=error equals=0
[FAIL] Поле 'answer[0].messages[0]' = 'пожалуйста выберите...'
не содержит 'несуществующая_фраза_xyz_999'
──────────────────────────────────────────────────
РЕЗУЛЬТАТ: ПРОВАЛЕН (шаг 1)
```
Режим отладки (`-v`):
```
── Шаг 1/2 ────────────────────────────────────────
→ Привет
← Время ответа: 176ms
← RAW JSON:
{
"error": 0,
"question": "Привет",
"answer": [{
"messages": [
"Пожалуйста выберите действие или задайте вопрос.\n\n{button:...}\n"
],
"voice": "Коля",
"interruptible": false
}],
"session": "monitoring_test_1782271424",
"uuid": "132e530c-..."
}
[PASS] path=error equals=0
[PASS] path=answer[0].messages[0] not_empty
```
**Exit code:** `0` — все шаги пройдены, `1` — есть ошибки, `2` — ошибка аргументов.
5. **Запустите демон** — метрики агрегируются автоматически (шаблон Zabbix универсален)
## Расширение системы
### Добавление нового сценария
1. Создать `config/scenarios/my_scenario.yaml`
2. Скопировать структуру из примеров
3. Указать `robot_id` вашего робота (узнать: `GET /ses/robot/listall`)
4. Описать шаги и ожидания
5. Сценарий автоматически подхватится через hot-reload (каждые 60 секунд)
### Добавление новой метрики
1. В нужном модуле добавить поле в датакласс-результат
2. В `main.py` вызвать `self._zabbix.add(key, value)`
3. Добавить item в Zabbix шаблон
## Мониторинг самого монитора
- **systemd**: `systemctl is-active ses-monitor`
- **Zabbix Active Agent**: `proc.num[python,root,ses_monitor]`
- **Логи**: `journalctl -u ses-monitor --since "10 min ago" -o cat | grep ERROR`
## Отказоустойчивость
- **Автоперезапуск**: systemd Restart=always, RestartSec=10
- **Graceful shutdown**: по SIGTERM отправляет последний батч метрик
- **Таймауты**: все HTTP-запросы с ограничением по времени (по умолчанию 30с)
- **Изоляция сценариев**: ошибка в одном сценарии не влияет на другие
- **Hot-reload**: сценарии перечитываются без перезапуска демона
## Лицензия
MIT