В предыдущей статье «Zabbix — расширяем макро границы» я рассказал как получать сессию авторизации и подставлять ее в локальный макрос хоста. В этой статье я расскажу как подружить Zabbix с Asterisk без внешних скриптов и ПО.
Идея «подружить» эти две системы родилась давно, причем без установки дополнительного софта и скриптов. Быстрое гугление выдавало множество вариантов решений, все сводилось к тому, что закиньте скрипты (на пыхе, баше, питоне и т. п.) на сервер, и будет вам счастье. Мне же хотелось реализовать мониторинг «из коробки» — без внешних скриптов и установки дополнительного софта на сервер с мониторингом и АТС.
Провозился я с этим в сумме 4 рабочих дня, но результат стоил того. Работа через интерфейс AMI, низкоуровневое обнаружение, триггеры, а главное, на подключение АТС и все остальные настройки теперь уходит минут 15.
В наличии заббикс 4.4, около 100 штук Астерисков 13 версии. Какие-то АТС идут с веб интерфейсом FreePBX, какие-то с голой консолью, кучей хитростей и интеграцией через диалплан.
Первый и основной момент, который нужно решить — получение данных о пирах и сип регистрациях. Для этого в АТС существуют интерфейсы AGI, AMI, ARI и SSH консоль. Дополнительные модули по понятным причинам не рассматривал.
Для начала надо разобраться, что из себя представляют эти аги, ами, ари….
Тем не менее, при всех своих недостатках ARI закрывает 90% всех потребностей по мониторингу.
AMI знаю хорошо, в свое время реализовывал отслеживание потерь в разговорах с делением по удаленным офисам, управление звонками и т.п. С Telnet тоже все предельно ясно: открывай подключение, шли команды и читай ответ. Что я и сделал, но результат меня разочаровал.
Telnet у заббикса не такой как в консоле Linux, он чуть более простой и заточенный под стандартную авторизацию типа логин/пароль. Если логика авторизации другая, и нет запроса пары логин/пароль, вылетает ошибка. После тщетных попыток обойти требование авторизации полез смотреть исходники модуля Telnet.
Я понял, что пока не будет традиционного запроса логина с паролем, дальше не продвинусь. Ради интереса, выкинул из кода все, что касается авторизации, пересобрал все. Работает! Но под требования не подходит. Идем дальше…
Перечитал еще раз документацию по ARI, провел дополнительные тесты — нет тут сип регистраций. Пиры есть, разговоры есть, бриджи есть, регистраций нет. В какой-то момент даже задумался, так ли нужны нам сип регистрации?
По забавному стечению обстоятельств, в этот момент прилетает очередной запрос от пользователя, с проблемой исходящих звонков. Проблема была в подвисании сип регистрации и решалась обычной перезагрузкой модуля.
Было бы здорово по вебу обращаться к AMI: это бы решило все проблемы, подумал я. Начинаю копать в этом направлении, и буквально первая строка поиска ведет на официальную документацию Asterisk, в которой говориться, что для моих задач есть опция webenabled в файле /etc/asterisk/manager.conf, которую нужно установить в значение YES, в секции [general]
После этого, через обычный веб запрос вида http://ats:8089/mxml?action=SIPshowregistry получаем всю необходимую информацию.
Добавление опции 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 запрос:
Ответ:
Для работы там нужно mansession_id="6f5de42c", т.е. сама кука авторизации.
Контент нужно лишь проверить наличие ответа «Authentication accepted». Дальше, при всех обращениях к серверу АТС нам необходимо будет добавлять в запрос куку авторизации.
Как получать куку авторизации и использовать в других запросах читайте тут: «Zabbix — расширяем макро границы»
Для создания элементов отслеживания в заббиксе буду использовать авто обнаружение.
Для авто обнаружения регистраций и отслеживания состояний пиров необходимо обращаться к адресу: https://ats:8089/mxml?action=SIPshowregistry или https://ats:8089/mxml?action=SIPpeers
В ответ АТС возвращает нам XML ответ:
В ответе много мусора, по этому в препроцессинге мы его фильтруем по шаблону XPath: //response/generic[@host]
Дальше начинается самое интересное. Чтобы работать с обнаружением и динамически создавать элементы, нужно чтобы ответ был в JSON формате. XML при авто обнаружениях не поддерживается.
Для преобразования XML в JSON пришлось немного поиграться с авто заменой, для чего я сделал скрипт на JS
Интересный момент, в ответе АТС все параметры обрамляются одинарными кавычками, а после применения шаблона //response/generic[@host] они заменяются на двойные.
Для создания элементов используем переменные из XML ответа ( теперь JSON ).
Для сип регистраций используем три переменные: username, host, port. Меня устраивало название элемента 111111@login.mtt.ru:5060, ситуаций когда нужно использовать все пять переменных я не нашел.
Главный элемент, который получает информацию о всех регистрациях, Asterisk — AMI SIPshowregistry. Раз в минуту он обращается GET запросом к https://ats:8089/mxml?action=SIPshowregistry, после чего данные XML ответа передаются всем зависимым элементам для анализа. Элемент по каждой регистрации создаю зависимыми от него. Это удобно, т. к. актуальную информацию мы получаем за один запрос, а не для каждого отдельно. У данной реализации есть существенный минус — нагрузка на процессор.
Полученную информацию я не храню в главном элементе. Во-первых, не вижу в этом необходимости, а во-вторых, если ответ больше 64К, то заббикс его обрезает.
Поскольку для зависимого элемента у нас используется полный XML ответ, нам нужно в препроцессинге получить значение данного элемента. Через XPath это делается так:
string(//response/generic[@event=«RegistryEntry»][@username="{#SIP_REGISTRY_USERNAME}"][@host="{#SIP_REGISTRY_HOST}"][@port="{#SIP_REGISTRY_PORT}"]/@state)
Для статусов регистраций не стал использовать текстовые статусы, а перевел их в числовой вид с помощью JavaScript:
По аналогии с сип регистрациями, есть главный элемент Asterisk — AMI SIPshowregistry, к которому добавляются зависимые.
Здесь создается два зависимых элемента:
Сам путь до элемента уже чуть проще XPath:
string(//response/generic[@objectname="{#SIP_PEER_OBEJECTNAME}"]/@status)
Для второго элемента использовал JavaScript, чтобы отделить время отклика от статуса пира, поскольку хранятся они вместе:
Решение «из коробки» может быть сложным и не сразу понятным. Возрастает гибкость и переносимость между разными системами
Всем приятной и легкой интеграции! Шаблон и инструкция по настройке на GitHub.
Идея «подружить» эти две системы родилась давно, причем без установки дополнительного софта и скриптов. Быстрое гугление выдавало множество вариантов решений, все сводилось к тому, что закиньте скрипты (на пыхе, баше, питоне и т. п.) на сервер, и будет вам счастье. Мне же хотелось реализовать мониторинг «из коробки» — без внешних скриптов и установки дополнительного софта на сервер с мониторингом и АТС.
Провозился я с этим в сумме 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.