Как подружить Zabbix с Asterisk «из коробки»

В предыдущей статье «Zabbix — расширяем макро границы» я рассказал как получать сессию авторизации и подставлять ее в локальный макрос хоста. В этой статье я расскажу как подружить Zabbix с Asterisk без внешних скриптов и ПО.

Идея «подружить» эти две системы родилась давно, причем без установки дополнительного софта и скриптов. Быстрое гугление выдавало множество вариантов решений, все сводилось к тому, что закиньте скрипты (на пыхе, баше, питоне и т. п.) на сервер, и будет вам счастье. Мне же хотелось реализовать мониторинг «из коробки» — без внешних скриптов и установки дополнительного софта на сервер с мониторингом и АТС.

Провозился я с этим в сумме 4 рабочих дня, но результат стоил того. Работа через интерфейс AMI, низкоуровневое обнаружение, триггеры, а главное, на подключение АТС и все остальные настройки теперь уходит минут 15.

В наличии заббикс 4.4, около 100 штук Астерисков 13 версии. Какие-то АТС идут с веб интерфейсом FreePBX, какие-то с голой консолью, кучей хитростей и интеграцией через диалплан.

Получаем данные из АТС

Первый и основной момент, который нужно решить — получение данных о пирах и сип регистрациях. Для этого в АТС существуют интерфейсы AGI, AMI, ARI и SSH консоль. Дополнительные модули по понятным причинам не рассматривал.

Для начала надо разобраться, что из себя представляют эти аги, ами, ари….

  • AGI — использование скриптов в диалплане. В основном используется для управления вызовами.
  • AMI — умеет отдавать всю необходимую информацию, работает через порт 5038 по аналогии с Telnet. Нам подходит!
  • ARI — современно, модно, JSONно. Много возможностей, формат данных в понятном виде для Zabbix, но для меня нет главного: нельзя контролировать сип регистрацию. Еще один минус, для пиров есть только два состояния online/offline, хотя состояний больше и их учитывать полезно при диагностике.
  • SSH — может все, но иногда его не дают из-за «соображений безопасности». Соображения могут быть разными, разбирать их не буду.

Тем не менее, при всех своих недостатках ARI закрывает 90% всех потребностей по мониторингу.

Zabbix и Telnet — мое разочарование

AMI знаю хорошо, в свое время реализовывал отслеживание потерь в разговорах с делением по удаленным офисам, управление звонками и т.п. С Telnet тоже все предельно ясно: открывай подключение, шли команды и читай ответ. Что я и сделал, но результат меня разочаровал.

Telnet у заббикса не такой как в консоле Linux, он чуть более простой и заточенный под стандартную авторизацию типа логин/пароль. Если логика авторизации другая, и нет запроса пары логин/пароль, вылетает ошибка. После тщетных попыток обойти требование авторизации полез смотреть исходники модуля Telnet.

Я понял, что пока не будет традиционного запроса логина с паролем, дальше не продвинусь. Ради интереса, выкинул из кода все, что касается авторизации, пересобрал все. Работает! Но под требования не подходит. Идем дальше…

Возвращаемся к поиску

Перечитал еще раз документацию по ARI, провел дополнительные тесты — нет тут сип регистраций. Пиры есть, разговоры есть, бриджи есть, регистраций нет. В какой-то момент даже задумался, так ли нужны нам сип регистрации?

По забавному стечению обстоятельств, в этот момент прилетает очередной запрос от пользователя, с проблемой исходящих звонков. Проблема была в подвисании сип регистрации и решалась обычной перезагрузкой модуля.

asterisk -rx "sip reload"

Было бы здорово по вебу обращаться к AMI: это бы решило все проблемы, подумал я. Начинаю копать в этом направлении, и буквально первая строка поиска ведет на официальную документацию Asterisk, в которой говориться, что для моих задач есть опция webenabled в файле /etc/asterisk/manager.conf, которую нужно установить в значение YES, в секции [general]

После этого, через обычный веб запрос вида http://ats:8089/mxml?action=SIPshowregistry получаем всю необходимую информацию.

При использовании интерфейса FreePBX, через веб нельзя включить данную опцию, включать нужно через консоль, внося правки в файл manager.conf. FreePBX не стирает ее при изменениях конфигурации через веб.

Сколько работал с разного рода интеграциями Asterisk, никогда не видел, чтобы где-то упоминалась эта функция. Меня удивило, что никто не описывает этот метод взаимодействия с АТС. Даже специально полез искать информацию по данной теме: практически ничего нет или использовалось для совершенно других задач.

WEB AMI — что за зверь?

Добавление опции webenabled в файл manager.conf открывало полноценный доступ к управлению АТСкой через веб. Все команды, доступные через обычный AMI, теперь есть в вебе, можно слушать события от АТС через сокет. Принцип работы ничем не отличается от консольного AMI. После активации данной опции, к АТС можно обратиться по следующим адресам:

https://ats:8089/manager — веб страница с простым интерфейсом, для тестов и ручной отправки запросов. Все ответы форматируются в читабельный HTML вид. Для мониторинга не очень подходит.
https://ats:8089/rawman — только текстовый вывод, формат аналогичен консольному AMI
https://ats:8089/mxml — только текстовый вывод, в формате XML. Нам подходит!

Как подружить Zabbix с Asterisk «из коробки»

Тут я было подумал: «Вот оно – решение! Сейчас все будет готово! Изи-пизи лемон сквизи», но радоваться было пока рано. Для получения нужной нам информации достаточно использовать GET запрос с нужными действием action, который в ответ возвращает xml со списком всех регистраций и их состоянием. Это все здорово, но нужна авторизация с запоминанием сессии из cookie. Когда тестируешь в браузере, не задумываешься об этом процессе.

Процесс авторизации

В начале мы обращаемся на адрес http://ats:8089/mxml?action=login&username=zabbix&secret=zabbix, в ответ нам сервер присылает куку с сессией авторизации. Вот так выглядит HTTP запрос:

https://ats:8089/mxml?action=login&username=zabbix&secret=zabbix

Host: ats:8089
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
DNT: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1

Ответ:

GET: HTTP/1.1 200 OK
Server: Asterisk/13.29.2
Date: Thu, 18 Jun 2020 17:41:19 GMT
Cache-Control: no-cache, no-store
Content-type: text/xml
Set-Cookie: mansession_id="6f5de42c"; Version=1; Max-Age=600
Pragma: SuppressEvents
Content-Length: 146

<ajax-response>
<response type="object" id="unknown">
<generic response="Success" message="Authentication accepted"/>
</response>
</ajax-response>

Для работы там нужно mansession_id="6f5de42c", т.е. сама кука авторизации.
Контент нужно лишь проверить наличие ответа «Authentication accepted». Дальше, при всех обращениях к серверу АТС нам необходимо будет добавлять в запрос куку авторизации.

https://ats:8089/mxml?action=SIPpeers

Host: ats:8089
Connection: close
Cookie: mansession_id="6f5de42c"

Как получать куку авторизации и использовать в других запросах читайте тут: «Zabbix — расширяем макро границы»

Для создания элементов отслеживания в заббиксе буду использовать авто обнаружение.

Авто обнаружение

Для авто обнаружения регистраций и отслеживания состояний пиров необходимо обращаться к адресу: https://ats:8089/mxml?action=SIPshowregistry или https://ats:8089/mxml?action=SIPpeers

В ответ АТС возвращает нам XML ответ:

<ajax-response>
<response type="object" id="unknown">
<generic response="Success" eventlist="start" message="Registrations will follow"/>
</response>
...
<response type="object" id="unknown">
<generic event="RegistryEntry" host="login.mtt.ru" port="5060" username="111111" domain="login.mtt.ru" domainport="5060" refresh="105" state="Registered" registrationtime="1592502142"/>
</response>
<response type="object" id="unknown">
<generic event="RegistryEntry" host="voip.uiscom.ru" port="5060" username="222222" domain="voip.uiscom.ru" domainport="5060" refresh="105" state="Registered" registrationtime="1592502142"/>
</response>
<response type="object" id="unknown">
<generic event="RegistryEntry" host="voip.uiscom.ru" port="5060" username="333333" domain="voip.uiscom.ru" domainport="5060" refresh="105" state="Registered" registrationtime="1592502142"/>
</response>
...
</ajax-response>

В ответе много мусора, по этому в препроцессинге мы его фильтруем по шаблону XPath: //response/generic[@host]
Дальше начинается самое интересное. Чтобы работать с обнаружением и динамически создавать элементы, нужно чтобы ответ был в JSON формате. XML при авто обнаружениях не поддерживается.

Для преобразования XML в JSON пришлось немного поиграться с авто заменой, для чего я сделал скрипт на JS

Как подружить Zabbix с Asterisk «из коробки»

Интересный момент, в ответе АТС все параметры обрамляются одинарными кавычками, а после применения шаблона //response/generic[@host] они заменяются на двойные.

Для создания элементов используем переменные из XML ответа ( теперь JSON )​.

Как подружить Zabbix с Asterisk «из коробки»

SIP Registry

Для сип регистраций используем три переменные: username, host, port. Меня устраивало название элемента [email protected]:5060, ситуаций когда нужно использовать все пять переменных я не нашел.

Главный элемент, который получает информацию о всех регистрациях, Asterisk — AMI SIPshowregistry. Раз в минуту он обращается GET запросом к https://ats:8089/mxml?action=SIPshowregistry, после чего данные XML ответа передаются всем зависимым элементам для анализа. Элемент по каждой регистрации создаю зависимыми от него. Это удобно, т. к. актуальную информацию мы получаем за один запрос, а не для каждого отдельно. У данной реализации есть существенный минус — нагрузка на процессор.

При тестировании до 100 зависимых элементов, нагрузку не заметил, но при 1700 элементов, это давало заметную 15 секундную нагрузку на процессор. Имейте это ввиду, если у вас большое количество зависимых элементов.

Как вариант для «размазывания» нагрузки или установки разной частоты опроса элемента, можно вынести логику обработки в каждый элемент отдельно.

Полученную информацию я не храню в главном элементе. Во-первых, не вижу в этом необходимости, а во-вторых, если ответ больше 64К, то заббикс его обрезает.

Поскольку для зависимого элемента у нас используется полный XML ответ, нам нужно в препроцессинге получить значение данного элемента. Через XPath это делается так:
string(//response/generic[@event=«RegistryEntry»][@username="{#SIP_REGISTRY_USERNAME}"][@host="{#SIP_REGISTRY_HOST}"][@port="{#SIP_REGISTRY_PORT}"]/@state)
Для статусов регистраций не стал использовать текстовые статусы, а перевел их в числовой вид с помощью JavaScript:

switch(value) {
  case 'Registered':
    return 1;
  case 'Unregistered':
    return 0;
  default:
    return -1;
}

SIP Peers

По аналогии с сип регистрациями, есть главный элемент Asterisk — AMI SIPshowregistry, к которому добавляются зависимые.

Здесь создается два зависимых элемента:

  • Статус пира в текстовом виде
  • Время отклика устройства — если статус ОК, то пишется время ответа устройства, иначе «-1»

Сам путь до элемента уже чуть проще XPath:

string(//response/generic[@objectname="{#SIP_PEER_OBEJECTNAME}"]/@status)

Для второго элемента использовал JavaScript, чтобы отделить время отклика от статуса пира, поскольку хранятся они вместе:

if(value.substring(0,2) == 'OK'){
	return value.match(/(d+)/gm);
}
else {
	return -1;
}

Заключение

Решение «из коробки» может быть сложным и не сразу понятным. Возрастает гибкость и переносимость между разными системами

Всем приятной и легкой интеграции! Шаблон и инструкция по настройке на GitHub.

Источник: habr.com