Resolvemos problemas práticos no Zabbix usando JavaScript
Tikhon Uskov, Engenheiro da equipe de integração do Zabbix
O Zabbix é uma plataforma personalizável que é usada para monitorar qualquer tipo de dados. Desde as primeiras versões do Zabbix, os administradores de monitoramento têm a capacidade de executar vários scripts via Opções para verificações nos nós da rede de destino. Ao mesmo tempo, o lançamento de scripts trouxe uma série de dificuldades, incluindo a necessidade de suporte a scripts, sua entrega a nós de comunicação e proxies, bem como suporte para diferentes versões.
JavaScript para Zabbix
Em abril de 2019, o Zabbix 4.2 foi introduzido com pré-processamento de JavaScript. Muitas pessoas ficaram empolgadas com a ideia de abandonar a escrita de scripts que pegam os dados em algum lugar, digerem e disponibilizam em um formato que o Zabbix entenda, e fazem verificações simples que vão receber dados que não estão prontos para armazenamento e processamento pelo Zabbix, e em seguida, processe esse fluxo de dados usando as ferramentas Zabbix e JavaScript. Em conjunto com a descoberta de baixo nível e itens dependentes que apareceram no Zabbix 3.4, obtivemos um conceito bastante flexível para classificar e gerenciar os dados recebidos.
No Zabbix 4.4, como uma continuação lógica do pré-processamento em JavaScript, surgiu um novo método de notificação - Webhook, que pode ser usado para integrar facilmente as notificações do Zabbix com aplicativos de terceiros.
JavaScript e Duktapes
Por que JavaScript e Duktape foram escolhidos? Várias opções de idiomas e motores foram consideradas:
Lua - Lua 5.1
Lua - LuaJIT
Javascript - Duktape
JavaScript – JerryScript
Python embutido
Perl embutido
Os principais critérios de seleção foram prevalência, facilidade de integração do motor no produto, baixo consumo de recursos e desempenho geral do motor, e a segurança de introduzir o código nesta linguagem no monitoramento. Com base na combinação de indicadores, o JavaScript venceu no mecanismo Duktape.
Zabbix.log() - permite escrever mensagens com diferentes níveis de detalhamento diretamente no log do Zabbix Server, o que possibilita correlacionar erros, por exemplo, em um Webhook, com o estado do servidor.
CurlHttpRequest() - permite fazer solicitações HTTP à rede, nas quais se baseia o uso do Webhook.
atob() e btoa() - permite codificar e decodificar strings no formato Base64.
ПРИМЕЧАНИЕ. Duktape está em conformidade com os padrões ACME. O Zabbix usa a versão 2015 do script. As alterações subseqüentes são pequenas, portanto, podem ser ignoradas..
magia do JavaScript
Toda a magia do JavaScript está na digitação dinâmica e na conversão de tipos: string, numérico e booleano.
Isso significa que não é necessário declarar antecipadamente qual tipo a variável deve retornar um valor.
Nas operações matemáticas, os valores retornados pelos operadores de função são convertidos em números. A exceção a essas operações é a adição, porque se pelo menos um dos termos for uma string, a conversão de string será aplicada a todos os termos.
ПРИМЕЧАНИЕ. Os métodos responsáveis por tais transformações são geralmente implementados nos protótipos pais do objeto, valor de и para sequenciar. valor de chamado durante a conversão numérica e sempre antes do método para sequenciar. Método valor de deve retornar valores primitivos, caso contrário seu resultado é ignorado.
Um método é chamado em um objeto valor de. Caso não seja encontrado ou não retorne um valor primitivo, o método é chamado para sequenciar. Se o método para sequenciar não encontrado, procurando valor de no protótipo do objeto, e tudo se repete até que o processamento do valor seja concluído e todos os valores na expressão sejam convertidos para o mesmo tipo. Se o objeto implementa um método para sequenciar, que retorna um valor primitivo, é ele que é usado para conversão de string.No entanto, o resultado da aplicação desse método não é necessariamente uma string.
Por exemplo, se for for objeto 'vol'método é definido para sequenciar,
`var obj = { toString() { return "200" }}`
método para sequenciar retorna exatamente uma string e, ao adicionar uma string com um número, obtemos uma string colada:
`obj + 1 // '2001'`
`obj + 'a' // ‘200a'`
Mas se você reescrever para sequenciar, de forma que o método retorne um número, ao somar o objeto será realizada uma operação matemática com conversão numérica e será obtido o resultado da adição matemática.
Nesse caso, se fizermos uma adição com uma string, uma conversão de string é realizada e obtemos uma string colada.
`obj + 'a' // ‘200a'`
Esse é o motivo de um grande número de erros cometidos por usuários iniciantes de JavaScript.
O método para sequenciar você pode escrever uma função que aumentará o valor atual do objeto em 1.
Execução do script, desde que a variável seja igual a 3, e também igual a 4.
Quando comparado com um elenco (==), o método é executado cada vez para sequenciar com função de aumento de valor. Assim, a cada comparação subsequente, o valor aumenta. Isso pode ser evitado usando comparação não convertida (===).
Comparação sem fundição de tipo
ПРИМЕЧАНИЕ. Não use comparações de elenco desnecessariamente.
Para scripts complexos, como Webhooks com lógica complexa, que requerem comparação com conversão de tipo, é recomendável pré-escrever verificações para os valores que retornam variáveis e tratam inconsistências e erros.
Mídia Webhook
No final de 2019 e início de 2020, a equipe de integração do Zabbix desenvolveu ativamente Webhooks e integrações prontas para uso que acompanham a distribuição do Zabbix.
O advento do pré-processamento em JavaScript possibilitou o abandono da maioria dos scripts externos e, atualmente, no Zabbix, você pode obter qualquer valor e convertê-lo em um valor completamente diferente.
O pré-processamento no Zabbix é implementado pelo código JavaScript, que, ao ser compilado em bytecode, é convertido em uma função que recebe um único valor como parâmetro valor como uma string (uma string pode conter um dígito e um número).
Como a saída é uma função, no final do script é obrigatório retorno.
É possível usar macros personalizadas no código.
Os recursos podem ser limitados não apenas no nível do sistema operacional, mas também programaticamente. A etapa de pré-processamento recebe no máximo 10 megabytes de RAM e um limite de tempo de execução de 10 segundos.
ПРИМЕЧАНИЕ. O valor de tempo limite de 10 segundos é bastante, porque a coleta condicional de milhares de itens de dados em 1 segundo de acordo com um cenário de pré-processamento bastante “pesado” pode desacelerar o Zabbix. Portanto, não é recomendado usar o pré-processamento para executar scripts JavaScript completos por meio dos chamados elementos de dados de sombra (itens fictícios), que são executados apenas para realizar o pré-processamento.
Você pode verificar seu código por meio do teste de pré-processamento ou usando o utilitário zabbix_js:
Substitua o item calculado pelo pré-processamento.
Condição: Obtenha a temperatura em Fahrenheit do sensor para armazenar em Celsius.
Anteriormente, criaríamos um item que coleta a temperatura em graus Fahrenheit. Depois disso, outro item de dados (calculado) que converteria Fahrenheit em Celsius usando uma fórmula.
Problemas:
É necessário duplicar os elementos de dados e armazenar todos os valores no banco de dados.
Você deve concordar com os intervalos para o item de dados "pai" que é calculado e usado na fórmula e para o item de dados calculado. Caso contrário, o item calculado pode entrar em um estado sem suporte ou calcular um valor anterior, o que afetará a confiabilidade dos resultados do monitoramento.
Uma solução foi mudar os intervalos de verificação flexíveis em favor de intervalos fixos para garantir que o item calculado seja avaliado após o item que recebe os dados (no nosso caso, a temperatura em graus Fahrenheit).
Mas se, por exemplo, usarmos o modelo para verificar um grande número de dispositivos e a verificação for realizada uma vez a cada 30 segundos, o Zabbix "hackeia" por 29 segundos e, no último segundo, começa a verificar e calcular. Isso cria uma fila e afeta o desempenho. Portanto, recomenda-se usar intervalos fixos apenas se for realmente necessário.
Neste problema, a solução ideal é um pré-processamento JavaScript de uma linha que converte graus Fahrenheit em graus Celsius:
`return (value - 32) * 5 / 9;`
É rápido e fácil, você não precisa criar itens de dados desnecessários e manter um histórico deles, e também pode usar intervalos flexíveis para verificações.
Mas, se em uma situação hipotética for necessário adicionar o elemento de dado recebido, por exemplo, com qualquer constante definida na macro, deve-se levar em consideração que o parâmetro valor expande em uma string. Em uma operação de adição de strings, duas strings são simplesmente combinadas em uma.
`return (value + "{$EXAMPLE.MACRO}");`
Para obter o resultado de uma operação matemática, é necessário converter os tipos dos valores obtidos para um formato numérico. Para isso você pode usar a função parseInt (), que produz um inteiro, uma função analisarFloat(), que produz um decimal, ou uma função número, que retorna um número inteiro ou decimal.
Tarefa 2
Obtenha o tempo em segundos até o final do certificado.
Condição: um serviço emite uma data de expiração de certificado no formato "12 de fevereiro 12:33:56 2022 GMT".
Em ECMAScript5 Date.parse () aceita uma data no formato ISO 8601 (AAAA-MM-DDTHH:mm:ss.sssZ). É necessário lançar uma string para ele no formato MMM DD AAAA HH:mm:ss ZZ
problema: o valor do mês é expresso como texto, não como número. Dados neste formato não são aceitos pela Duktape.
Exemplo de solução:
Em primeiro lugar, é declarada uma variável que recebe um valor (o script inteiro é uma declaração de variáveis listadas separadas por vírgulas).
Na primeira linha obtemos a data no parâmetro valor e separe-o com espaços usando o método divisão. Assim, obtemos um array, onde cada elemento do array, começando no índice 0, corresponde a um elemento de data antes e depois de um espaço. dividir(0) - mês, dividir(1) - número, dividir(2) - uma string com hora, etc. Depois disso, cada elemento da data pode ser acessado por índice no array.
`var split = value.split(' '),`
Cada mês (em ordem cronológica) corresponde ao índice de sua posição na matriz (de 0 a 11). Para converter um valor de texto em um valor numérico, um é adicionado ao índice do mês (porque os meses são numerados a partir de 1). Nesse caso, a expressão com a adição de um é tomada entre colchetes, pois, caso contrário, será obtida uma string, não um número. No final fazemos fatiar() - corte a matriz do final para deixar apenas dois caracteres (o que é importante para meses com um número de dois dígitos).
Os dados no formato resultante são o número de segundos de 1970 até algum ponto no futuro. É quase impossível usar dados no formato recebido em triggers, pois o Zabbix permite operar apenas com macros {Encontro} и {Tempo}, que retornam a data e a hora em um formato amigável.
Podemos então obter a data atual em JavaScript no formato Unix Timestamp e subtraí-la da data de expiração do certificado resultante para obter o número de milissegundos a partir de agora até que o certificado expire.
`now = Date.now();`
Dividimos o valor recebido por mil para obter os segundos no Zabbix.
No gatilho, você pode especificar a expressão 'durar' seguido de um conjunto de dígitos que corresponde ao número de segundos no período ao qual você deseja responder, por exemplo, em semanas. Assim, o gatilho notificará que o certificado expira em uma semana.
ПРИМЕЧАНИЕ. Preste atenção ao uso parseInt () em função retornopara converter o número fracionário resultante da divisão de milissegundos em um número inteiro. Você também pode usar analisarFloat() e armazenar dados fracionários.