W poprzednim artykule Opisałem, jak uzyskać sesję autoryzacyjną i wstawić ją do lokalnego makra hosta. W tym artykule opowiem, jak zintegrować Zabbix z Asterisk bez użycia zewnętrznych skryptów i oprogramowania.
Pomysł na „zintegrowanie” tych dwóch systemów zrodził się dawno temu, a wszystko to bez instalowania dodatkowego oprogramowania i skryptów. Szybkie wyszukiwanie w Google przyniosło wiele rozwiązań, które sprowadzały się do wrzucenia skryptów (w PHP, Bash, Pythonie itp.) na serwer, a potem po prostu się cieszysz. Ja chciałem zrealizować monitoring „z pudełka” — bez zewnętrznych skryptów i instalowania dodatkowego oprogramowania na serwerze monitorującym i ATE.
Spędziłem nad tym łącznie 4 dni robocze, ale wynik był tego wart. Praca przez interfejs AMI, niskopoziomowe wykrywanie, wyzwalacze, a co najważniejsze, teraz podłączenie do ATE i wszystkie inne ustawienia zajmują tylko około 15 minut.
Posiadam Zabbix 4.4 i około 100 sztuk Asterisk w wersji 13. Niektóre ATE mają interfejs webowy FreePBX, inne tylko nagą konsolę, z wieloma sztuczkami i integracją przez plan połączeń.
Pobieranie danych z ATE
Pierwsza i najważniejsza kwestia, którą trzeba rozwiązać — uzyskanie danych o peeringach i rejestracjach SIP. W tym celu w ATE istnieją interfejsy AGI, AMI, ARI i konsola SSH. Dodatkowe moduły z oczywistych powodów nie były brane pod uwagę.
Na początek trzeba zrozumieć, czym są te AGI, AMI, ARI...
- AGI — użycie skryptów w planie połączeń. W głównym celu służy do zarządzania połączeniami.
- AMI — potrafi przekazywać wszystkie niezbędne informacje, działa przez port 5038 analogicznie do Telnetu. Pasuje do naszych potrzeb!
- ARI — nowoczesne, modne, w formacie JSON. Wiele możliwości, format danych w zrozumiałej formie dla Zabbix, ale dla mnie brak kluczowego aspektu: nie można kontrolować rejestracji SIP. Jeszcze jednym minusem jest to, że dla peeringów istnieją tylko dwa stany online/offline, mimo że stanów jest więcej i ich uwzględnienie jest użyteczne przy diagnostyce.
- SSH — może wszystko, ale czasem nie udostępniają go z powodu „kwestii bezpieczeństwa”. Kwestie te mogą być różne, nie będę ich tu rozbierał.
Niemniej jednak, pomimo wszystkich swoich wad, ARI spełnia 90% wszystkich potrzeb monitorowania.
Zabbix i Telnet — moje rozczarowanie
Dobrze znam AMI, w swoim czasie realizowałem śledzenie strat w rozmowach z podziałem na zdalne biura, zarządzanie połączeniami itd. Z Telnetem też wszystko jasne: otwierasz połączenie, wysyłasz komendy i czytasz odpowiedź. I to właśnie zrobiłem, ale wynik mnie rozczarował.
Telnet w Zabbiksie jest inny niż w konsoli Linux, jest trochę prostszy i dostosowany do standardowej autoryzacji typu login/hasło. Jeśli logika autoryzacji jest inna, a nie ma zapytania o parę login/hasło, pojawia się błąd. Po bezowocnych próbach ominięcia wymagania autoryzacji, postanowiłem zajrzeć do kodu źródłowego modułu Telnet.
Zrozumiałem, że dopóki nie będzie tradycyjnego zapytania o login z hasłem, nie ruszę dalej. Dla ciekawego sprawdzenia wyrzuciłem z kodu wszystko, co dotyczy autoryzacji, i przebudowałem wszystko. Działa! Ale nie spełnia wymagań. Idziemy dalej…
Wracamy do poszukiwań
Przeczytałem ponownie dokumentację ARI, przeprowadziłem dodatkowe testy — nie ma tutaj rejestracji SIP. Są peerzy, są rozmowy, są mosty, ale nie ma rejestracji. W pewnym momencie nawet pomyślałem, czy naprawdę potrzebujemy rejestracji SIP?
Na zabawny zbieg okoliczności, w tym momencie dostaję kolejne zapytanie od użytkownika z problemem z połączeniami wychodzącymi. Problem polegał na zawieszaniu się rejestracji SIP i był rozwiązany zwykłym restartem modułu.
asterisk -rx "sip reload"Byłoby świetnie, gdyby można było uzyskać dostęp do AMI przez sieć: to rozwiązałoby wszystkie problemy, pomyślałem. Zaczynam kopać w tym kierunku i dosłownie pierwsza linia wyszukiwania prowadzi do oficjalnej dokumentacji Asterisk, w której mówi się, że dla moich zadań jest opcja webenabled w pliku /etc/asterisk/manager.conf, którą trzeba ustawić na wartość YES w sekcji [general]
Po tym, poprzez zwykłe zapytanie HTTP w stylu otrzymujemy wszystkie potrzebne informacje.
Podczas korzystania z interfejsu FreePBX, przez sieć nie można włączyć tej opcji, należy to zrobić przez konsolę, wprowadzając zmiany w pliku manager.conf. FreePBX nie usuwa jej podczas zmian konfiguracji przez sieć.
Ile razy pracowałem z różnego rodzaju integracjami Asterisk, nigdy nie widziałem, aby ta funkcja była gdzieś wspomniana. Zdumiewa mnie, że nikt nie opisuje tej metody interakcji z PBX. Nawet specjalnie zajrzałem, aby poszukać informacji na ten temat: praktycznie nic nie ma lub stosowano to do zupełnie innych celów.
WEB AMI — co to za zwierzę?
Dodanie opcji webenabled do pliku manager.conf otwierał pełny dostęp do zarządzania PBX poprzez sieć. Wszystkie komendy dostępne przez standardowe AMI są teraz dostępne w wersji webowej, można nasłuchiwać zdarzenia z PBX przez socket. Zasada działania jest identyczna jak w przypadku konsolowego AMI. Po aktywacji tej opcji, do PBX można się zgłosić pod następującymi adresami:
— strona internetowa z prostym interfejsem, do testów i ręcznego wysyłania zapytań. Wszystkie odpowiedzi formatują się w czytelnej wersji HTML. Nie nadaje się do monitorowania.
— tylko tekstowy wynik, format analogiczny do konsolowego AMI
— tylko tekstowy wynik, w formacie XML. To nam odpowiada!

Pomyślałem: „Oto rozwiązanie! Teraz wszystko będzie gotowe! Łatwizna!”, ale na radość było jeszcze za wcześnie. Aby uzyskać potrzebne informacje wystarczy użyć żądania GET z odpowiednią akcją akcja, które w odpowiedzi zwraca xml z listą wszystkich rejestracji i ich stanem. To wszystko jest w porządku, ale potrzebna jest autoryzacja z zapamiętaniem sesji w ciastkach. Kiedy testujesz w przeglądarce, nie myślisz o tym procesie.
Proces autoryzacji
Na początku zwracamy się pod adres , w odpowiedzi serwer wysyła nam ciastko z sesją autoryzacji. Oto jak wygląda żądanie 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: pl-PL,pl;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: 1Odpowiedź:
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 Aby to działało, potrzebujesz mansession_id="6f5de42c", czyli samego ciastka autoryzacyjnego.
Treść należy jedynie sprawdzić pod kątem obecności odpowiedzi „Authentication accepted”. Następnie, przy wszystkich połączeniach z serwerem PBX, będziemy musieli dodawać ciastko autoryzacyjne do żądania.
https://ats:8089/mxml?action=SIPpeers
Host: ats:8089
Connection: close
Cookie: mansession_id="6f5de42c"Jak uzyskać ciastko autoryzacyjne i używać w innych żądaniach, przeczytaj tutaj: „»
Do tworzenia elementów monitorujących w Zabbixie użyję auto-detekcji.
Auto-detekcja
Aby przeprowadzić auto-detekcję rejestracji i monitorować stany peerów, należy zwrócić się pod adres: lub
W odpowiedzi PBX zwraca nam odpowiedź XML:
...
... Odpowiedź zawiera dużo niepotrzebnych danych, dlatego w przetwarzaniu wstępnym filtrujemy je według wzoru XPath: //response/generic[@host]
Teraz zaczyna się najbardziej interesująca część. Aby pracować z wykrywaniem i dynamicznie tworzyć elementy, musi być odpowiedź w formacie JSON. XML nie jest wspierany przy auto wykrywaniach.
Aby przekształcić XML w JSON, musiałem trochę się pobawić z auto zamianą, do czego napisałem skrypt w JS

Interesujący moment, w odpowiedzi centrali wszystkie parametry są otoczone pojedynczymi cudzysłowami, a po zastosowaniu szablonu //response/generic[@host] zostają one zamienione na podwójne.
Aby stworzyć elementy, używamy zmiennych z odpowiedzi XML (teraz JSON).

SIP Registry
Do rejestracji SIP używamy trzech zmiennych: nazwa użytkownika, Najciekawsza linia — ostatnia., port. Zadowalała mnie nazwa elementu 111111@login.mtt.ru:5060, sytuacji, w których trzeba używać wszystkich pięciu zmiennych, nie znalazłem.
Główny element, który otrzymuje informacje o wszystkich rejestracjach, Asterisk — AMI SIPshowregistry. Co minutę wykonuje zapytanie GET do , po czym dane z odpowiedzi XML są przekazywane wszystkim zależnym elementom do analizy. Element dla każdej rejestracji tworzę zależnym od niego. To jest wygodne, ponieważ aktualne informacje uzyskujemy w jednym zapytaniu, a nie dla każdego z osobna. Ta realizacja ma poważną wadę — obciążenie procesora.
Podczas testowania do 100 zależnych elementów nie zauważyłem obciążenia, ale przy 1700 elementach dawalo to zauważalne 15-sekundowe obciążenie procesora. Miej to na uwadze, jeśli masz dużą liczbę zależnych elementów.
Jako opcję dla "rozpraszania" obciążenia lub ustawienia różnej częstotliwości zapytań dla elementu, można przenieść logikę przetwarzania do każdego elementu osobno.
Nie przechowuję uzyskanych informacji w elemencie głównym. Po pierwsze, nie widzę takiej potrzeby, a po drugie, jeśli odpowiedź jest większa niż 64K, Zabbix ją przycina.
Ponieważ dla elementu zależnego używamy pełnej odpowiedzi XML, musimy w preprocessing uzyskać wartość tego elementu. Przez XPath robi się to tak:
string(//response/generic[@event="RegistryEntry"][@username="{#SIP_REGISTRY_USERNAME}"][@host="{#SIP_REGISTRY_HOST}"][@port="{#SIP_REGISTRY_PORT}"]/@state)
Nie użyłem tekstowych statusów dla rejestracji, lecz przekształciłem je w wersję numeryczną za pomocą JavaScript:
switch(value) {
case 'Registered':
return 1;
case 'Unregistered':
return 0;
default:
return -1;
}
SIP Peers
Podobnie jak w przypadku rejestracji SIP, istnieje główny element Asterisk — AMI SIPshowregistry, do którego dodawane są elementy zależne.
Tutaj tworzone są dwa elementy zależne:
- Status peer w formie tekstowej
- Czas reakcji urządzenia — jeśli status jest OK, podawany jest czas odpowiedzi urządzenia, w przeciwnym razie „-1”
Sam ciąg do elementu jest już nieco prostszy XPath:
string(//response/generic[@objectname="{#SIP_PEER_OBJECTNAME}"]/@status)
Dla drugiego elementu użyłem JavaScript, aby oddzielić czas reakcji od statusu peer, ponieważ są one przechowywane razem:
if(value.substring(0,2) == 'OK'){
return value.match(/(d+)/gm);
}
else {
return -1;
}Podsumowanie
Rozwiązanie „out of the box” może być złożone i nie od razu zrozumiałe. Wzrasta elastyczność i przenośność między różnymi systemami.
Wszystkim życzę udanej i prostej integracji! Szablon i instrukcja dotycząca konfiguracji na .
Źródło: habr.com
