Resolviendo problemas prácticos en Zabbix con JavaScript

Resolviendo problemas prácticos en Zabbix con JavaScript
Tikhon Uskov, ingeniero del equipo de integración de Zabbix

Zabbix es una plataforma personalizable utilizada para monitorear cualquier tipo de datos. Desde las primeras versiones de Zabbix, los administradores de monitoreo han tenido la posibilidad de ejecutar varios scripts a través de Actions para verificaciones en los nodos objetivo de la red. Sin embargo, la ejecución de scripts conllevaba una serie de complejidades, incluyendo la necesidad de soportar los scripts, su entrega a los nodos de comunicación y proxy, así como el mantenimiento de diferentes versiones.

JavaScript para Zabbix

En abril de 2019 se presentó Zabbix 4.2 con la función de preprocesamiento en JavaScript. Muchos se entusiasmaron con la idea de abandonar la escritura de scripts que recogen datos, los procesan y los presentan en un formato comprensible para Zabbix, y en su lugar ejecutar verificaciones simples que recibirían datos no listos para almacenar y procesar, y luego procesar este flujo de datos utilizando las herramientas de Zabbix y JavaScript. En conjunto con el descubrimiento de bajo nivel y los elementos de datos dependientes, que aparecieron en Zabbix 3.4, se logró un concepto lo suficientemente flexible para clasificar y gestionar los datos obtenidos.

En Zabbix 4.4, como continuación lógica del preprocesamiento en JavaScript, se introdujo una nueva forma de notificación: Webhook, que se puede utilizar para una integración sencilla de las alertas de Zabbix con aplicaciones externas.

JavaScript y Duktape

¿Por qué se eligieron específicamente JavaScript y Duktape? Se consideraron varias opciones de lenguajes y motores:

  • Lua – Lua 5.1
  • Lua – LuaJIT
  • Javascript – Duktape
  • Javascript – JerryScript
  • Python incrustado
  • Perl incrustado

Los principales criterios de selección fueron la prevalencia, la simplicidad de la integración del motor en el producto, el bajo consumo de recursos y el rendimiento general del motor, así como la seguridad en la implementación del código en este lenguaje en el monitoreo. En conjunto, JavaScript en el motor Duktape fue el ganador.

Resolviendo problemas prácticos en Zabbix con JavaScript

Criterios de selección y pruebas de rendimiento

Características de Duktape:

— Estándar ECMAScript E5/E5.1
— Módulos de Zabbix para Duktape:

  • Zabbix.log() — permite escribir directamente en el registro de Zabbix Server mensajes con diversos niveles de detalle, lo que facilita la correlación de errores, por ejemplo, en Webhook con el estado del servidor.
  • CurlHttpRequest() — permite realizar solicitudes HTTP a la red, fundamento de la aplicación de Webhook.
  • atob() y btoa() — permite codificar y decodificar cadenas en formato Base64.

NOTA. Duktape cumple con los estándares de ACME. En Zabbix se utiliza la versión del script de 2015. Los cambios posteriores son mínimos, por lo que se pueden ignorar..

La magia de JavaScript

Toda la magia de JavaScript radica en la tipificación dinámica y la conversión de tipos: cadenas, numéricas y lógicas.

Esto significa que no es necesario declarar de antemano qué tipo de valor debe devolver una variable.

En operaciones matemáticas, los valores devueltos por los operadores-funciones se convierten en números. La única excepción a estas operaciones es la suma, ya que si al menos uno de los sumandos es una cadena, se aplica la conversión a cadena a todos los sumandos.

NOTA. Los métodos responsables de tales conversiones suelen estar implementados en los prototipos parentales de los objetos. valueOf y toString. valueOf se invoca durante la conversión numérica y siempre antes del método toString. El método valueOf debe devolver valores primitivos, de lo contrario, su resultado se ignorará.

Para un objeto se invoca el método valueOf. Si no se encuentra o no devuelve un valor primitivo, se invoca el método toString. Si el método toString no se encuentra, se busca valueOf en el prototipo del objeto, y todo se repite hasta que se completa el procesamiento del valor y se convierte todos los valores en la expresión a un solo tipo.. Si para un objeto se ha implementado un método toString, que devuelve un valor primitivo, entonces este es el que se utiliza para la conversión a cadena. Sin embargo, el resultado de la aplicación de este método no necesariamente es una cadena.

Por ejemplo, si para el objeto ‘obj‘ se define el método toString,

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

método toString devuelve precisamente una cadena, y al sumar esta cadena con un número obtenemos una cadena concatenada:

`obj + 1 // '2001'` 

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

Pero si reescribimos toString, para que el método devuelva un número, al sumar el objeto se realizará una operación matemática con conversión numérica y se obtiene el resultado de la suma matemática.

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

`obj + 1 // '2001'`

Al mismo tiempo, si realizamos una suma con una cadena, se lleva a cabo la conversión a cadena y obtenemos una cadena concatenada.

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

De hecho, aquí radica la razón de muchos errores de los nuevos usuarios de JavaScript.

En el método toString se puede escribir una función que aumente el valor actual del objeto en 1.

Resolviendo problemas prácticos en Zabbix con JavaScript
Ejecutar el script en condiciones de que la variable sea 3, y también sea igual a 4.

Al comparar con la conversión de tipos (==), se ejecuta el método cada vez. toString con la función que incrementa el valor. Por lo tanto, en cada comparación subsiguiente, el valor aumenta. Esto se puede evitar utilizando la comparación sin conversión de tipos (===).

Resolviendo problemas prácticos en Zabbix con JavaScript
Comparación sin conversión de tipos

NOTA. No utilices la comparación con conversión de tipos innecesariamente.

Para scripts complejos, por ejemplo, Webhooks con lógica complicada, en los que la comparación con conversión de tipos es necesaria, se recomienda escribir previamente verificaciones para los valores que devuelven las variables y manejar las discrepancias y errores.

Webhook Media

A finales de 2019 y principios de 2020, el equipo de integración de Zabbix estuvo trabajando activamente en el desarrollo de Webhooks e integraciones 'listos para usar', que se incluyen en la distribución de Zabbix.

Resolviendo problemas prácticos en Zabbix con JavaScript
Enlace a la documentación

Preprocesamiento

  • La aparición del preprocesamiento en JavaScript ha permitido prescindir de la mayoría de los scripts externos, y en la actualidad en Zabbix se puede obtener cualquier valor y transformarlo en otro completamente diferente.
  • El preprocesamiento en Zabbix se implementa con código en JavaScript, que al compilarse en bytecode se convierte en una función que acepta un único valor como parámetro. value en forma de cadena (en la cadena puede haber tanto un dígito como un número).
  • Dado que al final se obtiene una función, al final del script es obligatorio. return.
  • Se puede usar macros personalizadas en el código.
  • Los recursos se pueden limitar no solo a nivel del sistema operativo, sino también programáticamente. Para el paso de preprocesamiento se asignan un máximo de 10 megabytes de memoria RAM y un límite de tiempo de ejecución de 10 segundos.

Resolviendo problemas prácticos en Zabbix con JavaScript

NOTA. Los valores de tiempo de espera de 10 segundos son bastante, porque recolectar miles de elementos de datos condicionales en 1 segundo con un escenario de preprocesamiento lo suficientemente 'pesado' puede ralentizar el funcionamiento de Zabbix. Por lo tanto, no se recomienda usar el preprocesamiento para ejecutar scripts completos en JavaScript a través de los llamados elementos de datos ficticios (dummy items) que se ejecutan solo para hacer el preprocesamiento..

Puedes verificar tu código a través de una prueba de preprocesamiento o utilizando la herramienta 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`

Tareas prácticas

Tarea 1

Reemplazar el elemento de datos calculado con un procesamiento previo.

Condición: obtenemos del sensor la temperatura en grados Fahrenheit para almacenarla en grados Celsius.

Antes crearíamos un elemento de datos que recoge la temperatura en grados Fahrenheit. Después, otro elemento de datos (calculado) que transformaría los grados Fahrenheit en grados Celsius mediante una fórmula.

Problemas:

  • Es necesario duplicar los elementos de datos y almacenar todos los valores en la base de datos.
  • Es necesario coordinar los intervalos para el elemento de datos 'padre', que se calcula y se usa en la fórmula, y para el elemento de datos calculado. De lo contrario, el elemento de datos calculado puede entrar en un estado no soportado o contar el valor anterior, lo que afectará la fiabilidad de los resultados de monitoreo.

Una de las soluciones fue renunciar a los intervalos de chequeo flexibles a favor de intervalos fijos, para garantizar que el elemento de datos calculado se calcule después del elemento de datos que recibe los datos (en nuestro caso, la temperatura en grados Fahrenheit).

Pero, si por ejemplo, utilizamos la plantilla para verificar una gran cantidad de dispositivos, y la verificación se realiza cada 30 segundos, durante 29 segundos Zabbix 'no hace nada', y en el último segundo comienza las verificaciones y cálculos. Esto provoca la creación de una cola y afecta al rendimiento. Por lo tanto, se recomienda usar intervalos fijos solo si es realmente necesario.

En esta tarea, la solución óptima es un preprocesamiento de una línea en JavaScript que convierte los grados Fahrenheit a grados Celsius:

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

Es rápido y simple, no es necesario crear elementos de datos adicionales y almacenar su historial, además se puede usar para chequeos con intervalos flexibles.

Resolviendo problemas prácticos en Zabbix con JavaScript

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

Pero, si en una situación hipotética es necesario sumar el elemento de datos obtenido, por ejemplo, con alguna constante definida en un macro, es necesario considerar que el parámetro value se expande en una cadena. En la operación de suma de cadenas, dos cadenas simplemente se concatenan en una.

Resolviendo problemas prácticos en Zabbix con JavaScript

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

Para obtener el resultado de una operación matemática, es necesario convertir los tipos de los valores obtenidos a formato numérico. Para ello, se puede utilizar la función parseInt(), que devuelve un número entero, la función parseFloat(), que devuelve un número decimal, o la función number, que devuelve un número entero o decimal.

Tarea 2

Obtener el tiempo en segundos hasta la expiración del certificado.

Condición: un cierto servicio devuelve la fecha de expiración del certificado en el formato "Feb 12 12:33:56 2022 GMT".

En ECMAScript5 ahora soporta formatos de fecha adicionales: , acepta la fecha en formato ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ). Es necesario convertir la cadena en formato MMM DD YYYY HH:mm:ss ZZ

Problema: el valor del mes se expresa en texto, no en número. Los datos en este formato no son aceptados por Duktape.

Ejemplo de solución:

  • Primero se declara una variable que toma el valor (todo el script es la declaración de variables, que se enumeran separadas por comas).

  • En la primera línea obtenemos la fecha del parámetro value y la dividimos en espacios utilizando el método split. De esta manera, obtenemos un array donde a cada elemento del array, comenzando desde el índice 0, le corresponde un elemento de la fecha antes y después del espacio. split(0) — mes, split(1) — día, split(2) — cadena con la hora, etc. Después de esto, cada elemento de la fecha se puede acceder por su índice en el array.

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

  • A cada mes (en orden cronológico) le corresponde el índice de su posición en el array (de 0 a 11). Para convertir el valor textual a numérico, se suma uno al índice del mes (ya que la numeración de los meses comienza en 1). En este caso, la expresión con la suma uno se coloca entre paréntesis, porque de lo contrario se obtendría una cadena, no un número. Al final realizamos slice() — un corte del array desde el final, para dejar solo dos caracteres (lo que es importante para los meses con un número de dos cifras).

`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),`

  • Formamos una cadena a partir de los valores obtenidos en formato ISO mediante una concatenación de cadenas en el orden correspondiente.

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

Los datos en el formato obtenido son la cantidad de segundos desde 1970 hasta algún momento en el futuro. Usar los datos en el formato obtenido en desencadenadores es prácticamente imposible, porque Zabbix solo permite operar con macros {Date} y {Time}, que devuelven la fecha y la hora en un formato comprensible para el usuario.

  • Después de eso, podemos obtener en JavaScript la fecha actual en formato Unix Timestamp y restarla del valor obtenido de la fecha de finalización del certificado, para obtener la cantidad de milisegundos desde este momento hasta el momento de finalización del certificado.

`now = Date.now();`

  • Dividimos el valor obtenido entre mil, para obtener segundos en Zabbix.

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

En el desencadenador se puede especificar la expresión ‘last<‘ y un conjunto de cifras que corresponde a la cantidad de segundos en el período al que se debe reaccionar, por ejemplo, en semanas. Así, el desencadenador alertará que la validez del certificado expira en una semana.

NOTA. Presta atención a la utilización de parseInt() en la función return, para convertir un número decimal obtenido como resultado de la división de milisegundos en un número entero. También se pueden usar parseFloat() y almacenar datos decimales.

Ver informe

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster