Jak połączyć Zabbix z Asterisk od razu po wyjęciu z pudełka

W poprzednim artykule „Zabbix – poszerzanie granic makro” Mówiłem ci, jak otrzymać sesję autoryzacyjną i zastąpić ją makro hosta lokalnego. W tym artykule opowiem jak połączyć Zabbix z Asterisk bez zewnętrznych skryptów i oprogramowania.

Pomysł na „zaprzyjaźnienie się” tych dwóch systemów zrodził się dawno temu, bez konieczności instalowania dodatkowego oprogramowania czy skryptów. Szybkie googlowanie dało wiele możliwych rozwiązań, wszystko sprowadzało się do tego, że wrzucisz skrypty (w Pyha, Bash, Python itp.) na serwer i będziesz zadowolony. Chciałem wdrożyć monitoring „od razu po wyjęciu z pudełka” – bez zewnętrznych skryptów i instalowania dodatkowego oprogramowania na serwerze z monitoringiem i centralą PBX.

Spędziłem nad tym łącznie 4 dni robocze, ale wynik był tego wart. Praca przez interfejs AMI, wykrywanie niskiego poziomu, wyzwalacze i co najważniejsze, podłączenie centrali PBX i wszystkich innych ustawień zajmuje teraz około 15 minut.

Dostępny jest Zabbix 4.4, około 100 sztuk Asterisk w wersji 13. Niektóre centrale PBX są wyposażone w interfejs sieciowy FreePBX, inne z gołą konsolą, kilkoma sztuczkami i integracją poprzez dialplan.

Odbiór danych z centrali

Pierwszą i główną kwestią do rozwiązania jest uzyskanie danych o peerach i rejestracjach SIP. W tym celu centrala posiada interfejsy konsolowe AGI, AMI, ARI i SSH. Z oczywistych powodów nie brałem pod uwagę dodatkowych modułów.

Najpierw musimy się dowiedzieć, czym są te agi, ami, ari...

  • AGI - używanie skryptów w planie wybierania. Używany głównie do zarządzania połączeniami.
  • AMI - może dostarczyć wszystkich niezbędnych informacji, działa poprzez port 5038, podobnie jak Telnet. Nam pasuje!
  • ARI - nowoczesny, modny, JSON. Możliwości jest wiele, format danych jest zrozumiały dla Zabbixa, ale dla mnie nie ma najważniejszej rzeczy: nie można kontrolować rejestracji łyków. Inną wadą jest to, że dla peerów dostępne są tylko dwa stany online/offline, chociaż jest ich więcej i warto je wziąć pod uwagę podczas diagnozowania.
  • SSH może zrobić wszystko, ale czasami nie jest dozwolone ze względów bezpieczeństwa. Rozważania mogą być różne, nie będę się w nie zagłębiał.

Jednakże, pomimo wszystkich swoich niedociągnięć, ARI pokrywa 90% wszystkich potrzeb w zakresie monitorowania.

Zabbix i Telnet – moje rozczarowanie

Znam dobrze AMI, kiedyś wdrażałem śledzenie strat w rozmowach z oddziałami przez biura zdalne, zarządzanie połączeniami itp. Dzięki Telnetowi wszystko jest również bardzo jasne: otwórz połączenie, wyślij polecenia i przeczytaj odpowiedź. Tak właśnie zrobiłem, ale efekt mnie rozczarował.

Telnet w Zabbix to nie to samo, co w konsoli Linux, jest trochę prostszy i dostosowany do standardowej autoryzacji takiej jak login/hasło. Jeśli logika autoryzacji jest inna i nie ma żądania podania pary login/hasło, wystąpi błąd. Po daremnych próbach obejścia wymogu autoryzacji warto było zajrzeć do kodu źródłowego modułu Telnet.

Zdałem sobie sprawę, że dopóki nie pojawi się tradycyjna prośba o login i hasło, nie pójdę dalej. Dla zabawy usunąłem z kodu wszystko związane z autoryzacją i złożyłem wszystko ponownie. Pracuje! Ale nie spełnia wymagań. Zacząć robić…

Wróćmy do poszukiwań

Jeszcze raz przeczytałem dokumentację ARI, przeprowadziłem dodatkowe testy - nie ma tu żadnych rejestracji sipów. Są biesiady, są rozmowy, są bryczesy, ale nie ma zapisów. W pewnym momencie nawet pomyślałem, czy naprawdę potrzebujemy rejestracji sępów?

Zabawnym zbiegiem okoliczności w tym momencie przychodzi kolejne żądanie od użytkownika, z problemem połączeń wychodzących. Problem polegał na tym, że rejestracja łyku zawieszała się i został rozwiązany poprzez ponowne uruchomienie modułu.

asterisk -rx "sip reload"

Byłoby wspaniale mieć dostęp do AMI przez Internet: to rozwiązałoby wszystkie problemy, pomyślałem. Zaczynam kopać w tym kierunku i dosłownie pierwsza linijka wyszukiwania prowadzi do oficjalnej dokumentacji Asteriska, która mówi, że istnieje opcja dla moich zadań z obsługą sieci w pliku /etc/asterisk/manager.conf, które należy ustawić na TAK w sekcji [ogólny]

Następnie poprzez zwykłe żądanie internetowe formularza http://ats:8089/mxml?action=SIPshowregistry otrzymujemy wszystkie niezbędne informacje.

W przypadku korzystania z interfejsu FreePBX nie można włączyć tej opcji przez Internet; należy ją włączyć poprzez konsolę, wprowadzając zmiany w pliku manager.conf. FreePBX nie usuwa go, gdy zmiany konfiguracji zostaną wprowadzone przez Internet.

Od dłuższego czasu pracuję z różnymi rodzajami integracji Asterisk, ale nigdy nie widziałem nigdzie wzmianki o tej funkcji. Zdziwiłem się, że nikt nie opisuje tej metody interakcji z centralą. Szczególnie przydatne było szukanie informacji na ten temat: praktycznie nie ma nic lub był on używany do zupełnie innych zadań.

WEB AMI - jaka bestia?

Dodanie opcji z obsługą sieci do pliku menedżer.conf zapewnił pełny dostęp do zarządzania ATS poprzez sieć WWW. Wszystkie polecenia dostępne poprzez zwykły AMI są teraz dostępne w sieci, można odsłuchiwać zdarzenia z centrali poprzez gniazdo. Zasada działania nie różni się od konsoli AMI. Po włączeniu tej opcji można kontaktować się z centralą pod następującymi adresami:

https://ats:8089/manager — strona internetowa z prostym interfejsem do testowania i ręcznego wysyłania żądań. Wszystkie odpowiedzi są sformatowane w czytelnym formacie HTML. Niezbyt nadaje się do monitorowania.
https://ats:8089/rawman — tylko wyjście tekstowe, format podobny do konsolowego AMI
https://ats:8089/mxml - tylko tekst wyjściowy, w formacie XML. Nam pasuje!

Jak połączyć Zabbix z Asterisk od razu po wyjęciu z pudełka

Wtedy pomyślałem: „To jest rozwiązanie! Teraz wszystko będzie gotowe! Lekko wyciśnięty cytrynowy sok”, ale było za wcześnie, żeby się cieszyć. Aby uzyskać potrzebne nam informacje wystarczy skorzystać z żądania GET z odpowiednią akcją akcja, który w odpowiedzi zwraca plik XML z listą wszystkich rejestracji i ich statusem. Wszystko świetnie, ale potrzebujesz autoryzacji, aby zapamiętać sesję z pliku cookie. Kiedy testujesz w przeglądarce, nie myślisz o tym procesie.

Proces autoryzacji

Najpierw adresujemy http://ats:8089/mxml?action=login&username=zabbix&secret=zabbixw odpowiedzi serwer wysyła nam ciasteczko z sesją autoryzacyjną. Tak 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: 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

Odpowiedź:

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>

Aby tam pracować, potrzebujesz identyfikator_sesji rezydencji="6f5de42c", czyli sam plik cookie autoryzacyjny.
Treść, którą musisz tylko sprawdzić, aby znaleźć odpowiedź ”Uwierzytelnienie zaakceptowane" Następnie dla wszystkich wywołań do serwera PBX będziemy musieli dodać do żądania plik cookie autoryzacyjny.

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

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

Przeczytaj, jak uzyskać plik cookie autoryzacyjny i używać go w innych żądaniach tutaj: „Zabbix - poszerzanie granic makr»

Do tworzenia elementów śledzących w Zabbix użyję automatycznego wykrywania.

Automatyczne wykrywanie

Aby automatycznie wykrywać rejestracje i śledzić stany równorzędne, należy skontaktować się z następującym adresem: https://ats:8089/mxml?action=SIPshowregistry lub https://ats:8089/mxml?action=SIPpeers

W odpowiedzi centrala zwraca nam odpowiedź w formacie 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>

W odpowiedzi jest dużo śmieci, więc podczas wstępnego przetwarzania filtrujemy je według szablonu XPath: //odpowiedź/generic[@host]
Potem zaczyna się zabawa. Aby móc pracować z wykrywaniem i dynamicznie tworzyć elementy, odpowiedź musi być w formacie JSON. XML nie jest obsługiwany w przypadku automatycznego wykrywania.

Aby przekonwertować XML na JSON musiałem trochę pobawić się z automatyczną zamianą, dla czego zrobiłem skrypt w JS

Jak połączyć Zabbix z Asterisk od razu po wyjęciu z pudełka

Ciekawostka: w odpowiedzi ATS wszystkie parametry są ujęte w pojedyncze cudzysłowy i po zastosowaniu szablonu //odpowiedź/generic[@host] zastępuje się je podwójnymi.

Do tworzenia elementów używamy zmiennych z odpowiedzi XML (obecnie JSON).

Jak połączyć Zabbix z Asterisk od razu po wyjęciu z pudełka

Rejestr SIP

W przypadku rejestracji sip używamy trzech zmiennych: nazwa użytkownika, gospodarz, Port. Byłem zadowolony z nazwy elementu [email chroniony]: 5060, nie znalazłem żadnych sytuacji, w których trzeba byłoby użyć wszystkich pięciu zmiennych.

Głównym elementem otrzymującym informacje o wszystkich rejestracjach, Gwiazdka - rejestracja AMI SIPshow. Raz na minutę wysyła żądanie GET do https://ats:8089/mxml?action=SIPshowregistry, po czym dane XML odpowiedzi są przekazywane do wszystkich elementów zależnych w celu analizy. Dla każdej rejestracji tworzę element zależny od niej. Jest to wygodne, ponieważ aktualne informacje otrzymujemy w jednym zapytaniu, a nie dla każdego żądania z osobna. Ta implementacja ma znaczną wadę - obciążenie procesora.

Podczas testowania do 100 zależnych elementów nie zauważyłem obciążenia, ale przy 1700 elementach dało to zauważalne 15-sekundowe obciążenie procesora. Należy o tym pamiętać, jeśli masz dużą liczbę elementów zależnych.

Jako opcję „rozłożenia” obciążenia lub ustawienia różnych częstotliwości odpytywania dla elementu, możesz przenieść logikę przetwarzania do każdego elementu osobno.

Nie przechowuję otrzymanych informacji w elemencie głównym. Po pierwsze, nie widzę takiej potrzeby, a po drugie, jeśli odpowiedź przekracza 64 tys., to Zabbix ją ucina.

Ponieważ używamy pełnej odpowiedzi XML dla elementu zależnego, musimy uzyskać wartość tego elementu w przetwarzaniu wstępnym. Poprzez XPath robi się to tak:
string(//response/generic[@event="RegistryEntry"][@username="{#SIP_REGISTRY_USERNAME}"][@host="{#SIP_REGISTRY_HOST}"][@port="{#SIP_REGISTRY_PORT}"]/@ państwo)
W przypadku statusów rejestracyjnych nie korzystałem ze statusów tekstowych, lecz przekonwertowałem je na postać liczbową za pomocą JavaScript:

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

Koledzy SIP

Analogicznie do rejestracji SIP, istnieje główny element Asterisk - AMI SIPshowregistry, do którego dodawane są zależne.

Tworzy to dwa zależne elementy:

  • Status równorzędny w formie tekstowej
  • Czas reakcji urządzenia - jeżeli stan jest OK to zapisywany jest czas reakcji urządzenia, w przeciwnym razie „-1”

Ścieżka do samego elementu jest nieco prostsza XPath:

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

W przypadku drugiego elementu do oddzielenia użyłem JavaScript czas odpowiedzi ze statusu równorzędnego, ponieważ są przechowywane razem:

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

wniosek

Gotowe rozwiązanie może być złożone i nie od razu jasne. Zwiększa elastyczność i przenośność pomiędzy różnymi systemami

Udanej i łatwej integracji wszystkim! Szablon i instrukcje dotyczące konfiguracji GitHub.

Źródło: www.habr.com

Dodaj komentarz