Достаточно давно у меня работает оповещение о восстановлении работоспособности Интернета, настроенное на MikroTik'e. Но недавно я таки настроил систему мониторинга LibreNMS на Raspberry Pi 4 Model B и подумал… почему бы не свалить все яйца в одну корзину.
И это… оказалось не такой-то уж и простой задачей, т.к. главное в этом деле, это достоверность – сообщать о такой проблеме нужно только, если действительно она есть и это не один из временных сбоев, которые в этих ваших Интернетах происходят чуть чаще, чем постоянно.
Поэтому нужно проверять доступность сразу нескольких независимых хостов и уведомлять только, если они все одновременно не доступны. Вот в этом месте и затаилась главная проблема – как реализовать это в LibreNMS?
Если кратко резюмировать все нижеприведенные этапы, то все сводится к главному – написать собственный запрос к базе данных, который будет учитывать мои потребности. Благо в LibreNMS это возможно без танцев с бубном! Но, по порядку.
Для начала добавляем несколько, в моем случае 4, «устройств» через меню «Devices» → «Add Device», для которых указываем только «Hostname or IP»1) и, соответственно, выключаем «SNMP».
Важно выбирать такие хосты, которые не могут одновременно быть не доступны в глобальном плане.
Сперва я остановился на NTP-пулах из-за того, что в них крайне просто выбрать разные регионы, и они, в силу своей специфики, должны быть достаточно стабильные:
Но оказалось, что для этой цели они подходят слабо, т.к. по какой-то причине крайне часто один, а зачастую и не один, кратковременно не доступен для мониторинга! Т.е. в целом схема рабочая и ложных срабатываний нет, но в логах скапливается огромное количество записей типа «down/up».
Поэтому изменил свой выбор на публичные DNS сервера:
Далее нужно зайти во все устройства, кроме одного, и включить «Disable alerting», чтобы приходило только одно уведомление, а не по количеству устройств2). Так же нужно указать для всех устройств «Display Name» и включить «Override sysLocation», добавив туда что-то уникальное – я выбрал слово «World».
Создаем динамическую группу через «Devices» → «Manage Groups» → «New Device Group», где в качестве критерия выбираем «devices.hostname», «ends with» и, в моем случае, «ntp.org» «locations.id», «equal» и, в моем случае, «10»3).
Добавляем новое правило через «Alerts» → «Alert Rules» → «Create new alert rule» и на закладке «Main»:
Касательно транспортов есть нюанс – если нет локального SMS-шлюза, и, к примеру, используется электронная почта, то можно будет получить, что логично, только сообщение о восстановлении работоспособности Интернет-соединения, чего более, чем достаточно для домашнего, ну и не только, использования.
Теперь самое интересное! – переходим на закладку «Advanced», включаем «Override SQL» и добавляем следующий запрос:
SELECT * FROM devices WHERE ( SELECT count( devices.device_id ) FROM devices WHERE devices.device_id IN ( SELECT device_group_device.device_id FROM device_group_device WHERE device_group_device.device_group_id = ( SELECT @my_group := device_group_device.device_group_id FROM device_group_device WHERE device_group_device.device_id = ? ) ) AND devices.status = 0 ) = ( SELECT count( device_group_device.device_id ) FROM device_group_device WHERE device_group_device.device_group_id = @my_group ) AND devices.device_id IN ( SELECT device_group_device.device_id FROM device_group_device WHERE device_group_device.device_group_id = @my_group )
Вот код в более читабельном виде.
SELECT * #10 FROM devices WHERE ( SELECT #5 count( devices.device_id ) FROM devices WHERE devices.device_id IN ( SELECT #4 device_group_device.device_id FROM device_group_device WHERE device_group_device.device_group_id = ( SELECT #3 @my_group := device_group_device.device_group_id #1 FROM device_group_device WHERE device_group_device.device_id = ? #2 ) ) AND devices.status = 0 #6 ) = #8 ( SELECT #7 count( device_group_device.device_id ) FROM device_group_device WHERE device_group_device.device_group_id = @my_group ) AND devices.device_id IN ( SELECT #9 device_group_device.device_id FROM device_group_device WHERE device_group_device.device_group_id = @my_group );
Обратите внимание на заполнитель «?» (#2) – он обязателен, без него не будет работать правило! И он должен быть только один, иначе в отладке будет ошибка «SQLSTATE[HY093]: Invalid parameter number»!
Еще нюанс – в переменную можно поместить только одно значение и, если в нее передается массив, то сохранится только одно последнее! А очень жалко, можно было бы еще упростить код, убрав повторную выборку устройств, принадлежащих группе (#4 и #9).
Для отладки запроса нужно открыть консоль MySQL mysql -u root -p
, если надо, узнать название базы SHOW DATABASES;
и подключиться к базе use librenms
. Просмотр существующих таблиц – SHOW TABLES;
.
Если что-то не работает, то нужно зайти в одно из устройств, нажать на троеточие справа и выбрать «Capture», затем перейти на закладку «Poller» или «Alerts», нажать «Run» и изучать…
Теперь осталось только создать шаблон, перейдя в «Alerts» → «Alert Templates» и нажав «Create new alert template». После чего, назначить его созданному ранее правилу, выбрав в «Attach template to rules».
Template
Мой шаблон.
Internet connection is {{ $alert -> state ? 'FAULTED' : 'OK' }} from {{ $alert -> timestamp }}! <br><br> @if ( $alert -> state == 0 )Approx. time elapsed: {{ $alert -> elapsed }}. <br><br> @endif @if ( $alert -> faults )Faults list: <br><br> @foreach ( $alert -> faults as $key => $value ) {{ $key }}) {{ $value[ 'display' ] }} (id {{ $value[ 'device_id' ] }}): <br> last poll at {{ $value[ 'last_polled' ] }}, <br> last ping at {{ $value[ 'last_ping' ] }} ({{ $value[ 'last_ping_timetaken' ] }}ms). <br><br> @endforeach @endif Rule: @if ( $alert -> name ){{ $alert -> name }}@else{{ $alert -> rule }}@endif () (id: {{ $alert -> rule_id }}, severity: {{ $alert -> severity }}). <br><br> Unique-ID: {{ $alert -> uid }}.
Крайне не удобная разметка! Особенно, поди угадай, чтобы между словами и знаками препинания было нужное количество пробелов!
Обратите внимание на пустые скобки «()» после «@endif» – они нужны, чтобы операторы не «съедали» то, что дальше. Так же нужно делать и с «@else»… Говорю же, не разметка, а глюк какой-то!
Alert title
{{ $alert -> name }} [{{ strtoupper( $alert -> severity ) }}]
Recovery title
{{ $alert -> name }} [OK]
Для тестирования шаблона и просмотра доступных значений и их заполнителей, нужно авторизоваться под пользователем «librenms»
sudo su - librenms
и выполнить команду
./scripts/test-template.php -t X -d -h HOST -r Y
где «X», это номер шаблона, а «Y» – номер правила. Обе цифры можно посмотреть в первой колонке соответствующего списка – списка шаблонов или списка правил. Естественно, «HOST» должен соответствовать значению, указанному в поле «Hostname or IP» соответствующего устройства.
Обсуждение