
Tikhon Uskov, Ingenieur des Zabbix-Integrationsteams
Zabbix ist eine anpassbare Plattform, die zur Überwachung beliebiger Daten verwendet wird. Seit den frühesten Versionen von Zabbix hatten die Monitoring-Administratoren die Möglichkeit, verschiedene Skripte über Aktionen zur Überprüfung auf den Zielknoten im Netzwerk auszuführen. Dabei führte die Ausführung von Skripten zu einer Reihe von Schwierigkeiten, darunter die Notwendigkeit der Unterstützung von Skripten, ihrer Bereitstellung an Kommunikationsknoten und Proxys sowie die Unterstützung verschiedener Versionen.
JavaScript für Zabbix
Im April 2019 wurde Zabbix 4.2 mit einer Vorverarbeitungsfunktion in JavaScript vorgestellt. Viele begannen, die Idee zu verfolgen, auf das Schreiben von Skripten zu verzichten, die irgendwo Daten abrufen, sie verarbeiten und bereits in einem für Zabbix verständlichen Format bereitstellen, und stattdessen einfache Überprüfungen durchzuführen, die nicht für Zabbix bereitgestellte Daten erhalten und dann diesen Datenstrom mit den Mitteln von Zabbix und JavaScript verarbeiten. Zusammen 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, als logische Fortsetzung der Vorverarbeitung in JavaScript, wurde eine neue Benachrichtigungsmethode – Webhook – eingeführt, die zur einfachen Integration von Zabbix-Benachrichtigungen mit Drittanwendungen verwendet werden kann.
JavaScript und Duktape
Warum wurden gerade JavaScript und Duktape gewählt? Verschiedene Optionen für Sprachen und Engines wurden in Betracht gezogen:
- Lua – Lua 5.1
- Lua – LuaJIT
- JavaScript – Duktape
- JavaScript – JerryScript
- Eingebettetes Python
- Eingebettetes Perl
Die Hauptkriterien für die Auswahl waren Verbreitung, einfache Integration der Engine in das Produkt, geringer Ressourcenverbrauch sowie die allgemeine Leistungsfähigkeit der Engine und die Sicherheit der Code-Einführung in dieser Sprache im Monitoring. In der Gesamtheit der Kriterien gewann JavaScript auf der Engine Duktape.

Auswahlkriterien und Leistungstests
Besonderheiten von Duktape:
— Standard
— Zabbix-Module für Duktape:
- Zabbix.log() – ermöglicht das direkte Eintragen von Nachrichten mit verschiedenen Detaillierungsgraden in das Zabbix-Server-Log, was die Möglichkeit bietet, beispielsweise Fehler im Webhook mit dem Zustand des Servers zu vergleichen.
- CurlHttpRequest() – ermöglicht HTTP-Anfragen über das Netzwerk, auf dem die Anwendung von Webhook basiert.
- atob() und btoa() – ermöglichen das Kodieren und Dekodieren von Zeichenfolgen im Base64-Format.
HINWEIS. Duktape entspricht den ACME-Standards. In Zabbix wird die Skriptversion von 2015 verwendet. Nachfolgende Änderungen sind geringfügig und können daher ignoriert werden..
Die Magie von JavaScript
Die gesamte Magie von JavaScript liegt in der dynamischen Typisierung und Typumwandlung: von Zeichenfolgen, Zahlen und logischen Werten.
Das bedeutet, dass man den Typ der Variablen, die einen Wert zurückgeben soll, nicht im Voraus deklarieren muss.
Bei mathematischen Operationen werden die Werte, die von Funktionsoperatoren zurückgegeben werden, in Zahlen umgewandelt. Eine Ausnahme bilden hier die Addition, denn wenn eines der Summanden eine Zeichenfolge ist, wird für alle Summanden eine Zeichenfolgenumwandlung angewendet.
HINWEIS. Die Methoden, die für solche Umwandlungen verantwortlich sind, sind in der Regel in den Elternprototypen von Objekten implementiert, valueOf und toString. valueOf wird bei der numerischen Umwandlung aufgerufen und immer vor der Methode toString. Die Methode valueOf muss primitive Werte zurückgeben, sonst wird ihr Ergebnis ignoriert.
Für das Objekt wird die Methode valueOF. Wenn sie nicht gefunden wird oder keinen primitiven Wert zurückgibt, wird die Methode toString. Wenn die Methode toString nicht gefunden wird, erfolgt die Suche valueOf im Prototyp des Objekts, und alles wiederholt sich, bis die Verarbeitung des Wertes abgeschlossen ist und alle Werte im Ausdruck auf einen Typ umgewandelt werden.. Wenn für das Objekt eine Methode toStringimplementiert ist, die einen primitiven Wert zurückgibt, wird genau diese für die Zeichenfolgenumwandlung verwendet. Dabei ist das Ergebnis dieser Methode nicht unbedingt eine Zeichenfolge.
Zum Beispiel, wenn für das Objekt ‘obj' die Methode toString,
`var obj = { toString() { return "200" }}` Methode toString tatsächlich eine Zeichenfolge zurückgibt, und bei der Addition der Zeichenfolge mit einer Zahl erhalten wir eine verkettete Zeichenfolge:
`obj + 1 // '2001'`
`obj + 'a' // '200a'`Wenn wir jedoch die Methode umschreiben, damit sie eine Zahl zurückgibt, wird bei der Addition des Objekts eine mathematische Operation mit numerischer Umwandlung durchgeführt, und es ergibt sich das Resultat der mathematischen Addition. toString`var obj = { toString() { return 200 }}``obj + 1 // '2001'`
Dabei erfolgt bei der Addition mit einer Zeichenfolge eine Zeichenfolgenumwandlung, und wir erhalten eine verkettete Zeichenfolge.`obj + 'a' // '200a'`
Das ist der Grund für die vielen Fehler, die Anfänger in JavaScript machen.Es kann eine Funktion eingefügt werden, die den aktuellen Wert des Objekts um 1 erhöht.
In die Methode toString Das Skript wird ausgeführt, wenn die Variable gleich 3 ist, und sie ist auch gleich 4.

Die Ausführung wird erleichtert, wenn die Variable 3 ist und gleichzeitig 4 ist.
Beim Vergleich mit Typumwandlung (==) wird jedes Mal die Methode ausgeführt toString mit der Funktion zur Erhöhung des Wertes. Folglich erhöht sich der Wert bei jedem weiteren Vergleich. Dies kann vermieden werden, indem der Vergleich ohne Typumwandlung (===) verwendet wird.

Vergleich ohne Typumwandlung
HINWEIS. Verwenden Sie den Vergleich mit Typumwandlung nicht ohne Notwendigkeit.
Für komplexe Skripte, wie zum Beispiel Webhook mit komplexer Logik, in denen ein Vergleich mit Typumwandlung erforderlich ist, wird empfohlen, vorab Überprüfungen für die Werte zu schreiben, die Variablen zurückgeben, und Abweichungen sowie Fehler zu behandeln.
Webhook Media
Ende 2019 und Anfang 2020 war das Zabbix-Integrationsteam aktiv mit der Entwicklung von Webhooks und „Out-of-the-Box“-Integrationen beschäftigt, die im Zabbix-Release enthalten sind.

Link zu
Vorverarbeitung
- Durch das Auftreten der Vorverarbeitung in JavaScript war es möglich, auf die meisten externen Skripte zu verzichten, und derzeit kann in Zabbix jedes beliebige Wert erhalten und in einen völlig anderen Wert umgewandelt werden.
- Die Vorverarbeitung in Zabbix wird durch JavaScript-Code realisiert, der beim Kompilieren in Bytecode in eine Funktion umgewandelt wird, die einen einzigen Wert als Parameter akzeptiert value in Form eines Strings (im String können sowohl Ziffern als auch Zahlen enthalten sein).
- Da am Ende eine Funktion entsteht, ist am Ende des Skripts eine return.
- Verwendung von benutzerdefinierten Makros im Code möglich.
- Ressourcen können nicht nur auf Betriebssystemebene, sondern auch programmgesteuert eingeschränkt werden. Für den Schritt der Vorverarbeitung werden maximal 10 Megabyte Arbeitsspeicher und eine Ausführungszeit von 10 Sekunden zugewiesen.

HINWEIS. Die Timeout-Werte von 10 Sekunden sind ziemlich hoch, da das Sammeln von tausenden von Datenpunkten in 1 Sekunde bei einem relativ „schweren“ Vorverarbeitungsszenario die Leistung von Zabbix verlangsamen kann. Daher wird nicht empfohlen, die Vorverarbeitung zur Ausführung vollwertiger JavaScript-Skripte über sogenannte Dummy-Datenpunkte zu verwenden, die nur zur Durchführung der Vorverarbeitung ausgeführt werden..
Sie können Ihren Code durch den Test der Vorverarbeitung oder mit dem Dienstprogramm 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
Aufgabe 1
Ersetzen Sie das berechnete Datenelement durch preprocessing.
Bedingung: von einem Sensor erhalten wir die Temperatur in Grad Fahrenheit, um sie in Grad Celsius zu speichern.
Früher hätten wir ein Datenelement erstellt, das die Temperatur in Grad Fahrenheit erfasst. Danach — ein weiteres Datenelement (berechnet), das mittels einer Formel Fahrenheit in Celsius umwandelt.
Probleme:
- Es ist notwendig, die Datenelemente zu duplizieren und alle Werte in der Datenbank zu speichern.
- Es ist notwendig, die Intervalle für das "Elterndatenelement", das berechnet wird und in der Formel verwendet wird, und für das berechnete Datenelement abzustimmen. Andernfalls kann das berechnete Datenelement in einen nicht unterstützten Zustand übergehen oder den vorherigen Wert berechnen, was die Zuverlässigkeit der Überwachungsergebnisse beeinträchtigt.
Einer der Lösungsansätze war der Verzicht auf flexible Prüfintervalle zugunsten von festen Intervallen, um sicherzustellen, dass das berechnete Datenelement nach dem Datenelement, das die Daten erhält (in unserem Fall die Temperatur in Grad Fahrenheit), berechnet wird.
Aber wenn wir beispielsweise die Vorlage für die Überprüfung einer großen Anzahl von Geräten verwenden und die Überprüfung alle 30 Sekunden erfolgt, dann "schlampt" Zabbix 29 Sekunden lang und beginnt in der letzten Sekunde mit den Überprüfungen und Berechnungen. Dies führt zur Bildung einer Warteschlange und wirkt sich auf die Leistung aus. Daher wird empfohlen, feste Intervalle nur zu verwenden, wenn dies wirklich notwendig ist.
In dieser Aufgabe ist die optimale Lösung ein preprocessing aus einer Zeile in JavaScript, das Grad Fahrenheit in Grad Celsius konvertiert:
`return (value - 32) * 5 / 9;`Es ist schnell und einfach, es müssen keine zusätzlichen Datenelemente erstellt und deren Historie gespeichert werden, und es kann auch für flexible Prüfintervalle verwendet werden.

`return (parseInt(value) + parseInt("{$EXAMPLE.MACRO}"));`Aber wenn in einem hypothetischen Szenario das erhaltene Datenelement beispielsweise mit einer bestimmten Konstante, die im Makro festgelegt ist, addiert werden muss, ist zu beachten, dass der Parameter value in eine Zeichenkette aufgelöst wird. Bei der Addition von Zeichenketten werden zwei Zeichenketten einfach zu einer zusammengefügt.

`return (value + "{$EXAMPLE.MACRO}");`Um das Ergebnis einer mathematischen Operation zu erhalten, müssen die Typen der erhaltenen Werte in ein numerisches Format konvertiert werden. Dazu kann die Funktion parseInt(), die eine ganze Zahl zurückgibt, die Funktion parseFloat(), die eine Dezimalzahl zurückgibt, oder die Funktion number, die entweder eine ganze Zahl oder eine Dezimalzahl zurückgibt.
Aufgabe 2
Die Zeit in Sekunden bis zum Ablauf des Zertifikats erhalten.
Bedingung: Ein Dienst gibt das Ablaufdatum des Zertifikats im Format "Feb 12 12:33:56 2022 GMT" aus.
In ECMAScript5 Date.parse() akzeptiert das Datum im ISO 8601-Format (YYYY-MM-DDTHH:mm:ss.sssZ). Es ist notwendig, den String im Format MMM DD YYYY HH:mm:ss ZZ
Problem: Der Monat wird textuell dargestellt, nicht als Zahl. Daten in diesem Format werden von Duktape nicht akzeptiert.
Beispiel für eine Lösung:
Zuerst wird eine Variable deklariert, die einen Wert annimmt (das gesamte Skript besteht aus der Deklaration von Variablen, die durch Kommas getrennt sind).
In der ersten Zeile erhalten wir das Datum im Parameter value und teilen es durch Leerzeichen mittels der Methode Splits. Auf diese Weise erhalten wir ein Array, bei dem jedem Element des Arrays, beginnend mit Index 0, ein Datumselement vor und nach dem Leerzeichen entspricht. split(0) — Monat, split(1) — Tag, split(2) — Zeile mit der Uhrzeit usw. Danach können wir auf jedes Datumselement im Array über den Index zugreifen.
`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 1 zum Index des Monats addiert (weil die Nummerierung der Monate bei 1 beginnt). Dabei wird der Ausdruck mit der Addition von 1 in Klammern gesetzt, da sonst eine Zeichenkette und nicht eine Zahl erhalten wird. Am Ende führen wir slice() aus, um das Array von hinten zu kürzen, sodass nur zwei Zeichen übrig bleiben (was für Monate mit zweistelliger Zahl wichtig ist).
`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),`- Aus den erhaltenen Werten wird eine Zeichenkette im ISO-Format durch einfaches Verketten der Zeichenketten in der entsprechenden Reihenfolge gebildet.
`ISOdate = split[3] + '-' + month_index + '-' + split[1] + 'T' + split[2],`Die Daten im erhaltenen Format stellen die Anzahl der Sekunden seit 1970 bis zu einem bestimmten Zeitpunkt in der Zukunft dar. Es ist nahezu unmöglich, die Daten im erhaltenen Format in Triggern zu verwenden, da Zabbix nur mit Makros arbeiten kann. {Date} und {Time}, die Datum und Uhrzeit in einem benutzerfreundlichen Format zurückgeben.
- Danach können wir in JavaScript das aktuelle Datum im Unix-Timestamp-Format erhalten und es vom erhaltenen Enddatum des Zertifikats abziehen, um die Anzahl der Millisekunden vom aktuellen Zeitpunkt bis zum Ablauf des Zertifikats zu ermitteln.
`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<‘ und eine Zahl angegeben werden, die der Anzahl der Sekunden im Zeitraum entspricht, auf den reagiert werden muss, beispielsweise in Wochen. So wird der Trigger benachrichtigen, dass die Gültigkeit des Zertifikats in einer Woche abläuft.
HINWEIS. Beachten Sie die Verwendung von parseInt() in der Funktion return, um die resultierende Fließkommazahl aus der Division der Millisekunden in eine Ganzzahl zu konvertieren. Es können auch parseFloat() und Fließkommadaten gespeichert werden..
Quelle: habr.com
