В предыдущей статье
«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. Нам подходит!
Тут я было подумал: «Вот оно – решение! Сейчас все будет готово! Изи-пизи лемон сквизи», но радоваться было пока рано. Для получения нужной нам информации достаточно использовать 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
Интересный момент, в ответе АТС все параметры обрамляются одинарными кавычками, а после применения шаблона
//response/generic[@host] они заменяются на двойные.
Для создания элементов используем переменные из XML ответа ( теперь JSON ).
SIP Registry
Для сип регистраций используем три переменные:
username,
host,
port. Меня устраивало название элемента
111111@login.mtt.ru: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.