Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript
Tikhon Uskov, inżynier zespołu integracji Zabbix

Zabbix to platforma dostosowywana, która służy do monitorowania wszelkich danych. Od najwcześniejszych wersji Zabbix administratorzy monitoringu mieli możliwość uruchamiania różnych skryptów przez Akcje do testów na docelowych węzłach sieci. Uruchamianie skryptów rodziło jednak szereg trudności, w tym potrzebę wsparcia skryptów, ich dostarczania do węzłów komunikacyjnych i proxy, a także wsparcia dla różnych wersji.

JavaScript dla Zabbix

W kwietniu 2019 roku zaprezentowano Zabbix 4.2 z funkcją wstępnego przetwarzania w JavaScript. Wiele osób zaczęło rozważać rezygnację z pisania skryptów, które pobierają dane z różnych źródeł, przetwarzają je i udostępniają w formacie zrozumiałym dla Zabbiksa. W zamian za to planowano przeprowadzanie prostych testów, które będą otrzymywać dane niesformatowane do przechowywania i przetwarzania przez Zabbiksa, a następnie przetwarzać ten strumień danych z wykorzystaniem narzędzi Zabbiksa oraz JavaScript. W duecie z niskopoziomowym wykrywaniem i zależnymi elementami danych, które wprowadził Zabbix 3.4, udało się stworzyć wystarczająco elastyczną koncepcję do sortowania i zarządzania otrzymanymi danymi.

W Zabbix 4.4, jako logiczna kontynuacja wstępnego przetwarzania w JavaScript, wprowadzono nowy sposób powiadamiania — Webhook, który można wykorzystać do prostej integracji powiadomień Zabbiksa z aplikacjami zewnętrznymi.

JavaScript i Duktape

Dlaczego wybrano właśnie JavaScript i Duktape? Rozważano różne opcje języków i silników:

  • Lua – Lua 5.1
  • Lua – LuaJIT
  • JavaScript – Duktape
  • JavaScript – JerryScript
  • Embedded Python
  • Embedded Perl

Główne kryteria wyboru to powszechność, łatwość integracji silnika z produktem, niskie zużycie zasobów oraz ogólna wydajność silnika, jak również bezpieczeństwo implementacji kodu w tym języku do monitoringu. W sumie, JavaScript na silniku Duktape okazał się najlepszy.

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript

Kryteria wyboru i testowanie wydajności

Cechy Duktape:

— Standard ECMAScript E5/E5.1
— Moduły Zabbix dla Duktape:

  • Zabbix.log() — umożliwia bezpośrednie zapisywanie wiadomości o różnym poziomie szczegółowości do logu serwera Zabbix, co umożliwia przyporządkowanie błędów, na przykład, w Webhook do stanu serwera.
  • CurlHttpRequest() — pozwala na wykonywanie zapytań HTTP do sieci, co jest podstawą użycia Webhook.
  • atob() i btoa() — umożliwiają kodowanie i dekodowanie łańcuchów w formacie Base64.

UWAGA. Duktape spełnia standardy ACME. W Zabbix używana jest wersja skryptu z 2015 roku. Kolejne zmiany są nieznaczne, dlatego można je zignorować..

Magia JavaScript

Cała magia JavaScript tkwi w dynamicznym typowaniu i rzutowaniu typów: tekstowych, liczbowych i logicznych.

Oznacza to, że nie trzeba z góry określać, jakiego typu wartość ma zwracać zmienna.

Podczas operacji matematycznych wartości zwracane przez operatory-funkcje są konwertowane na liczby. Wyjątkiem w takich operacjach jest dodawanie, ponieważ jeśli choćby jedna ze składników jest ciągiem, do wszystkich składników stosowana jest konwersja do ciągu.

UWAGA. Metody odpowiedzialne za takie konwersje są zazwyczaj realizowane w prototypach nadrzędnych obiektów, valueOf и toString. valueOf wywoływana podczas konwersji numerycznej i zawsze przed metodą toString. Metoda valueOf musi zwracać wartości prymitywne, w przeciwnym razie jej wynik jest ignorowany.

Dla obiektu wywoływana jest metoda valueOf. Jeśli nie zostanie znaleziona lub nie zwraca wartości prymitywnej, wywoływana jest metoda toString. Jeśli metoda toString nie jest znaleziona, następuje poszukiwanie valueOf w prototypie obiektu, i wszystko powtarza się do zakończenia przetwarzania wartości i przekształcenia wszystkich wartości w wyrażeniu do jednego typu.. Jeżeli dla obiektu zdefiniowana jest metoda toString, która zwraca wartość prymitywną, to właśnie ona jest używana do przekształcenia do ciągu. Przy tym wynikiem zastosowania tej metody nie musi być koniecznie ciąg.

Na przykład, jeśli dla obiektu ‘obj‘ definiuje się metodę toString,

`var obj = { toString() { return "200" }}` 

metoda toString zwraca dokładnie ciąg znaków, a w przypadku dodawania ciągu do liczby otrzymujemy połączony ciąg:

`obj + 1 // '2001'` 

`obj + 'a' // ‘200a'`

Jednak jeśli przepiszesz toString, aby metoda zwracała liczbę, w przypadku dodawania obiektu wykona się działanie matematyczne z konwersją liczby, i otrzymujemy wynik dodawania matematycznego.

`var obj = { toString() { return 200 }}` 

`obj + 1 // '2001'`

Przy czym, jeśli wykonujemy dodawanie z ciągiem, następuje konwersja do ciągu, i otrzymujemy połączony ciąg.

`obj + 'a' // ‘200a'`

To właśnie w tym kryje się powód wielu błędów początkujących użytkowników JavaScript.

W metodzie toString można wpisać funkcję, która zwiększy bieżącą wartość obiektu o 1.

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript
Wykonanie skryptu przy założeniu, że zmienna ma wartość 3, a także 4.

Przy porównywaniu z rzutowaniem typów (==) za każdym razem wykonywana jest metoda toString z funkcją zwiększania wartości. W związku z tym, przy każdym następnym porównaniu, wartość wzrasta. Można tego uniknąć, używając porównania bez rzutowania typów (===).

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript
Porównanie bez rzutowania typów

UWAGA. Nie używaj porównania z rzutowaniem typów bez potrzeby.

W przypadku złożonych skryptów, takich jak Webhook z skomplikowaną logiką, gdzie wymagane jest porównanie z rzutowaniem typów, zaleca się najpierw napisanie weryfikacji dla wartości, które zwracają zmienne oraz obsługę niespójności i błędów.

Webhook Media

Pod koniec 2019 roku oraz na początku 2020 roku zespół integracji Zabbiksa intensywnie rozwijał Webhooki i integracje 'plug-and-play', które są dostarczane w dystrybucji Zabbiksa.

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript
Link do dokumentacji

Preprocessing

  • Wprowadzenie preprocesowania w JavaScript pozwoliło na rezygnację z większości zewnętrznych skryptów, a w obecnej chwili w Zabbiksie można uzyskać dowolną wartość i przekształcić ją w zupełnie inną.
  • Preprocessing w Zabbiksie jest realizowane kodem JavaScript, który w czasie kompilacji do bajt-kodu przekształca się w funkcję przyjmującą pojedynczą wartość jako parametr wartość w postaci ciągu (w ciągu może być zarówno liczba, jak i cyfra).
  • Ponieważ na wyjściu otrzymujemy funkcję, na końcu skryptu wymagana jest return.
  • Można używać niestandardowych makr w kodzie.
  • Zasoby można ograniczyć nie tylko na poziomie systemu operacyjnego, ale także programowo. Na etap wstępnego przetwarzania przydzielane jest maksymalnie 10 megabajtów pamięci operacyjnej oraz limit czasu wykonania wynoszący 10 sekund.

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript

UWAGA. Wartości limitu czasu wynoszącego 10 sekund są dość długie, ponieważ zbieranie tysięcy elementów danych w 1 sekundzie w dość 'ciężkim' scenariuszu wstępnego przetwarzania może spowolnić działanie Zabbixa. Dlatego nie zaleca się używania wstępnego przetwarzania do uruchamiania pełnoprawnych skryptów w JavaScript za pomocą tzw. fikcyjnych elementów danych (dummy items), które są uruchamiane tylko w celu wstępnego przetwarzania..

Swój kod można sprawdzić za pomocą testu wstępnego przetwarzania lub korzystając z narzędzia. zabbix_js:

`zabbix_js -s *script-file -p *input-param* [-l log-level] [-t timeout]`

`zabbix_js -s script-file -i input-file [-l log-level] [-t timeout]`

`zabbix_js -h`

`zabbix_js -V`

Zadania praktyczne

Zadanie 1

Zastąpić obliczany element danych wstępnym przetwarzaniem.

Warunek: otrzymujemy z czujnika temperaturę w stopniach Fahrenheita do przechowania w stopniach Celsjusza.

Dawniej stworzyliśmy element danych, który zbierał temperaturę w stopniach Fahrenheita. Następnie powstałby kolejny element danych (obliczeniowy), który na podstawie wzoru przekształcałby stopnie Fahrenheita na temperaturę w stopniach Celsjusza.

Problemy:

  • Należy zduplikować elementy danych i przechować wszystkie wartości w bazie.
  • Trzeba uzgodnić interwały dla «rodzicielskiego» elementu danych, który jest obliczany i wykorzystywany we wzorze, oraz dla elementu danych obliczeniowego. W przeciwnym razie obliczeniowy element danych może znaleźć się w stanie nieobsługiwanym lub obliczyć poprzednią wartość, co wpłynie na niezawodność wyników monitorowania.

Jednym z rozwiązań było rezygnacja z elastycznych interwałów sprawdzania na rzecz stałych interwałów, aby zapewnić prawidłowe obliczenie obliczeniowego elementu danych po elemencie danych, który zbiera dane (w naszym przypadku — temperatura w stopniach Fahrenheita).

Jednak jeśli na przykład używamy szablonu do sprawdzania dużej liczby urządzeń, a sprawdzanie odbywa się co 30 sekund, przez 29 sekund Zabbix „leniuchuje”, a w ostatniej sekundzie zaczyna sprawdzanie i obliczenia. To prowadzi do powstawania kolejki i wpływa na wydajność. Dlatego zaleca się stosowanie stałych interwałów, tylko jeśli jest to naprawdę konieczne.

W tej zadań najlepszym rozwiązaniem jest przetwarzanie w jednej linii w JavaScript, które konwertuje stopnie Fahrenheita na stopnie Celsjusza:

`return (value - 32) * 5 / 9;`

Jest to szybkie i proste, nie trzeba tworzyć dodatkowych elementów danych i przechowywać historię, a także można wykorzystać elastyczne interwały do sprawdzeń.

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript

`return (parseInt(value) + parseInt("{$EXAMPLE.MACRO}"));`

Jednak jeśli w hipotetycznej sytuacji konieczne jest zsumowanie uzyskanego elementu danych z jakąś stałą określoną w makrosie, należy pamiętać, że parametr wartość rozszerza się do łańcucha. Podczas operacji dodawania łańcuchów, dwa łańcuchy po prostu łączą się w jeden.

Rozwiązujemy praktyczne zadania w Zabbix przy użyciu JavaScript

`return (value + "{$EXAMPLE.MACRO}");`

Aby uzyskać wynik działania matematycznego, należy przekonwertować uzyskane wartości na format liczbowy. Można do tego wykorzystać funkcję parseInt(), która zwraca liczbę całkowitą, funkcję parseFloat(), która zwraca liczbę zmiennoprzecinkową, lub funkcję number, która zwraca lub liczbę całkowitą, lub zmiennoprzecinkową.

Zadanie 2

Uzyskaj czas w sekundach do wygaśnięcia certyfikatu.

Warunek: pewna usługa podaje datę wygaśnięcia certyfikatu w formacie "Feb 12 12:33:56 2022 GMT".

W ECMAScript5 Date.parse() przyjmuje datę w formacie ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ). Należy przekonwertować na ten format ciąg w formacie MMM DD YYYY HH:mm:ss ZZ

Problem: wartość miesiąca wyrażona jest tekstowo, a nie liczbowo. Dane w tym formacie nie są akceptowane przez Duktape.

Przykład rozwiązania:

  • Na początku deklarujemy zmienną, która przyjmuje wartość (cały skrypt to deklaracja zmiennych, które są wymienione oddzielone przecinkami).

  • W pierwszym wierszu uzyskujemy datę w parametrze wartość i rozdzielamy ją spacjami metodą split. W ten sposób otrzymujemy tablicę, gdzie każdemu elementowi tablicy, zaczynając od indeksu 0, odpowiada jeden element daty przed i po spacji. split(0) — miesiąc, split(1) — dzień, split(2) — ciąg z czasem itp. Następnie do każdego elementu daty można uzyskać dostęp za pomocą indeksu w tablicy.

`var split = value.split(' '),`

  • Każdemu miesiącowi (w kolejności chronologicznej) przypisany jest indeks jego pozycji w tablicy (od 0 do 11). Aby przekonwertować wartość tekstową na numeryczną, do indeksu miesiąca dodaje się jeden (ponieważ numeracja miesięcy zaczyna się od 1). Wyrażenie z dodawaniem jeden wzięto w nawias, ponieważ w przeciwnym razie otrzymalibyśmy ciąg, a nie liczbę. Na koniec wykonujemy slice() — wycinanie tablicy od końca, aby pozostawić tylko dwa znaki (co jest ważne dla miesięcy z dwucyfrowym numerem).

`MONTHS_LIST = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],`

`month_index = ('0' + (MONTHS_LIST.indexOf(split[0]) + 1)).slice(-2),`

  • Tworzymy z uzyskanych wartości łańcuch w formacie ISO poprzez zwykłe dodawanie łańcuchów w odpowiedniej kolejności.

`ISOdate = split[3] + '-' + month_index + '-' + split[1] + 'T' + split[2],`

Dane w uzyskanym formacie to liczba sekund od 1970 roku do jakiegoś momentu w przyszłości. Używanie danych w uzyskanym formacie w wyzwalaczach jest praktycznie niemożliwe, ponieważ Zabbix pozwala operować tylko na makrach {Date} и {Time}, które zwracają datę i czas w zrozumiałym dla użytkownika formacie.

  • Po tym możemy uzyskać w JavaScript bieżącą datę w formacie Unix Timestamp i odjąć ją od uzyskanego wartości daty ważności certyfikatu, aby otrzymać liczbę milisekund od chwili obecnej do momentu zakończenia certyfikatu.

`now = Date.now();`

  • Dzielimy uzyskaną wartość przez tysiąc, aby uzyskać sekundy w Zabbix.

`return parseInt((Date.parse(ISOdate) - now) / 1000);`

W wyzwalaczu można podać wyrażenie ‘last<‘ oraz zestaw cyfr, które odpowiadają liczbie sekund w okresie, na który należy zareagować, na przykład w tygodniach. W ten sposób wyzwalacz będzie informować, że ważność certyfikatu kończy się za tydzień.

UWAGA. Zwróć uwagę na wykorzystanie parseInt() w funkcji return, aby skonwertować liczbę zmiennoprzecinkową uzyskaną w wyniku dzielenia milisekund na liczbę całkowitą. Można również użyć parseFloat() i przechowywać dane zmiennoprzecinkowe.

Zobacz raport

Źródło: habr.com

Kup solidny hosting dla stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting dla stron z ochroną przed DDoS, serwery VPS VDS | ProHoster