Расширение Syslog + Zabbix на несколько подсетей через LLD
1. Назначение доработки
Первоначальная схема Syslog + Zabbix была настроена для одной подсети:
10.44.100.0/24
Для этой подсети в Zabbix уже вручную созданы элементы данных, триггеры и теги, которые читают alert-файлы из каталога:
/var/log/remote/_alerts/10.44.100.0/
Эта схема остаётся рабочей и не удаляется. Данная инструкция описывает развитие существующей схемы: добавление автоматического обнаружения дополнительных подсетей через Zabbix LLD — Low-Level Discovery / низкоуровневое обнаружение.
Новый LLD-механизм добавляется отдельно и не должен создавать дубли для уже настроенной вручную подсети.
2. Что уже умеет rsyslog
В текущей схеме rsyslog уже формирует alert-файлы по подсети источника. Используется структура:
/var/log/remote/_alerts/<ПОДСЕТЬ_ИСТОЧНИКА>/high.log
/var/log/remote/_alerts/<ПОДСЕТЬ_ИСТОЧНИКА>/alerts_critical.log
/var/log/remote/_alerts/<ПОДСЕТЬ_ИСТОЧНИКА>/alerts_warning.log
/var/log/remote/_alerts/<ПОДСЕТЬ_ИСТОЧНИКА>/alerts_behavior.log
Каталог подсети рассчитывается по правилу /24. Например:
10.44.100.37 -> /var/log/remote/_alerts/10.44.100.0/
10.44.200.25 -> /var/log/remote/_alerts/10.44.200.0/
10.46.100.30 -> /var/log/remote/_alerts/10.46.100.0/
То есть основная логика rsyslog уже готова к нескольким IPv4-подсетям с маской /24. Доработка требуется на стороне Zabbix, чтобы он не был жёстко привязан только к каталогу 10.44.100.0.
3. Принятая архитектура
В этой доработке используется следующая логика:
10.44.100.0/24
остаётся на старых ручных элементах данных и триггерах;
через LLD не создаётся, чтобы не было дублей.
10.44.200.0/24
обнаруживается автоматически через LLD;
получает discovered items и discovered triggers.
10.46.100.0/24
обнаруживается автоматически через LLD;
получает discovered items и discovered triggers.
LLD будет сканировать реальные каталоги внутри:
/var/log/remote/_alerts/
Если внутри _alerts появился каталог:
10.44.200.0
Python-скрипт сформирует JSON со значениями LLD-макросов:
{#SYSLOG_NET} = 10.44.200.0
{#SYSLOG_NET_CIDR} = 10.44.200.0/24
Zabbix использует эти макросы в прототипах элементов данных и триггеров.
4. Почему используем Zabbix agent active
Правило обнаружения создаётся с типом:
Zabbix agent active / Zabbix агент активный
Это означает, что Zabbix Server не будет пассивно опрашивать агент по этому ключу. Zabbix Agent 2 на Syslog-сервере сам получает список active checks, локально запускает Python-скрипт по расписанию и отправляет результат на Zabbix Server.
Для LLD задаём интервал:
1h
То есть примерно один раз в час агент будет запускать Python-скрипт, получать JSON со списком сетевых каталогов и отправлять его в Zabbix Server.
5. Что будет при одинаковом JSON
Если каждый час Python-скрипт возвращает один и тот же JSON, Zabbix не создаёт дубликаты.
новая сеть появилась в JSON
-> Zabbix создаёт новые discovered items и discovered triggers;
та же сеть снова пришла в JSON
-> Zabbix подтверждает, что объект существует, но дубли не создаёт;
сеть перестала приходить в JSON
-> Zabbix считает её потерянным ресурсом и применяет настройки lost resources.
6. Создание файла исключений для старой ручной подсети
Чтобы LLD не создал дубли для уже настроенной вручную подсети 10.44.100.0/24, создаём файл исключений:
sudo nano /etc/zabbix/syslog_alert_networks_lld.exclude
Содержимое файла:
# Сети, которые пока НЕ должны попадать в LLD.
# Они уже обслуживаются ручными элементами данных и ручными триггерами.
10.44.100.0
Назначаем владельца и права:
sudo chown root:zabbix /etc/zabbix/syslog_alert_networks_lld.exclude
sudo chmod 640 /etc/zabbix/syslog_alert_networks_lld.exclude
Здесь владельцем должен быть root, а не syslog, потому что этот файл не относится к записи логов. Он используется только для управления логикой обнаружения Zabbix.
| Параметр | Расшифровка |
|---|---|
root:zabbix |
root — владелец файла; zabbix — группа, которая может читать файл. |
640 |
6 для root = чтение + запись; 4 для zabbix = только чтение; 0 для остальных = доступа нет. |
7. Создание Python-скрипта для формирования JSON LLD
Создаём файл:
sudo nano /usr/local/bin/zabbix_syslog_alert_networks_lld.py
Содержимое файла:
#!/usr/bin/env python3
# Импортируем модуль json.
# Он нужен, чтобы сформировать корректный JSON для Zabbix LLD.
import json
# Импортируем модуль os.
# Он нужен для работы с файлами, каталогами и путями.
import os
# Импортируем модуль re.
# Он нужен для проверки имени каталога через регулярное выражение.
import re
# Импортируем модуль sys.
# Он нужен для корректного завершения скрипта с кодом выхода.
import sys
# Каталог, внутри которого rsyslog создаёт сетевые alert-каталоги.
# Например:
# /var/log/remote/_alerts/10.44.100.0/
# /var/log/remote/_alerts/10.44.200.0/
ALERTS_DIR = "/var/log/remote/_alerts"
# Файл со списком сетей, которые нужно исключить из LLD.
# Например, 10.44.100.0 уже обслуживается ручными элементами данных и триггерами.
EXCLUDE_FILE = "/etc/zabbix/syslog_alert_networks_lld.exclude"
# Регулярное выражение для проверки имени каталога сети /24.
# Разрешаются имена вида:
# 10.44.100.0
# 10.44.200.0
# 10.46.100.0
#
# Не разрешаются:
# test
# 10.44.100.25
# 10.44.100.0-old
NETWORK_RE = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.0$")
def normalize_network_name(value: str) -> str:
"""
Приводит имя сети к единому виду.
На вход можно передать:
10.44.100.0
или:
10.44.100.0/24
На выходе всегда будет:
10.44.100.0
"""
# Убираем пробелы и символы перевода строки в начале и конце строки.
value = value.strip()
# Если сеть записана с суффиксом /24, убираем этот суффикс.
if value.endswith("/24"):
value = value[:-3]
# Возвращаем нормализованное имя сети.
return value
def read_excluded_networks() -> set[str]:
"""
Читает файл исключений и возвращает множество сетей,
которые не должны попадать в LLD.
"""
# Создаём пустое множество исключённых сетей.
excluded = set()
# Если файл исключений отсутствует, это не ошибка.
# Просто считаем, что исключений нет.
if not os.path.isfile(EXCLUDE_FILE):
return excluded
# Открываем файл исключений в режиме чтения.
with open(EXCLUDE_FILE, "r", encoding="utf-8") as file:
# Читаем файл построчно.
for line in file:
# Убираем пробелы и перевод строки по краям.
line = line.strip()
# Пустые строки пропускаем.
if not line:
continue
# Строки-комментарии, начинающиеся с #, тоже пропускаем.
if line.startswith("#"):
continue
# Нормализуем имя сети и добавляем его в множество исключений.
excluded.add(normalize_network_name(line))
# Возвращаем найденные исключения.
return excluded
def is_valid_ipv4_24_network_name(name: str) -> bool:
"""
Проверяет, что имя каталога похоже на IPv4-сеть /24.
Примеры допустимых имён:
10.44.100.0
192.168.1.0
Примеры недопустимых имён:
test
10.44.100.25
10.44.999.0
"""
# Сначала проверяем общий формат через регулярное выражение.
if not NETWORK_RE.match(name):
return False
# Разбиваем строку IP-адреса на октеты.
# Например, "10.44.200.0" превращается в список:
# ["10", "44", "200", "0"]
octets = name.split(".")
# Проверяем каждый октет отдельно.
for octet in octets:
# Пробуем преобразовать октет в число.
try:
value = int(octet)
# Если преобразовать не удалось, значит это не IPv4-октет.
except ValueError:
return False
# Каждый IPv4-октет должен быть в диапазоне от 0 до 255.
if value < 0 or value > 255:
return False
# Если все проверки прошли успешно, имя считается корректным.
return True
def main() -> int:
"""
Основная функция скрипта.
Она:
1. читает файл исключений;
2. сканирует каталог /var/log/remote/_alerts;
3. выбирает каталоги вида X.X.X.0;
4. исключает сети из exclude-файла;
5. возвращает JSON для Zabbix LLD.
"""
# Список обнаруженных сетей.
discovered = []
# Читаем список сетей, которые нужно исключить из LLD.
excluded_networks = read_excluded_networks()
# Если каталога _alerts нет, возвращаем пустой LLD-результат.
# Это не ошибка: возможно, alert-каталоги ещё не были созданы.
if not os.path.isdir(ALERTS_DIR):
print(json.dumps([], ensure_ascii=False))
return 0
# Получаем список объектов внутри /var/log/remote/_alerts
# и обрабатываем их в отсортированном порядке.
for entry in sorted(os.listdir(ALERTS_DIR)):
# Собираем полный путь к найденному объекту.
full_path = os.path.join(ALERTS_DIR, entry)
# Если объект не является каталогом, пропускаем его.
if not os.path.isdir(full_path):
continue
# Если имя каталога не похоже на сеть /24, пропускаем его.
if not is_valid_ipv4_24_network_name(entry):
continue
# Если сеть указана в файле исключений, пропускаем её.
if entry in excluded_networks:
continue
# Добавляем сеть в результат LLD.
# Эти значения станут LLD-макросами Zabbix.
discovered.append({
"{#SYSLOG_NET}": entry,
"{#SYSLOG_NET_CIDR}": f"{entry}/24",
"{#SYSLOG_ALERT_DIR}": full_path
})
# Печатаем JSON в stdout.
# Именно этот вывод получит Zabbix Agent 2.
print(json.dumps(discovered, ensure_ascii=False))
# Возвращаем код успешного завершения.
return 0
# Эта проверка означает:
# запускать main() только если файл запущен как самостоятельный скрипт.
if __name__ == "__main__":
sys.exit(main())
Назначаем права на выполнение:
sudo chmod 755 /usr/local/bin/zabbix_syslog_alert_networks_lld.py
Проверяем запуск:
/usr/local/bin/zabbix_syslog_alert_networks_lld.py
Пример результата:
[
{
"{#SYSLOG_NET}": "10.44.200.0",
"{#SYSLOG_NET_CIDR}": "10.44.200.0/24",
"{#SYSLOG_ALERT_DIR}": "/var/log/remote/_alerts/10.44.200.0"
},
{
"{#SYSLOG_NET}": "10.46.100.0",
"{#SYSLOG_NET_CIDR}": "10.46.100.0/24",
"{#SYSLOG_ALERT_DIR}": "/var/log/remote/_alerts/10.46.100.0"
}
]
Примечание. В современных версиях Zabbix LLD принимает JSON-массив в корне документа. Старый вариант с объектом {"data": [...]} также поддерживается для совместимости, но в этой инструкции используется массив в корне.
8. Проверка скрипта от имени пользователя zabbix
Так как скрипт будет запускаться через Zabbix Agent 2, важно проверить его именно от пользователя zabbix:
sudo -u zabbix /usr/local/bin/zabbix_syslog_alert_networks_lld.py
Проверяем доступ к файлу исключений:
sudo -u zabbix cat /etc/zabbix/syslog_alert_networks_lld.exclude
Проверяем доступ к каталогу _alerts:
sudo -u zabbix ls -la /var/log/remote/_alerts/
Если есть ошибка доступа к логам, проверяем членство пользователя zabbix в группе adm:
id zabbix
Если пользователя zabbix нет в группе adm, добавляем:
sudo usermod -aG adm zabbix
sudo systemctl restart zabbix-agent2
9. Подробная расшифровка Python-скрипта
9.1. Шебанг
#!/usr/bin/env python3
Это шебанг. Шебанг — специальная первая строка скрипта в Linux/Unix. Она начинается с символов #! и говорит операционной системе, каким интерпретатором запускать файл.
В нашем случае /usr/bin/env python3 означает: найти python3 в системном окружении и выполнить этот файл через Python 3.
9.2. Импорты
import json
Подключает стандартный модуль json. Он нужен для формирования JSON-ответа, который будет передан в Zabbix.
import os
Подключает модуль os. Он нужен для работы с файловой системой: os.path.isdir(), os.path.isfile(), os.listdir(), os.path.join().
import re
Подключает модуль регулярных выражений. Он нужен для проверки, что имя каталога похоже на сеть /24.
import sys
Подключает модуль sys. Он используется в конце скрипта, чтобы корректно завершить программу и вернуть код выполнения.
9.3. Константы
ALERTS_DIR = "/var/log/remote/_alerts"
Путь к каталогу, который будет сканировать скрипт.
EXCLUDE_FILE = "/etc/zabbix/syslog_alert_networks_lld.exclude"
Путь к файлу исключений.
NETWORK_RE = re.compile(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.0$")
Регулярное выражение, которое пропускает имена вида 10.44.200.0 и не пропускает 10.44.200.15, test, 10.44.200.0-old.
9.4. Функция normalize_network_name
def normalize_network_name(value: str) -> str:
Создаётся функция с именем normalize_network_name. Она принимает строку и возвращает строку.
value = value.strip()
Метод .strip() убирает пробелы, табуляции и переводы строк по краям строки.
if value.endswith("/24"):
Проверяет, заканчивается ли строка на /24.
value = value[:-3]
Отрезает последние три символа. Например, 10.44.100.0/24 становится 10.44.100.0.
return value
Возвращает нормализованное имя сети.
9.5. Функция read_excluded_networks
def read_excluded_networks() -> set[str]:
Создаётся функция, которая возвращает множество строк. Множество удобно тем, что в нём быстро проверять наличие элемента.
excluded = set()
Создаётся пустое множество.
if not os.path.isfile(EXCLUDE_FILE):
return excluded
Если файла исключений нет, возвращается пустое множество. Скрипт не падает из-за отсутствия файла исключений.
with open(EXCLUDE_FILE, "r", encoding="utf-8") as file:
Открывается файл в режиме чтения. Конструкция with удобна тем, что Python сам закроет файл после выхода из блока.
for line in file:
Чтение файла построчно.
line = line.strip()
Убираются пробелы и перевод строки по краям.
if not line:
continue
Если строка пустая, она пропускается.
if line.startswith("#"):
continue
Если строка начинается с #, она считается комментарием и пропускается.
excluded.add(normalize_network_name(line))
Нормализованное имя сети добавляется в множество исключений.
return excluded
Возвращается множество исключённых сетей.
9.6. Функция is_valid_ipv4_24_network_name
def is_valid_ipv4_24_network_name(name: str) -> bool:
Функция принимает строку и возвращает True или False.
if not NETWORK_RE.match(name):
return False
Если имя каталога не соответствует шаблону X.X.X.0, функция возвращает False.
octets = name.split(".")
Строка разбивается по точкам. Например, 10.44.200.0 превращается в список ["10", "44", "200", "0"].
for octet in octets:
Начинается цикл по каждому октету.
try:
value = int(octet)
except ValueError:
return False
Скрипт пробует преобразовать октет в число. Если это невозможно, возвращается False.
if value < 0 or value > 255:
return False
Каждый IPv4-октет должен быть в диапазоне от 0 до 255.
return True
Если все проверки прошли успешно, функция возвращает True.
9.7. Функция main
def main() -> int:
Главная функция программы.
discovered = []
Создаётся пустой список обнаруженных сетей.
excluded_networks = read_excluded_networks()
Читается файл исключений.
if not os.path.isdir(ALERTS_DIR):
Проверяется, существует ли каталог /var/log/remote/_alerts.
print(json.dumps([], ensure_ascii=False))
return 0
Если каталога нет, скрипт возвращает пустой JSON-массив [] и завершается успешно.
for entry in sorted(os.listdir(ALERTS_DIR)):
Получается список объектов внутри _alerts, затем он сортируется.
full_path = os.path.join(ALERTS_DIR, entry)
Собирается полный путь к найденному объекту.
if not os.path.isdir(full_path):
continue
Если объект не каталог, он пропускается.
if not is_valid_ipv4_24_network_name(entry):
continue
Если имя каталога не похоже на сеть /24, оно пропускается.
if entry in excluded_networks:
continue
Если сеть указана в файле исключений, она пропускается.
discovered.append({
"{#SYSLOG_NET}": entry,
"{#SYSLOG_NET_CIDR}": f"{entry}/24",
"{#SYSLOG_ALERT_DIR}": full_path
})
Найденная сеть добавляется в результат LLD. На каждую сеть формируются три макроса: {#SYSLOG_NET}, {#SYSLOG_NET_CIDR}, {#SYSLOG_ALERT_DIR}.
print(json.dumps(discovered, ensure_ascii=False))
Формируется JSON и печатается в stdout.
return 0
Возвращается код успешного завершения.
if __name__ == "__main__":
sys.exit(main())
Если файл запущен как самостоятельный скрипт, выполняется функция main().
10. Добавление UserParameter для Zabbix Agent 2
Создаём конфигурационный файл:
sudo nano /etc/zabbix/zabbix_agent2.d/syslog_alert_networks.conf
Содержимое:
UserParameter=syslog.alert.networks.discovery,/usr/local/bin/zabbix_syslog_alert_networks_lld.py
Расшифровка:
UserParameter=<ключ>,<команда>
В нашем случае syslog.alert.networks.discovery — это ключ, который будет использоваться в Zabbix Discovery rule. Команда /usr/local/bin/zabbix_syslog_alert_networks_lld.py будет выполнена Zabbix Agent 2.
Перезапускаем агент:
sudo systemctl restart zabbix-agent2
sudo systemctl status zabbix-agent2 --no-pager
Проверка через zabbix_get, если он установлен и passive-проверки с localhost разрешены:
zabbix_get -s 127.0.0.1 -k syslog.alert.networks.discovery
11. Служебный скрипт подготовки alert-файлов
Этот скрипт не является обязательной частью LLD, но он полезен.
Проблема может быть такой: от новой сети пришло только событие MACFLAP, и rsyslog создал файл:
/var/log/remote/_alerts/10.44.200.0/alerts_behavior.log
Но файлов:
alerts_critical.log
alerts_warning.log
high.log
может ещё не быть. Если Zabbix уже создаст элементы данных по всем типам alert-файлов, часть элементов может временно стать Unsupported / Не поддерживается.
Чтобы этого избежать, создаём служебный скрипт, который будет создавать стандартные пустые alert-файлы во всех сетевых каталогах.
Создаём файл:
sudo nano /usr/local/sbin/prepare_syslog_alert_files.sh
Содержимое:
#!/usr/bin/env bash
# Каталог, где лежат сетевые alert-папки.
# Например:
# /var/log/remote/_alerts/10.44.100.0/
# /var/log/remote/_alerts/10.44.200.0/
ALERTS_DIR="/var/log/remote/_alerts"
# Если каталога _alerts нет, спокойно завершаем работу.
# Это не ошибка: возможно, alert-каталоги ещё не созданы.
if [ ! -d "$ALERTS_DIR" ]; then
exit 0
fi
# Ищем только каталоги первого уровня внутри _alerts.
# -mindepth 1 — не брать сам каталог _alerts;
# -maxdepth 1 — не уходить глубже сетевых каталогов;
# -type d — брать только каталоги.
find "$ALERTS_DIR" -mindepth 1 -maxdepth 1 -type d \
| while read -r dir; do
# Получаем имя каталога без полного пути.
# Например:
# /var/log/remote/_alerts/10.44.200.0
# превратится в:
# 10.44.200.0
net_name="$(basename "$dir")"
# Проверяем, что имя каталога похоже на сеть /24.
# Всё, что не похоже на X.X.X.0, пропускаем.
if [[ ! "$net_name" =~ ^([0-9]{1,3}\.){3}0$ ]]; then
continue
fi
# Создаём стандартные alert-файлы, если их ещё нет.
# Если файл уже существует, touch не удаляет и не портит его содержимое.
touch "$dir/high.log"
touch "$dir/alerts_critical.log"
touch "$dir/alerts_warning.log"
touch "$dir/alerts_behavior.log"
# Назначаем владельца и группу.
# syslog — владелец, потому что rsyslog пишет в эти файлы.
# adm — группа для чтения логов, в неё должен входить пользователь zabbix.
chown syslog:adm "$dir/high.log" \
"$dir/alerts_critical.log" \
"$dir/alerts_warning.log" \
"$dir/alerts_behavior.log"
# Назначаем права:
# 6 для владельца syslog = чтение + запись;
# 4 для группы adm = чтение;
# 0 для остальных = нет доступа.
chmod 640 "$dir/high.log" \
"$dir/alerts_critical.log" \
"$dir/alerts_warning.log" \
"$dir/alerts_behavior.log"
done
Назначаем права:
sudo chmod 755 /usr/local/sbin/prepare_syslog_alert_files.sh
Проверяем:
sudo /usr/local/sbin/prepare_syslog_alert_files.sh
12. Расшифровка bash-скрипта
#!/usr/bin/env bash
Шебанг для Bash-скрипта. Он говорит системе запускать файл через Bash.
ALERTS_DIR="/var/log/remote/_alerts"
Создаётся переменная ALERTS_DIR, в которой хранится путь к каталогу с сетевыми alert-папками.
if [ ! -d "$ALERTS_DIR" ]; then
Проверяется, существует ли каталог _alerts. Параметр -d проверяет, существует ли каталог, а ! означает отрицание.
exit 0
Скрипт завершается успешно. Код 0 означает успешное завершение.
find "$ALERTS_DIR" -mindepth 1 -maxdepth 1 -type d \
Команда find ищет только каталоги первого уровня внутри _alerts. Параметр -mindepth 1 не берёт сам каталог _alerts, -maxdepth 1 не уходит глубже первого уровня, -type d берёт только каталоги.
| while read -r dir; do
Символ | — это pipe / конвейер. Он передаёт результат команды find в цикл while. Команда read -r dir читает очередную строку и кладёт её в переменную dir.
net_name="$(basename "$dir")"
Команда basename берёт только имя последнего элемента пути. Например, /var/log/remote/_alerts/10.44.200.0 становится 10.44.200.0.
if [[ ! "$net_name" =~ ^([0-9]{1,3}\.){3}0$ ]]; then
Проверяется, похоже ли имя каталога на сеть /24. Оператор =~ сравнивает строку с регулярным выражением.
continue
Если имя каталога не подходит, цикл переходит к следующему каталогу.
touch "$dir/high.log"
Создаёт файл high.log, если его нет. Если файл уже существует, содержимое не удаляется.
chown syslog:adm ...
Меняет владельца и группу файла: syslog — владелец, adm — группа.
chmod 640 ...
Назначает права: 6 — владелец читает и пишет; 4 — группа читает; 0 — остальные не имеют доступа.
13. Добавление служебного скрипта в cron
Создаём cron-файл:
sudo nano /etc/cron.d/prepare-syslog-alert-files
Содержимое:
*/5 * * * * root /usr/local/sbin/prepare_syslog_alert_files.sh >/dev/null 2>&1
| Фрагмент | Расшифровка |
|---|---|
*/5 * * * * |
Запускать каждые 5 минут. |
root |
Запускать от имени root. |
/usr/local/sbin/prepare_syslog_alert_files.sh |
Какой скрипт запускать. |
>/dev/null 2>&1 |
Не выводить результат в консоль и не засорять почту cron. |
14. Создание Discovery rule в Zabbix
Переходим в шаблон:
Data collection / Сбор данных
-> Templates / Шаблоны
-> Template App Syslog Alerts
-> Discovery rules / Правила обнаружения
-> Create discovery rule / Создать правило обнаружения
Параметры:
Name / Имя:
Syslog alert networks discovery
Type / Тип:
Zabbix agent active / Zabbix агент активный
Key / Ключ:
syslog.alert.networks.discovery
Type of information / Тип информации:
Text / Текст
Update interval / Интервал обновления:
1h
Важно. Для lost resources на первом этапе лучше не ставить мгновенное удаление. Рекомендуемый безопасный вариант: Delete lost resources / Удалять потерянные ресурсы: 30d.
15. Что именно будет делать Discovery rule
Каждый час Zabbix Agent 2 будет локально запускать Python-скрипт:
/usr/local/bin/zabbix_syslog_alert_networks_lld.py
Скрипт будет проверять каталог:
/var/log/remote/_alerts/
Если там есть каталоги:
10.44.100.0
10.44.200.0
10.46.100.0
и в exclude-файле указано:
10.44.100.0
то в Zabbix будет отправлен JSON только по новым сетям:
[
{
"{#SYSLOG_NET}": "10.44.200.0",
"{#SYSLOG_NET_CIDR}": "10.44.200.0/24",
"{#SYSLOG_ALERT_DIR}": "/var/log/remote/_alerts/10.44.200.0"
},
{
"{#SYSLOG_NET}": "10.46.100.0",
"{#SYSLOG_NET_CIDR}": "10.46.100.0/24",
"{#SYSLOG_ALERT_DIR}": "/var/log/remote/_alerts/10.46.100.0"
}
]
После получения JSON Zabbix создаёт реальные элементы данных и триггеры на основании item prototypes и trigger prototypes.
16. Прототипы элементов данных
16.1. Общий принцип
Старый ручной элемент данных для одной сети был привязан к пути:
/var/log/remote/_alerts/10.44.100.0/^alerts_critical\.log$
В прототипе нужно использовать LLD-макрос:
/var/log/remote/_alerts/{#SYSLOG_NET}/^alerts_critical\.log$
То есть 10.44.100.0 заменяется на {#SYSLOG_NET}, а в имени и тегах можно использовать {#SYSLOG_NET_CIDR}.
16.2. Пример item prototype: ERR_DISABLE count
Name / Имя:
Syslog PM ERR_DISABLE count: {#SYSLOG_NET_CIDR}
Type / Тип:
Zabbix agent active / Zabbix агент активный
Key / Ключ:
logrt.count["/var/log/remote/_alerts/{#SYSLOG_NET}/^alerts_critical\.log$","PM-4-ERR_DISABLE","UTF-8",{$SYSLOG_MAXLINES},skip]
Type of information / Тип информации:
Numeric (unsigned) / Числовой без знака
Теги:
component = syslog
subnet = {#SYSLOG_NET_CIDR}
file = alerts_critical.log
event_group = port_state
event = err_disable
severity_logic = immediate
item_role = counter
source = lld
16.3. Пример item prototype: ERR_DISABLE last line
Name / Имя:
Syslog PM ERR_DISABLE last line: {#SYSLOG_NET_CIDR}
Type / Тип:
Zabbix agent active / Zabbix агент активный
Key / Ключ:
logrt["/var/log/remote/_alerts/{#SYSLOG_NET}/^alerts_critical\.log$","PM-4-ERR_DISABLE","UTF-8",{$SYSLOG_MAXLINES},skip]
Type of information / Тип информации:
Log / Журнал
Теги:
component = syslog
subnet = {#SYSLOG_NET_CIDR}
file = alerts_critical.log
event_group = port_state
event = err_disable
severity_logic = immediate
item_role = last_line
source = lld
16.4. Принцип создания остальных item prototypes
Все остальные элементы данных создаются по той же логике.
Было:
/var/log/remote/_alerts/10.44.100.0/
Стало:
/var/log/remote/_alerts/{#SYSLOG_NET}/
Было в имени:
10.44.100.0/24
Стало:
{#SYSLOG_NET_CIDR}
Было в теге:
subnet = 10.44.100.0/24
Стало:
subnet = {#SYSLOG_NET_CIDR}
Также добавляем тег:
source = lld
17. Прототипы триггеров
17.1. Общий принцип
Старый ручной триггер для одной сети был привязан к конкретному пути:
/var/log/remote/_alerts/10.44.100.0/
В trigger prototype используется:
/var/log/remote/_alerts/{#SYSLOG_NET}/
В имени триггера используется {#SYSLOG_NET_CIDR}.
17.2. Пример trigger prototype: ERR_DISABLE
Name / Имя:
Syslog critical: port err-disabled: {#SYSLOG_NET_CIDR}
Severity / Важность:
High / Высокая
Expression / Выражение:
sum(/Template App Syslog Alerts/logrt.count["/var/log/remote/_alerts/{#SYSLOG_NET}/^alerts_critical\.log$","PM-4-ERR_DISABLE","UTF-8",{$SYSLOG_MAXLINES},skip],{$SYSLOG_IMMEDIATE_WIN})>={$SYSLOG_CRIT_COUNT}
and
length(last(/Template App Syslog Alerts/logrt["/var/log/remote/_alerts/{#SYSLOG_NET}/^alerts_critical\.log$","PM-4-ERR_DISABLE","UTF-8",{$SYSLOG_MAXLINES},skip]))>0
Настройки:
OK event generation / Генерация события восстановления:
None / Не создавать
Allow manual close / Разрешить ручное закрытие:
Yes / Да
Problem event generation mode / Режим генерации проблем:
Single / Одиночный
Теги:
component = syslog
subnet = {#SYSLOG_NET_CIDR}
file = alerts_critical.log
event_group = port_state
event = err_disable
severity_logic = immediate
reaction = incident
source = lld
17.3. Operational data / Операционные данные
Можно использовать такой вариант:
Счётчик: {ITEM.LASTVALUE1} | Syslog: {ITEM.LASTVALUE2}
Для триггеров, где используется несколько элементов данных, важно проверить порядок {ITEM.LASTVALUE1}, {ITEM.LASTVALUE2}, {ITEM.LASTVALUE3} в конкретном выражении.
18. Теги для ручной и LLD-схемы
Чтобы в проблемах было понятно, откуда пришёл триггер, рекомендуется добавить тег source.
Для старых ручных элементов и триггеров:
source = manual
Для новых LLD-прототипов:
source = lld
Для старой сети:
subnet = 10.44.100.0/24
source = manual
Для новых сетей:
subnet = {#SYSLOG_NET_CIDR}
source = lld
19. Проверка работы
19.1. Проверка каталогов
sudo ls -la /var/log/remote/_alerts/
Пример:
10.44.100.0
10.44.200.0
10.46.100.0
19.2. Проверка exclude-файла
cat /etc/zabbix/syslog_alert_networks_lld.exclude
Должно быть:
10.44.100.0
19.3. Проверка Python-скрипта
/usr/local/bin/zabbix_syslog_alert_networks_lld.py
Проверка от пользователя zabbix:
sudo -u zabbix /usr/local/bin/zabbix_syslog_alert_networks_lld.py
Если всё правильно, в JSON не должно быть сети:
10.44.100.0
19.4. Проверка UserParameter
zabbix_get -s 127.0.0.1 -k syslog.alert.networks.discovery
Если zabbix_get не работает из-за настроек passive-доступа, достаточно проверки запуска скрипта от пользователя zabbix.
19.5. Проверка в Zabbix
Data collection / Сбор данных
-> Hosts / Узлы сети
-> <Syslog host>
-> Latest data / Последние данные
Ищем элементы, созданные через LLD.
Также проверяем:
Data collection / Сбор данных
-> Templates / Шаблоны
-> Template App Syslog Alerts
-> Discovery rules / Правила обнаружения
-> Syslog alert networks discovery
После выполнения discovery должны появиться discovered items и discovered triggers для новых сетей.
20. Важные ограничения
Данная схема рассчитана на IPv4 и подсети /24.
Она корректно обрабатывает каталоги вида:
10.44.200.0
10.46.100.0
192.168.1.0
Но не предназначена для автоматического расчёта других масок, например:
/23
/22
/25
Если в будущем потребуется поддержка других масок, нужно будет менять логику формирования сетевого каталога в rsyslog и логику проверки имён в Python-скрипте.
21. Итоговая логика после внедрения
1. Cisco отправляет syslog на Syslog-сервер.
2. rsyslog принимает сообщения и пишет alert-события в:
/var/log/remote/_alerts/<сеть_24>/
3. Старая сеть 10.44.100.0/24 продолжает обслуживаться ручными элементами данных и триггерами.
4. Zabbix Agent 2 один раз в час запускает локальный Python-скрипт.
5. Python-скрипт сканирует /var/log/remote/_alerts/.
6. Python-скрипт исключает 10.44.100.0 из LLD.
7. Python-скрипт формирует JSON со списком новых сетей.
8. Zabbix Agent 2 отправляет JSON на Zabbix Server.
9. Zabbix Server создаёт discovered items и discovered triggers для новых сетей.
10. Если JSON не изменился, дубли не создаются.
Итог: текущая рабочая схема для 10.44.100.0/24 сохраняется, а новые подсети подключаются через LLD без ручного копирования большого количества элементов данных и триггеров.