diff --git a/README.md b/README.md index fb1170b..c52c413 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,18 @@ Zabbix агент по запросу сервера запускает скри и зависимые элементы с препроцессингом JSONPath. Настройка: -1. Импортировать шаблон `zabbix/zbx_ses_template_5.0_up.xml` в Zabbix +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. В `/etc/zabbix/zabbix_agentd.conf` добавить строку: +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 ``` -5. Перезапустить Zabbix агента: `sudo systemctl restart zabbix-agent` +6. Перезапустить Zabbix агента: `sudo systemctl restart zabbix-agent` ## Структура проекта @@ -86,9 +90,11 @@ Zabbix агент по запросу сервера запускает скри │ ├── example_liveness.yaml # Пример: быстрая проверка │ └── example_dialog.yaml # Пример: многошаговый диалог ├── zabbix/ -│ ├── zbx_ses_template_5.0.xml # Шаблон для sender (trapper) -│ ├── zbx_ses_template_5.0_up.xml # Шаблон для UserParameter -│ └── zbx_ses_monitor.conf # UserParameter для 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/ @@ -181,8 +187,15 @@ 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. Выбрать файл `zabbix/zbx_ses_template_5.0.xml` (для sender) или `zabbix/zbx_ses_template_5.0_up.xml` (для UserParameter) +2. Выбрать файл шаблона 3. Нажать Import 4. Привязать шаблон "SES Monitor" к нужному хосту diff --git a/src/ses_monitor/main.py b/src/ses_monitor/main.py index 7800be3..f5f1534 100644 --- a/src/ses_monitor/main.py +++ b/src/ses_monitor/main.py @@ -93,6 +93,7 @@ class SESMonitorApp: self._config.ses.base_url, zbx_cfg.metrics_file, ) + self._zabbix.write_agentd_config() else: log.info( "SES Monitor v%s запускается (SES: %s, Zabbix: %s:%d, хост: %s)", @@ -240,46 +241,46 @@ class SESMonitorApp: Zabbix-метрик (ses.scenario.ok, .total, .failed, .avg_time, .max_time, .error). Это делает шаблон Zabbix универсальным — не нужно добавлять отдельный item под каждый сценарий. - """ - if not self._scenario_runner: - return + Всегда пишет дефолтные значения метрик, даже если сценарии + отключены или не настроены — иначе Zabbix-препроцессинг JSONPath + упадёт на отсутствующих ключах. + """ due_results: List[ScenarioResult] = [] - for scenario in self._scenarios: - name = scenario.name or "unknown" - last_run = self._last_scenario_run.get(name, 0) + if self._scenario_runner: + for scenario in self._scenarios: + name = scenario.name or "unknown" + last_run = self._last_scenario_run.get(name, 0) - if now - last_run >= scenario.interval: - try: - result = await self._scenario_runner.run_one( - self._client, scenario - ) - due_results.append(result) - self._last_scenario_run[name] = now - - if not result.passed: - log.warning( - "Сценарий '%s': ПРОВАЛЕН за %dms (ошибок: %d шагов)", - name, - round(result.total_time_ms), - len(result.failed_steps), + if now - last_run >= scenario.interval: + try: + result = await self._scenario_runner.run_one( + self._client, scenario ) - except Exception as e: - log.exception( - "Сценарий '%s': неожиданное исключение: %s", name, e - ) + due_results.append(result) + self._last_scenario_run[name] = now - # Агрегируем результаты и отправляем фиксированные ключи - if due_results and self._zabbix.enabled: + if not result.passed: + log.warning( + "Сценарий '%s': ПРОВАЛЕН за %dms (ошибок: %d шагов)", + name, + round(result.total_time_ms), + len(result.failed_steps), + ) + except Exception as e: + log.exception( + "Сценарий '%s': неожиданное исключение: %s", name, e + ) + + if self._zabbix.enabled: agg = aggregate_scenario_results(due_results) self._zabbix.add("ses.scenario.ok", agg["ok"]) self._zabbix.add("ses.scenario.total", agg["total"]) self._zabbix.add("ses.scenario.failed", agg["failed"]) self._zabbix.add("ses.scenario.avg_time", agg["avg_time"]) self._zabbix.add("ses.scenario.max_time", agg["max_time"]) - if agg["error"]: - self._zabbix.add("ses.scenario.error", agg["error"]) + self._zabbix.add("ses.scenario.error", agg["error"]) async def _run_log_check(self) -> None: """Выполняет проверку логов (если включено).""" diff --git a/src/ses_monitor/zabbix.py b/src/ses_monitor/zabbix.py index c9323f8..75c9a51 100644 --- a/src/ses_monitor/zabbix.py +++ b/src/ses_monitor/zabbix.py @@ -220,6 +220,43 @@ class UserParameterWriter: """ return ( "# Автоматически сгенерировано ses-monitor.\n" - "# Требуется: Include=/opt/ses-monitor/config/zbx_agentd_ses.conf\n" + "# Добавьте в /etc/zabbix/zabbix_agentd.conf:\n" + f"# Include={self._config.agentd_config_dir}/zbx_agentd_ses.conf\n" + "# Или скопируйте этот файл в /etc/zabbix/zabbix_agentd.d/\n" f"UserParameter=ses.metrics,/opt/ses-monitor/scripts/zbx_get.py\n" ) + + def write_agentd_config(self) -> bool: + """Записывает конфигурационный файл UserParameter на диск. + + Создаёт или обновляет zbx_agentd_ses.conf в agentd_config_dir. + Использует атомарную запись (temp + rename). + + Returns: + True если запись успешна, False при ошибке. + """ + config_dir = Path(self._config.agentd_config_dir) + config_path = config_dir / "zbx_agentd_ses.conf" + content = self.generate_agentd_config() + try: + config_dir.mkdir(parents=True, exist_ok=True) + fd, tmp_path = tempfile.mkstemp( + dir=str(config_dir), + prefix=".zbx_agentd_", + suffix=".tmp", + ) + try: + os.write(fd, content.encode("utf-8")) + os.fsync(fd) + os.close(fd) + os.replace(tmp_path, str(config_path)) + os.chmod(str(config_path), 0o644) + except Exception: + if os.path.exists(tmp_path): + os.unlink(tmp_path) + raise + log.info("Конфиг Zabbix агента записан: %s", config_path) + return True + except Exception as e: + log.error("Ошибка записи конфига Zabbix агента: %s", e) + return False diff --git a/zabbix/zbx_ses_monitor.conf b/zabbix/zbx_ses_monitor.conf index 260a8b9..3f65427 100644 --- a/zabbix/zbx_ses_monitor.conf +++ b/zabbix/zbx_ses_monitor.conf @@ -1,10 +1,5 @@ -# ============================================================================= -# Резервный вариант: UserParameter для Zabbix Agent (пассивный режим). -# ============================================================================= -# Используется если zabbix_sender недоступен. -# Поместить в /etc/zabbix/zabbix_agentd.d/ses_monitor.conf -# и перезапустить zabbix-agent: systemctl restart zabbix-agent -# -# Скрипт ses_monitor_zabbix_agent.py должен быть установлен в -# /opt/ses-monitor/scripts/ses_monitor_zabbix_agent.py -# ============================================================================= +# Автоматически сгенерировано ses-monitor. +# Добавьте в /etc/zabbix/zabbix_agentd.conf: +# Include=/opt/ses-monitor/config/zbx_agentd_ses.conf +# Или скопируйте этот файл в /etc/zabbix/zabbix_agentd.d/ +UserParameter=ses.metrics,/opt/ses-monitor/scripts/zbx_get.py diff --git a/zabbix/zbx_ses_template_up.xml b/zabbix/zbx_ses_template_up.xml new file mode 100644 index 0000000..e9c7594 --- /dev/null +++ b/zabbix/zbx_ses_template_up.xml @@ -0,0 +1,331 @@ + + + 6.4 + + + 88166f35-c76b-44e2-b8ad-333b3b4b9692 + Templates + + + + + +