
Tikhon Uskov, Ingenieur im Zabbix-Integrations-Team
Zabbix ist eine anpassbare Plattform, die zur Überwachung aller Arten von Daten eingesetzt wird. Seit den frühesten Versionen von Zabbix hatten Monitoring-Administratoren die Möglichkeit, verschiedene Skripte über Aktionen auf den Zielknoten im Netzwerk auszuführen. Dabei brachte das Ausführen von Skripten eine Reihe von Herausforderungen mit sich, einschließlich der Notwendigkeit, die Skripte zu verwalten, sie zu den Kommunikationsknoten und Proxys zu übertragen sowie unterschiedliche Versionen zu unterstützen.
JavaScript für Zabbix
Im April 2019 wurde Zabbix 4.2 mit einer Vorverarbeitungsfunktion für JavaScript vorgestellt. Viele waren begeistert von der Idee, auf das Schreiben von Skripten zu verzichten, die irgendwo Daten abrufen, diese verarbeiten und bereits im für Zabbix verständlichen Format bereitstellen. Stattdessen sollten einfache Prüfungen durchgeführt werden, die vorbereitete Daten, die noch nicht für Zabbix gespeichert und verarbeitet sind, erfassen und dann diesen Datenstrom mithilfe der Zabbix-Mittel und JavaScript verarbeiten. In Verbindung mit der Low-Level-Erkennung und den abhängigen Datenelementen, die in Zabbix 3.4 eingeführt wurden, entstand ein ausreichend flexibles Konzept zur Sortierung und Verwaltung der erhaltenen Daten.
In Zabbix 4.4 gibt es als logische Fortsetzung der Vorverarbeitung mit JavaScript eine neue Benachrichtigungsmethode – Webhook, die für eine einfache Integration von Zabbix-Benachrichtigungen mit Drittanbieteranwendungen verwendet werden kann.
JavaScript und Duktape
Warum wurden gerade JavaScript und Duktape ausgewählt? Es wurden verschiedene Programmiersprachen und Engines in Betracht gezogen:
- Lua – Lua 5.1
- Lua – LuaJIT
- Javascript – Duktape
- Javascript – JerryScript
- Embedded Python
- Embedded Perl
Die wichtigsten Auswahlkriterien waren Verbreitung, einfache Integration der Engine in das Produkt, niedriger Ressourcenverbrauch und die allgemeine Leistungsfähigkeit der Engine sowie die Sicherheit bei der Ausführung von Code in dieser Sprache für das Monitoring. Aufgrund dieser Kriterien setzte sich JavaScript auf der Duktape-Engine durch.

Auswahlkriterien und Performance-Tests
Besonderheiten von Duktape:
— Standard
— Zabbix-Module für Duktape:
- Zabbix.log() – ermöglicht es, Nachrichten mit unterschiedlichem Detaillierungsgrad direkt ins Zabbix-Serverprotokoll zu schreiben, was die Zuordnung von Fehlern, beispielsweise im Webhook, zum Serverstatus erleichtert.
- CurlHttpRequest() – ermöglicht HTTP-Anfragen an das Netzwerk, was die Grundlage für die Verwendung von Webhooks bildet.
- atob() und btoa() – ermöglichen das Codieren und Decodieren von Strings im Base64-Format.
HINWEIS. Duktape entspricht den ACME-Standards. In Zabbix wird die Skriptversion von 2015 verwendet. Spätere Änderungen sind unerheblich und können ignoriert werden..
Die Magie von JavaScript
Die ganze Magie von JavaScript liegt in der dynamischen Typisierung und Typumwandlung: von Zeichenfolgen, Zahlen und booleschen Werten.
Das bedeutet, dass man den Typ der Variablen nicht im Voraus deklarieren muss, die einen Wert zurückgeben soll.
Bei mathematischen Operationen werden die Rückgabewerte von Funktionsoperatoren in Zahlen umgewandelt. Eine Ausnahme bilden Additionen, da bei einer der Summanden, die eine Zeichenfolge sind, die Zeichenumwandlung auf alle Summen angewandt wird.
HINWEIS. Methoden, die für solche Umwandlungen zuständig sind, sind in der Regel in den Elternelement-Prototypen der Objekte implementiert. valueOf und toString. valueOf wird bei der numerischen Umwandlung aufgerufen und steht immer vor der Methode toString. Die Methode valueOf muss primitive Werte zurückgeben, andernfalls wird ihr Ergebnis ignoriert.
Für das Objekt wird die Methode valueOfaufgerufen. Wenn sie nicht gefunden wird oder keinen primitiven Wert zurückgibt, wird die Methode toStringaufgerufen. Wenn die Methode toString nicht gefunden wird, erfolgt eine Suche. valueOf Im Prototyp des Objekts wird alles wiederholt, bis die Verarbeitung des Wertes abgeschlossen und alle Werte im Ausdruck auf einen einheitlichen Typ gebracht werden.. Wenn für das Objekt eine Methode implementiert ist, toString, die einen primitiven Wert zurückgibt, wird genau diese für die String-Konvertierung verwendet. Das Ergebnis dieser Methode muss jedoch nicht zwingend ein String sein.
Zum Beispiel, wenn für das Objekt ‘obj‘ eine Methode definiert wird, toString,
`var obj = { toString() { return "200" }}` Methode toString gibt sie tatsächlich einen String zurück, und bei der Addition des Strings mit einer Zahl erhalten wir eine zusammengefügte Zeichenkette:
`obj + 1 // '2001'`
`obj + 'a' // ‘200a'`Wenn wir jedoch toString, sodass die Methode eine Zahl zurückgibt, wird bei der Addition des Objekts eine mathematische Operation mit numerischer Umwandlung durchgeführt, und das Ergebnis ist die mathematische Addition.
`var obj = { toString() { return 200 }}`
`obj + 1 // '2001'`Wenn wir die Addition mit einem String durchführen, erfolgt eine String-Umwandlung, und wir erhalten eine zusammengefügte Zeichenkette.
`obj + 'a' // ‘200a'`Das ist der Grund für die Vielzahl von Fehlern bei angehenden JavaScript-Nutzern.
In die Methode toString kann eine Funktion eingebaut werden, die den aktuellen Wert des Objekts um 1 erhöht.

Das Ausführen eines Skripts, wenn die Variable 3 und gleichzeitig 4 ist.
Beim Vergleich mit Typumwandlung (==) wird jedes Mal die Methode toString zur Erhöhung des Wertes aufgerufen. Daher wird der Wert bei jedem weiteren Vergleich erhöht. Dies kann vermieden werden, indem der Vergleich ohne Typumwandlung (===) verwendet wird.

Vergleich ohne Typumwandlung
HINWEIS. Vermeiden Sie den Vergleich mit Typumwandlung, wenn es nicht erforderlich ist..
Für komplexe Skripte, wie z.B. Webhooks mit komplizierter Logik, bei denen ein Vergleich mit Typumwandlung erforderlich ist, wird empfohlen, vorher Prüfungen für die Werte, die von Variablen zurückgegeben werden, zu schreiben und Unstimmigkeiten sowie Fehler zu behandeln.
Webhook Media
Ende 2019 und Anfang 2020 arbeitete das Zabbix-Integrations-Team aktiv an der Entwicklung von Webhooks und „Out-of-the-Box“-Integrationen, die im Zabbix-Distributionspaket enthalten sind.

Link zu
Vorverarbeitung
- Die Einführung von Preprocessing in JavaScript hat es ermöglicht, die Mehrheit der externen Skripte abzulehnen. Aktuell kann in Zabbix jeder Wert erhalten und in einen ganz anderen Wert umgewandelt werden.
- Das Preprocessing in Zabbix wird durch JavaScript-Code realisiert, der beim Kompilieren in Bytecode in eine Funktion umgewandelt wird, die einen einzelnen Wert als Parameter entgegennimmt. value in Form eines Strings (der String kann sowohl eine Zahl als auch eine Ziffer enthalten).
- Da am Ende eine Funktion ausgegeben wird, ist am Ende des Skripts zwingend erforderlich. return.
- Benutzerdefinierte Makros können im Code verwendet werden.
- Die Ressourcen können nicht nur auf Betriebssystemebene, sondern auch programmatisch begrenzt werden. Für den Preprocessing-Schritt stehen maximal 10 Megabyte RAM und eine Ausführungszeit von 10 Sekunden zur Verfügung.

HINWEIS. Die Timeout-Werte von 10 Sekunden sind ausreichend, da das Sammeln von Tausenden von Datenelementen pro Sekunde in einem relativ "intensiven" Vorverarbeitungs-Szenario die Leistung von Zabbix beeinträchtigen kann. Daher wird nicht empfohlen, Vorverarbeitung für die Ausführung vollständiger Skripte in JavaScript über sogenannte Dummy-Datenpunkte zu verwenden, die nur zur Durchführung der Vorverarbeitung gestartet werden..
Sie können Ihren Code mit dem Vorverarbeitungstest oder mit dem Tool überprüfen. 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`
Praktische Aufgaben
Ziel 1
Ersetzen Sie das berechnete Datenelement durch Vorverarbeitung.
Bedingung: Wir erhalten von dem Sensor die Temperatur in Fahrenheit zur Speicherung in Celsius.
Früher hätten wir ein Datenelement erstellt, das die Temperatur in Fahrenheit sammelt. Danach ein weiteres Datenelement (berechnet), das die Fahrenheit-Grade mithilfe einer Formel in Celsius umwandeln würde.
Die Probleme:
- Es ist notwendig, Datenelemente zu duplizieren und alle Werte in der Datenbank zu speichern.
- Es ist notwendig, Intervalle für das „Elternelement“ der Daten zu vereinbaren, das in der Formel berechnet und verwendet wird, sowie für das berechnete Datenelement. Andernfalls könnte das berechnete Datenelement in einen nicht unterstützten Zustand übergehen oder den vorherigen Wert berechnen, was sich negativ auf die Zuverlässigkeit der Überwachungsergebnisse auswirkt.
Eine der Lösungen war, von flexiblen Überprüfungsintervallen auf feste Intervalle umzusteigen, um sicherzustellen, dass das berechnete Datenelement nach dem Datenelement, das die Daten erhält (in unserem Fall die Temperatur in Fahrenheit), berechnet wird.
Wenn wir jedoch beispielsweise die Vorlage zur Überprüfung einer großen Anzahl von Geräten verwenden und die Überprüfung alle 30 Sekunden erfolgt, dann „schlummert“ Zabbix 29 Sekunden und beginnt erst in der letzten Sekunde mit den Überprüfungen und Berechnungen. Dies führt zur Erstellung einer Warteschlange und beeinflusst die Leistung. Daher wird empfohlen, feste Intervalle nur dann zu verwenden, wenn es wirklich notwendig ist.
In dieser Aufgabe ist die optimale Lösung die Vorverarbeitung einer Zeile in JavaScript, die Fahrenheit in Celsius umwandelt:
`return (value - 32) * 5 / 9;`Dies ist schnell und einfach, ohne dass zusätzliche Datenelemente erstellt und deren Verlauf gespeichert werden müssen. Außerdem kann es für flexible Intervalle verwendet werden.

`return (parseInt(value) + parseInt("{$EXAMPLE.MACRO}"));`Wenn jedoch in einer hypothetischen Situation das resultierende Datenelement beispielsweise mit einer im Makro definierten Konstante addiert werden soll, muss beachtet werden, dass der Parameter value in einen String umgewandelt wird. Bei der Addition von Strings werden die beiden Strings einfach zu einem vereint.

`return (value + "{$EXAMPLE.MACRO}");`Um das Ergebnis der mathematischen Operation zu erhalten, müssen die Typen der erhaltenen Werte in numerisches Format umgewandelt werden. Dafür kann die Funktion parseInt(), die eine ganze Zahl zurückgibt, die Funktion parseFloat(), die eine Dezimalzahl zurückgibt, oder die Funktion number, die eine ganze Zahl oder Dezimalzahl zurückgibt, verwendet werden.
Aufgabe 2
Ermitteln Sie die Zeit in Sekunden bis zum Ablauf des Zertifikats.
Bedingung: Ein gewisser Dienst gibt das Ablaufdatum des Zertifikats im Format "Feb 12 12:33:56 2022 GMT" aus.
In ECMAScript5 Date.parse() akzeptiert ein Datum im ISO 8601-Format (YYYY-MM-DDTHH:mm:ss.sssZ). Die Zeichenfolge muss in das Format MMM DD YYYY HH:mm:ss ZZ umgewandelt werden.
Das Problem: der Monat muss in Textform und nicht als Zahl angegeben werden. Daten in diesem Format werden von Duktape nicht akzeptiert.
Beispiel für eine Lösung:
Zuerst wird eine Variable deklariert, die den Wert erhält (das gesamte Skript besteht aus Variablendeklarationen, die durch Kommas getrennt sind).
In der ersten Zeile erhalten wir das Datum aus dem Parameter value und trennen es mithilfe der Methode Splits. So erhalten wir ein Array, bei dem jedes Element, beginnend mit Index 0, einem Datumsteil vor und nach dem Leerzeichen entspricht. split(0) — Monat, split(1) — Tag, split(2) — Zeitzeichenfolgen usw. Danach kann auf jedes Datumselement anhand des Index im Array zugegriffen werden.
`var split = value.split(' '),`- Jeden Monat (in chronologischer Reihenfolge) entspricht der Index seiner Position im Array (von 0 bis 11). Um den Textwert in eine Zahl zu konvertieren, wird zum Monatsindex eins addiert (da die Monatszählung bei 1 beginnt). Der Ausdruck mit der Addition von eins ist in Klammern gesetzt, da sonst ein String und keine Zahl erhalten wird. Am Ende führen wir aus slice() — die Array-Scheibe von hinten, um nur zwei Zeichen zu behalten (was wichtig ist für Monate mit zweistelligem Nummernformat).
`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),`- Wir bilden aus den erhaltenen Werten einen String im ISO-Format durch einfaches Verketten der Strings in der entsprechenden Reihenfolge.
`ISOdate = split[3] + '-' + month_index + '-' + split[1] + 'T' + split[2],`Die Daten im erhaltenen Format sind die Anzahl der Sekunden seit 1970 bis zu einem beliebigen Zeitpunkt in der Zukunft. Das Verwenden der Daten im erhaltenen Format in Triggern ist praktisch unmöglich, da Zabbix nur mit Makros operieren kann {Date} und {Time}, die Datum und Uhrzeit in einem benutzerfreundlichen Format zurückgeben.
- Danach können wir das aktuelle Datum in JavaScript im Unix-Timestamp-Format erhalten und es vom erhaltenen Wert des Ablaufdatums des Zertifikats abziehen, um die Anzahl der Millisekunden vom aktuellen Zeitpunkt bis zum Ablauf des Zertifikats zu erhalten.
`now = Date.now();`- Wir teilen den erhaltenen Wert durch tausend, um die Sekunden in Zabbix zu erhalten.
`return parseInt((Date.parse(ISOdate) - now) / 1000);`Im Trigger kann der Ausdruck ‘last<’ angegeben werden, sowie eine Zahl, die der Anzahl der Sekunden im Zeitraum entspricht, auf den reagiert werden soll, beispielsweise in Wochen. Auf diese Weise wird der Trigger warnen, dass das Zertifikat in einer Woche abläuft.
HINWEIS. Beachten Sie die Verwendung von parseInt() in der Funktion return, um die Dezimalzahl, die aus der Division der Millisekunden resultiert, in eine Ganzzahl umzuwandeln. Es kann auch verwendet werden parseFloat() und zur Speicherung von Dezimalwerten.
Quelle: habr.com
