Resolvendo problemas práticos no Zabbix com JavaScript

Resolvendo problemas práticos no Zabbix com JavaScript
Tikhon Uskov, engenheiro da equipe de integração do Zabbix

Zabbix é uma plataforma personalizável usada para monitorar qualquer tipo de dados. Desde as primeiras versões, os administradores de monitoramento tiveram a capacidade de executar vários scripts através de Ações para verificações em nós-alvo na rede. Entretanto, a execução de scripts levou a uma série de dificuldades, incluindo a necessidade de suportar scripts, a sua entrega nos nós de comunicação e proxy, assim como o suporte a diferentes versões.

JavaScript para Zabbix

Em abril de 2019, o Zabbix 4.2 foi apresentado com uma função de pré-processamento em JavaScript. Muitos se entusiasmaram com a ideia de abrir mão da criação de scripts que coletam dados de algum lugar, processam-nas e as disponibilizam em um formato compreensível para o Zabbix, optando por realizar verificações simples que receberiam dados ainda não formatados para armazenamento e processamento pelo Zabbix e depois processariam esse fluxo de dados utilizando os recursos do Zabbix e JavaScript. Em conjunto com a detecção de nível inferior e elementos de dados dependentes que apareceram no Zabbix 3.4, surgiu uma concepção bastante flexível para filtrar e gerenciar os dados recebidos.

No Zabbix 4.4, como uma continuidade lógica do pré-processamento em JavaScript, surgiu um novo método de notificação — Webhook, que pode ser usado para uma integração simples de notificações do Zabbix com aplicativos de terceiros.

JavaScript e Duktape

Por que JavaScript e Duktape foram escolhidos? Vários idiomas e mecanismos foram considerados:

  • Lua - Lua 5.1
  • Lua - LuaJIT
  • JavaScript - Duktape
  • JavaScript - JerryScript
  • Python embutido
  • Perl embutido

Os principais critérios de seleção foram a prevalência, a simplicidade de integração do mecanismo no produto, o baixo consumo de recursos e o desempenho geral do mecanismo, além da segurança na implementação de código nessa linguagem no monitoramento. De acordo com a soma de critérios, o JavaScript no mecanismo Duktape foi o vencedor.

Resolvendo problemas práticos no Zabbix com JavaScript

Critérios de seleção e performance testing

Características do Duktape:

— Padrão ECMAScript E5/E5.1
— Módulos Zabbix para Duktape:

  • Zabbix.log() — permite gravar diretamente no log do Zabbix Server mensagens com diferentes níveis de detalhamento, o que possibilita correlacionar erros, por exemplo, no Webhook, com o estado do servidor.
  • CurlHttpRequest() — permite fazer requisições HTTP na rede, sobre o que se baseia a aplicação do Webhook.
  • atob() e btoa() — permite codificar e decodificar strings no formato Base64.

NOTA. Duktape atende aos padrões ACME. O Zabbix usa a versão do script de 2015. As alterações subsequentes são insignificantes, portanto podem ser ignoradas..

A Magia do JavaScript

Toda a magia do JavaScript reside na tipagem dinâmica e nas conversões de tipos: string, numéricas e booleanas.

Isso significa que não é necessário declarar antecipadamente qual tipo de variável deve retornar um valor.

Em operações matemáticas, os valores retornados pelos operadores-funções são convertidos em números. A exceção a tais operações é a adição, uma vez que, se pelo menos um dos summands for uma string, a conversão em string é aplicada a todos os summands.

NOTA. Os métodos responsáveis por tais conversões geralmente estão implementados nos protótipos pais dos objetos, valueOf e toString. valueOf é chamado durante a conversão numérica e sempre antes do método toString. O método valueOf deve retornar valores primitivos; caso contrário, seu resultado é ignorado.

Para um objeto, o método valueOF. Se não for encontrado ou não retornar um valor primitivo, o método toString. Se o método toString não for encontrado, procura-se valueOf no protótipo do objeto, e todo o processo se repete até que o valor seja processado e todos os valores na expressão sejam convertidos para um só tipo.. Se um método é implementado para o objeto toString, que retorna um valor primitivo, este será usado para a conversão em string. Entretanto, o resultado da aplicação desse método não precisa ser uma string.

Por exemplo, se para o objeto ‘obj‘ é definido o método toString,

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

o método toString retorna exatamente uma string, e ao somar uma string com um número obtemos uma string concatenada:

`obj + 1 // '2001'` 

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

Mas se reescrevermos toString, de forma que o método retorne um número, ao somar o objeto será realizada a operação matemática com a conversão numérica, resultando na soma matemática.

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

`obj + 1 // '2001'`

Dessa forma, se realizarmos a soma com uma string, ocorrerá a conversão em string, e obteremos uma string concatenada.

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

É exatamente isso que explica a grande quantidade de erros cometidos por iniciantes em JavaScript.

No método toString , é possível inserir uma função que aumentará o valor atual do objeto em 1.

Resolvendo problemas práticos no Zabbix com JavaScript
Executar o script se a variável for igual a 3 e também igual a 4.

Ao comparar com a conversão de tipos (==), o método é chamado toda vez. toString com a função de incremento. Assim, a cada comparação subsequente, o valor aumenta. Isso pode ser evitado usando comparação sem conversão de tipos (===).

Resolvendo problemas práticos no Zabbix com JavaScript
Comparação sem conversão de tipos

NOTA. Não use comparação com conversão de tipos sem necessidade.

Para scripts complexos, como Webhook com lógica complexa, onde a comparação com conversão de tipos é necessária, é recomendável escrever verificações antecipadas para os valores retornados pelas variáveis e lidar com inconsistências e erros.

Webhook Media

No final de 2019 e no início de 2020, a equipe de integração do Zabbix esteve ativamente desenvolvendo Webhooks e integrações "prontas para uso", que são entregues na distribuição do Zabbix.

Resolvendo problemas práticos no Zabbix com JavaScript
Link para documentação

Pré-processamento

  • A introdução do pré-processamento em JavaScript permitiu abrir mão da maioria dos scripts externos, e atualmente no Zabbix é possível obter qualquer valor e transformá-lo em qualquer outro valor.
  • O pré-processamento no Zabbix é implementado com código JavaScript, que ao ser compilado em bytecode se torna uma função que aceita um único valor como parâmetro valor na forma de string (a string pode conter tanto um dígito quanto um número).
  • Como no final se obtém uma função, no final do script é obrigatório return.
  • É possível utilizar macros personalizadas no código.
  • Os recursos podem ser limitados não só no nível do sistema operacional, mas também programaticamente. Para o passo de pré-processamento, é limitado a um máximo de 10 megabytes de memória RAM e um limite de tempo de execução de 10 segundos.

Resolvendo problemas práticos no Zabbix com JavaScript

NOTA. Os valores de timeout de 10 segundos são bastante amplos, pois coletar milhares de elementos de dados em 1 segundo em um cenário de pré-processamento relativamente "pesado" pode desacelerar o funcionamento do Zabbix. Portanto, não é recomendável usar pré-processamento para executar scripts completos em JavaScript através dos chamados elementos de dados fantasma (dummy items), que são executados apenas para realizar o pré-processamento..

Você pode verificar seu código através do teste de pré-processamento ou usando a ferramenta 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`

Tarefas práticas

Tarefa 1

Substituir o elemento de dados calculado por pré-processamento.

Condição: obtemos a temperatura do sensor em graus Fahrenheit para armazená-la em graus Celsius.

Anteriormente, teríamos criado um elemento de dados que coleta a temperatura em graus Fahrenheit. Depois disso, outro elemento de dados (calculado) que, por meio de uma fórmula, converteria graus Fahrenheit em graus Celsius.

Problemas:

  • É necessário duplicar os elementos de dados e armazenar todos os valores no banco de dados.
  • É preciso alinhar os intervalos para o elemento de dados "pai", que é calculado e utilizado na fórmula, e para o elemento de dados calculado. Caso contrário, o elemento de dados calculado pode entrar em um estado não suportado ou calcular o valor anterior, o que afetará a confiabilidade dos resultados de monitoramento.

Uma das soluções foi abrir mão de intervalos de verificação flexíveis em favor de intervalos fixos, para garantir que o elemento de dados calculado seja calculado após o elemento de dados de coleta (neste caso — temperatura em graus Fahrenheit).

Mas se, por exemplo, estamos usando o template para verificar um grande número de dispositivos, e a verificação é feita a cada 30 segundos, durante 29 segundos o Zabbix "está de folga", e no último segundo começa as verificações e cálculos. Isso leva à formação de uma fila e afeta o desempenho. Portanto, recomenda-se usar intervalos fixos, somente se realmente necessário.

Nesta tarefa, a solução ideal é um pré-processamento em uma linha em JavaScript que converte graus Fahrenheit em graus Celsius:

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

É rápido e simples, não é necessário criar elementos de dados adicionais e manter um histórico deles, e também pode ser usado para verificações com intervalos flexíveis.

Resolvendo problemas práticos no Zabbix com JavaScript

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

Mas se, em uma situação hipotética, for necessário somar o elemento de dados obtido, por exemplo, com alguma constante definida em um macro, deve-se considerar que o parâmetro valor é expresso em string. Ao realizar a operação de soma de strings, duas strings simplesmente são concatenadas em uma só.

Resolvendo problemas práticos no Zabbix com JavaScript

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

Para obter o resultado de uma operação matemática, é necessário converter os tipos de valores obtidos para um formato numérico. Para isso, pode-se utilizar a função parseInt(), que retorna um número inteiro, a função parseFloat(), que retorna um número decimal, ou a função number, que retorna um número inteiro ou decimal.

Tarefa 2

Obter o tempo em segundos até a expiração do certificado.

Condição: um determinado serviço fornece a data de expiração do certificado no formato "Feb 12 12:33:56 2022 GMT".

Em ECMAScript5 Date.parse() aceita a data no formato ISO 8601 (YYYY-MM-DDTHH:mm:ss.sssZ). É necessário converter a string para o formato MMM DD YYYY HH:mm:ss ZZ

O problema: o valor do mês é expresso por texto, e não por número. Dados nesse formato não são aceitos pelo Duktape.

Exemplo de solução:

  • Primeiramente, declara-se uma variável que recebe o valor (todo o script consiste em declarações de variáveis, que são listadas separadas por vírgula).

  • Na primeira linha, obtemos a data no parâmetro valor e a dividimos em espaços usando o método split. Assim, obtemos um array, onde a cada elemento do array, começando do índice 0, corresponde um elemento da data antes e depois do espaço. split(0) — mês, split(1) — dia, split(2) — string com a hora e assim por diante. Depois disso, cada elemento da data pode ser acessado pelo índice no array.

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

  • A cada mês (em ordem cronológica) corresponde um índice de sua posição no array (de 0 a 11). Para converter o valor textual em numérico, adiciona-se um ao índice do mês (porque a numeração dos meses começa em 1). Nesse caso, a expressão com a adição do um é colocada entre parênteses, pois, caso contrário, obteremos uma string em vez de um número. No final, realizamos slice() — um corte no array do final, para deixar apenas dois caracteres (o que é importante para meses com números de dois dígitos).

`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 a partir dos valores obtidos uma string no formato ISO através da soma das strings na ordem correspondente.

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

Os dados no formato obtido representam o número de segundos desde 1970 até algum momento no futuro. Usar os dados obtidos nos gatilhos é praticamente impossível, pois o Zabbix só permite operar com macros {Date} e {Time}, que retornam a data e a hora em um formato compreensível para o usuário.

  • Depois disso, podemos obter a data atual em JavaScript no formato Unix Timestamp e subtrair isso do valor obtido da data de expiração do certificado, para obter o número de milissegundos desde o momento atual até a expiração do certificado.

`now = Date.now();`

  • Dividimos o valor obtido por mil, para obter segundos no Zabbix.

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

No gatilho, pode-se especificar a expressão ‘last<‘ e um conjunto de números que corresponde ao número de segundos no período em que precisamos reagir, por exemplo, em semanas. Assim, o gatilho notificará que a validade do certificado termina em uma semana.

NOTA. Preste atenção no uso de parseInt() na função return, para converter o número fracionário resultante da divisão em um número inteiro. Também é possível usar parseFloat() e armazenar dados fracionários.

Assistir à apresentação

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster