
Tikhon Uskov, ingénieur de l'équipe d'intégration Zabbix
Zabbix est une plateforme personnalisable utilisée pour surveiller toutes sortes de données. Dès les premières versions de Zabbix, les administrateurs de surveillance avaient la possibilité d'exécuter divers scripts via Actions pour des vérifications sur les nœuds cibles du réseau. Cependant, l'exécution de scripts posait un certain nombre de difficultés, notamment la nécessité de prendre en charge les scripts, de les transmettre aux nœuds de communication et aux proxy, ainsi que de gérer différentes versions.
JavaScript pour Zabbix
En avril 2019, Zabbix 4.2 a été lancé avec la fonctionnalité de prétraitement en JavaScript. Beaucoup ont été séduits par l'idée d'abandonner les scripts qui récupèrent des données, les traitent et les fournissent dans un format compréhensible par Zabbix, et de réaliser de simples vérifications qui allaient recevoir des données non prêtes pour le stockage et le traitement par Zabbix, puis de traiter ce flux de données en utilisant les outils de Zabbix et JavaScript. Associé à la détection de bas niveau et aux éléments de données dépendants, apparus dans Zabbix 3.4, cela a donné une conception suffisamment flexible pour trier et gérer les données reçues.
Dans Zabbix 4.4, comme extension logique du prétraitement en JavaScript, un nouveau moyen de notification est apparu : le Webhook, qui peut être utilisé pour une intégration simple des notifications Zabbix avec des applications tierces.
JavaScript et Duktape
Pourquoi ont-ils choisi JavaScript et Duktape ? Différents langages et moteurs ont été envisagés :
- Lua – Lua 5.1
- Lua – LuaJIT
- Javascript – Duktape
- Javascript – JerryScript
- Python embarqué
- Perl embarqué
Les principaux critères de sélection étaient la popularité, la facilité d'intégration du moteur dans le produit, la faible consommation de ressources et les performances globales du moteur, ainsi que la sécurité de l'intégration du code de ce langage dans la surveillance. En tenant compte de tous ces critères, JavaScript sur le moteur Duktape a remporté la victoire.

Critères de sélection et test de performance
Caractéristiques de Duktape :
— Standard
— Modules Zabbix pour Duktape :
- Zabbix.log() — permet d'écrire directement dans le log du serveur Zabbix des messages avec divers niveaux de détail, ce qui facilite la mise en correspondance des erreurs, par exemple dans Webhook, avec l'état du serveur.
- CurlHttpRequest() — permet d'effectuer des requêtes HTTP sur le réseau, ce qui est à la base de l'utilisation des Webhooks.
- atob() et btoa() — permettent de coder et de décoder des chaînes au format Base64.
REMARQUE. Duktape respecte les normes ACME. Dans Zabbix, la version du script utilisée est celle de 2015. Les changements ultérieurs sont minimes et peuvent donc être ignorés..
La magie de JavaScript
Toute la magie de JavaScript réside dans la typage dynamique et la conversion de types : chaînes de caractères, numériques et booléens.
Cela signifie qu'il n'est pas nécessaire de déclarer à l'avance le type que la variable doit retourner.
Lors des opérations mathématiques, les valeurs retournées par les opérateurs-fonctions sont converties en nombres. L'exception à de telles opérations est l'addition, car si au moins un des addends est une chaîne, la conversion en chaîne s'applique à tous les addends.
REMARQUE. Les méthodes responsables de ces conversions sont généralement implémentées dans les prototypes parent des objets. valueOf et toString. valueOf est appelée lors de la conversion numérique et se produit toujours avant la méthode toString. La méthode valueOf doit retourner des valeurs primitives, sinon son résultat est ignoré.
Pour un objet, la méthode valueOf. Si elle n'est pas trouvée ou ne retourne pas une valeur primitive, la méthode toString. Si la méthode toString n'est pas trouvée, une recherche dans le prototype de l'objet est effectuée et tout se répète jusqu'à ce que la valeur soit complètement traitée et que toutes les valeurs de l'expression soient ramenées à un seul type. valueOf Si un objet implémente la méthode. , qui retourne une valeur primitive, alors c'est elle qui est utilisée pour la conversion en chaîne. toStringLe résultat de l'application de cette méthode n'est pas nécessairement une chaîne. Par exemple, si pour l'objet '
objobj' une méthode est définie toString,
`var obj = { toString() { return "200" }}` méthode toString retourne exactement une chaîne, et lors de l'addition d'une chaîne avec un nombre, nous obtenons une chaîne concaténée :
`obj + 1 // '2001'`
`obj + 'a' // '200a'`Mais si nous reformulons toString, de sorte que la méthode retourne un nombre, lors de l'addition de l'objet, une opération mathématique avec une conversion numérique sera effectuée, ce qui donne le résultat d'une somme mathématique.
`var obj = { toString() { return 200 }}`
`obj + 1 // '2001'`Dans ce cas, si nous effectuons une addition avec une chaîne, la conversion en chaîne est effectuée, et nous obtenons une chaîne concaténée.
`obj + 'a' // '200a'`C'est précisément la raison pour laquelle un grand nombre d'erreurs se produisent parmi les utilisateurs débutants de JavaScript.
Le support pour le formatage des unités de mesure, des devises, des notations scientifiques et compactes a été ajouté à la méthode toString vous pouvez écrire une fonction qui augmentera la valeur actuelle de l'objet de 1.

Exécution du script si la variable est égale à 3 et qu'elle est également égale à 4.
Lors de la comparaison avec la conversion de types (==), la méthode est exécutée à chaque fois. toString avec la fonction d'augmentation de valeur. Par conséquent, à chaque comparaison suivante, la valeur augmente. Cela peut être évité en utilisant une comparaison sans conversion de types (===).

Comparaison sans conversion de types
REMARQUE. N'utilisez pas la comparaison avec conversion de types sans nécessité.
Pour les scripts complexes, par exemple, les Webhooks avec une logique complexe, où une comparaison avec conversion de types est nécessaire, il est recommandé de d'abord écrire des vérifications pour les valeurs renvoyées par les variables et de traiter les incohérences et les erreurs.
Webhook Media
À la fin de 2019 et au début de 2020, l'équipe d'intégration de Zabbix a travaillé activement sur le développement de Webhooks et d'intégrations "prêtes à l'emploi" fournies avec la distribution de Zabbix.

Lien vers
Prétraitement
- L'apparition du prétraitement en JavaScript a permis de se passer de la plupart des scripts externes, et il est désormais possible d'obtenir n'importe quelle valeur dans Zabbix et de la transformer en n'importe quelle autre valeur.
- Le prétraitement dans Zabbix est mis en œuvre par du code JavaScript, qui, lors de la compilation en bytecode, est transformé en une fonction prenant une seule valeur en tant que paramètre. value sous forme de chaîne (la chaîne peut contenir un chiffre ou un nombre).
- Étant donné qu'une fonction est produite en sortie, à la fin du script, il est obligatoire retourner.
- Il est possible d'utiliser des macros personnalisées dans le code.
- Les ressources peuvent être limitées non seulement au niveau du système d'exploitation, mais aussi de manière programmatique. Pour l'étape de prétraitement, un maximum de 10 mégaoctets de mémoire vive et une limite de temps d'exécution de 10 secondes sont alloués.

REMARQUE. Des valeurs de délai d'attente de 10 secondes sont suffisantes, car la collecte d'éventuels milliers d'éléments de données en 1 seconde dans un scénario de prétraitement relativement "lourd" peut ralentir Zabbix. Il est donc déconseillé d'utiliser le prétraitement pour exécuter de véritables scripts en JavaScript via les soi-disant éléments de données fictifs (dummy items), qui ne sont activés que pour effectuer le prétraitement..
Vous pouvez vérifier votre code via le test de prétraitement ou en utilisant l'outil. 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`
Tâches pratiques
La tâche 1
Remplacer l'élément de données calculé par un prétraitement.
Condition: obtenir la température d'un capteur en degrés Fahrenheit pour le stockage en degrés Celsius.
Auparavant, nous aurions créé un élément de données qui collecte la température en degrés Fahrenheit. Ensuite, nous aurions créé un autre élément de données (calculé) qui aurait converti les degrés Fahrenheit en degrés Celsius selon une formule.
Problèmes:
- Il est nécessaire de dupliquer les éléments de données et de stocker toutes les valeurs dans la base.
- Il est indispensable de synchroniser les intervalles pour l'élément de données « parent », qui est calculé et utilisé dans la formule, et pour l'élément de données calculé. Sinon, l'élément de données calculé peut entrer dans un état non pris en charge ou calculer la dernière valeur, ce qui affecte la fiabilité des résultats de surveillance.
L'une des solutions a été d'abandonner les intervalles de vérification flexibles au profit d'intervalles fixes, afin de garantir que l'élément de données calculé soit calculé après l'élément de données qui récupère les données (dans notre cas, la température en degrés Fahrenheit).
Mais si, par exemple, nous utilisons le modèle pour vérifier un grand nombre d'appareils et que la vérification a lieu toutes les 30 secondes, pendant 29 secondes, Zabbix 'ne fait rien', et dans la dernière seconde, commence les vérifications et les calculs. Cela crée une file d'attente et impacte les performances. Il est donc recommandé d'utiliser des intervalles fixes uniquement si cela est vraiment nécessaire.
Dans cette tâche, la solution optimale est un prétraitement en une ligne en JavaScript qui convertit les degrés Fahrenheit en degrés Celsius :
`return (value - 32) * 5 / 9;`C'est rapide et simple, il n'est pas nécessaire de créer d'éléments de données supplémentaires et de stocker leur historique, et cela peut aussi être utilisé pour des vérifications avec des intervalles flexibles.

`return (parseInt(value) + parseInt("{$EXAMPLE.MACRO}"));`Mais si, dans une situation hypothétique, il est nécessaire d'ajouter l'élément de données obtenu à une constante définie dans un macro, il faut prendre en compte que le paramètre value s'exprime sous forme de chaîne. Lors de l'opération d'addition de chaînes, les deux chaînes sont simplement concaténées en une seule.

`return (value + "{$EXAMPLE.MACRO}");`Pour obtenir le résultat d'une opération mathématique, il est nécessaire de convertir les types de valeurs obtenues en format numérique. Pour cela, vous pouvez utiliser la fonction parseInt(), qui renvoie un nombre entier, la fonction parseFloat(), qui renvoie un nombre décimal, ou la fonction number, qui renvoie un nombre entier ou décimal.
Tâche 2
Obtenir le temps en secondes avant l'expiration du certificat.
Condition: un certain service fournit la date d'expiration du certificat au format "Feb 12 12:33:56 2022 GMT".
En ECMAScript5 Date.parse() , prend la date au format ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ). Il faut convertir la chaîne au format MMM DD YYYY HH:mm:ss ZZ
Le problème: la valeur du mois est exprimée par du texte, et non par un nombre. Les données dans ce format ne sont pas acceptées par Duktape.
Exemple de solution:
Tout d'abord, une variable est déclarée, qui prend une valeur (tout le script consiste à déclarer des variables, qui sont séparées par des virgules).
Dans la première ligne, nous obtenons la date dans le paramètre value et la séparons par des espaces avec la méthode splits. De cette façon, nous obtenons un tableau, où chaque élément du tableau, à partir de l'index 0, correspond à un élément de la date avant et après l'espace. split(0) — mois, split(1) — jour, split(2) — chaîne avec l'heure, etc. Après cela, chaque élément de la date peut être accédé par son index dans le tableau.
`var split = value.split(' '),`- À chaque mois (dans l'ordre chronologique) correspond un index de sa position dans le tableau (de 0 à 11). Pour convertir la valeur textuelle en numérique, on ajoute un à l'index du mois (puisque la numérotation des mois commence à 1). Cette expression avec l'ajout d'un a été mise entre parenthèses, car sinon, on obtiendrait une chaîne plutôt qu'un nombre. À la fin, nous exécutons slice() — une coupe du tableau à la fin, pour ne garder que deux caractères (ce qui est important pour les mois avec des numéros à deux chiffres).
`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),`- Nous formons une chaîne dans le format ISO à partir des valeurs obtenues en concaténant des chaînes dans l'ordre approprié.
`ISOdate = split[3] + '-' + month_index + '-' + split[1] + 'T' + split[2],`Les données dans le format obtenu représentent le nombre de secondes depuis 1970 jusqu'à un certain moment dans le futur. Utiliser les données dans ce format dans des déclencheurs est pratiquement impossible, car Zabbix ne permet de travailler qu'avec des macros {Date} et {Time}, qui retourne la date et l'heure dans un format compréhensible pour l'utilisateur.
- Ensuite, nous pouvons obtenir en JavaScript la date actuelle au format Unix Timestamp et la soustraire de la valeur de la date d'expiration du certificat pour obtenir le nombre de millisecondes depuis maintenant jusqu'à l'expiration du certificat.
`now = Date.now();`- Nous divisons la valeur obtenue par mille pour obtenir les secondes dans Zabbix.
`return parseInt((Date.parse(ISOdate) - now) / 1000);`Dans le déclencheur, vous pouvez spécifier l'expression 'last<' et un ensemble de chiffres correspondant au nombre de secondes dans la période à laquelle il faut réagir, par exemple, en semaines. Ainsi, le déclencheur avertira que la validité du certificat expire dans une semaine.
REMARQUE. Faites attention à l'utilisation de parseInt() dans la fonction retourner, pour convertir le nombre décimal obtenu à partir de la division des millisecondes en un entier. Vous pouvez aussi utiliser parseFloat() et stocker des données décimales..
Source : habr.com
