Scripts externos para alertas¶
O canal «Processo» da entrega externa de alertas executa no nó o seu programa ou script a cada evento de alerta: a geração de um incidente e a sua resolução. Assim, os alertas chegam a qualquer sistema que possa ser chamado a partir de um script: um gateway de SMS, um mensageiro, um webhook, o seu próprio monitoramento ou um sistema de chamados. Esta página descreve como o nó executa o comando, o que ele recebe e como o resultado é tratado, e traz dois modelos prontos: em /bin/sh e em Python 3.
O funcionamento geral da entrega externa e os outros dois canais, Telegram e e-mail, estão descritos em Entrega externa de alertas; a janela de configuração, em Destinos de alerta.
Configuração do canal¶
O canal é configurado na aba «Processo» da janela «Destinos de alerta». A janela é aberta pelo botão «Destinos de alerta…» nas configurações do alertador (Destinos de alerta). Os mesmos valores ficam na seção alerter do arquivo de configuração.
Campo |
Chave |
Finalidade |
|---|---|---|
«Ativar destino de processo» |
|
Ativa o canal. Desativado por padrão. |
«Comando» |
|
Linha de comando: o programa e os seus argumentos. Vazia por padrão; um canal ativado com o comando vazio não executa nada. |
«Severidade mínima» |
|
Limiar inferior de severidade do evento: |
«Timeout, ms» |
|
Tempo em milissegundos concedido a cada execução: de 100 a 600000, padrão 5000. |
Os quatro valores são lidos a cada evento, por isso uma alteração vale a partir do próximo alerta, sem reiniciar o serviço.
Como o comando é executado¶
Sem shell. A linha «Comando» é dividida em palavras pelas regras do shell, mas o shell em si não é iniciado. As palavras são separadas por espaços; o texto entre aspas simples é tomado como está; entre aspas duplas, a barra invertida escapa apenas " e \; fora das aspas, a barra invertida escapa o caractere seguinte; aspas vazias '' geram um argumento vazio. A primeira palavra é o programa, as demais são os seus argumentos. Pipes |, redirecionamentos >, substituição de variáveis $VAR, ~ e padrões de nomes de arquivo não funcionam: chegam ao programa como texto comum. Tudo isso deve ser feito dentro do script.
Busca do programa. Se a primeira palavra contém /, ela é o caminho de um arquivo: o arquivo deve existir e ser executável; caso contrário, cada evento termina com o erro process-command not executable. Um nome sem / é procurado no PATH do serviço. O mais seguro é indicar sempre o caminho completo.
Ambiente de execução.
Usuário e grupo: os mesmos do serviço,
pss.Diretório de trabalho: a raiz
/; no script, indique os caminhos completos.As variáveis de ambiente são herdadas do serviço (inclusive o seu
PATH), e a elas se somam as variáveisPSS_ALERT_*.Os arquivos e sockets abertos do serviço não são passados ao script: ele tem apenas a entrada padrão, a saída padrão e a saída de erro padrão.
A saída padrão do script vai para o registro do sistema do serviço, ou seja, para a saída de
journalctl -u pss(Registro do serviço). A saída de erro padrão é lida pelo próprio nó, depois que o script termina (Resultado, tempo limite e registro).
O que o script recebe¶
Cada evento é passado de duas formas ao mesmo tempo: em campos nas variáveis de ambiente e por inteiro em formato JSON na entrada padrão. O nó não passa argumentos: há apenas os que estão escritos em «Comando».
Variáveis de ambiente
Variável |
Valor |
|---|---|
|
Identificador estável do incidente, o mesmo na sua geração e na sua resolução, por exemplo |
|
|
|
Severidade em número: 3–7 (veja «Severidade mínima» acima). |
|
Severidade por extenso: |
|
Código numérico do tipo de incidente (Catálogo de códigos de alerta). |
|
O mesmo código como designação textual, por exemplo |
|
Tipo de fonte: |
|
Identificador da fonte, por exemplo o número do fluxo. |
|
Nome do nó em que o incidente foi gerado. Em um alerta recebido de um vizinho do domínio Meshwork, é o nome desse vizinho. |
|
Nome legível do objeto, por exemplo o nome do fluxo. |
|
Texto do alerta. |
|
Horário deste evento, em segundos Unix (UTC). |
|
Horário da geração do incidente. |
|
Horário da resolução; |
As variáveis são práticas em /bin/sh: textos com espaços e quebras de linha passam por elas sem distorção, e elas não aparecem na lista de processos (ps).
JSON na entrada padrão. Uma única linha sem quebra de linha final; depois dela, a entrada é fechada. O JSON traz as mesmas informações, mas a severidade e o tipo de fonte vêm apenas como números (tipo de fonte: 1 — System, 2 — Stream, 3 — Cluster, 4 — EPG, 5 — Mosaic, 6 — DVB, 7 — CS), e não há designações textuais do código e da severidade. Campos:
sempre:
id,state,ts,raised_ts,cleared_ts,severity,src_type,src_id,node,display_name,code,message;nos incidentes de uma entrada ou saída específica:
io_ideio_kind(inputououtput);nos alertas
AdminAction:admin_action_src;em um incidente «oscilante»:
flapping(true) eflap_cycles, o número dos seus ciclos (Quando o script é executado).
O script não é obrigado a ler a entrada padrão: tudo o que é necessário também está nas variáveis. Mas, se ele lê a entrada, deve lê-la até o fim, e não apenas uma linha.
Exemplo: no nó edge-1, o fluxo Sport HD (número 12) está há 15 segundos sem dados. Geração:
{"id":"2:12:3","state":"raised","ts":1761134400,"raised_ts":1761134400,
"cleared_ts":0,"severity":4,"src_type":2,"src_id":12,"node":"edge-1",
"display_name":"Sport HD","code":3,
"message":"[Sport HD] no data 15s (no-data threshold 15s)"}
Resolução do mesmo incidente dois minutos depois:
{"id":"2:12:3","state":"cleared","ts":1761134520,"raised_ts":1761134400,
"cleared_ts":1761134520,"severity":4,"src_type":2,"src_id":12,"node":"edge-1",
"display_name":"Sport HD","code":3,
"message":"[Sport HD] no data 15s (no-data threshold 15s)"}
Aqui o JSON foi dividido em linhas para facilitar a leitura; o nó o passa em uma única linha. A resolução mantém a severidade e o texto do incidente; não existe um texto «restabelecido», e quem indica a resolução é o campo state. As variáveis de ambiente da mesma geração:
PSS_ALERT_ID=2:12:3
PSS_ALERT_STATE=raised
PSS_ALERT_SEVERITY=4
PSS_ALERT_SEVERITY_STR=Warning
PSS_ALERT_CODE=3
PSS_ALERT_CODE_STR=no-data
PSS_ALERT_SRC_TYPE=Stream
PSS_ALERT_SRC_ID=12
PSS_ALERT_NODE=edge-1
PSS_ALERT_DISPLAY_NAME=Sport HD
PSS_ALERT_MESSAGE=[Sport HD] no data 15s (no-data threshold 15s)
PSS_ALERT_TS=1761134400
PSS_ALERT_RAISED_TS=1761134400
PSS_ALERT_CLEARED_TS=0
Quando o script é executado¶
Geração e resolução. O script é executado uma vez quando o incidente é gerado e uma vez quando ele é resolvido. As atualizações intermediárias de um incidente ativo (repetição, novo texto, mudança de severidade) não são passadas ao script.
Limiar de severidade. A «Severidade mínima» se aplica ao incidente, e não a cada evento. Um incidente abaixo do limiar não é passado. Se a sua severidade subiu depois até o limiar, o script recebe
raiseduma única vez, no momento desse aumento.clearedchega se e somente seraisedfoi passado, qualquer que seja a severidade no momento da resolução. Considera-se passado o evento entregue ao canal: se a entrega falhou, o incidente continua sendo considerado aberto, e a sua resolução será passada.Interruptor principal. Com o alertador do nó desativado (Alertador), o script não é executado em nenhum caso, nem mesmo para a resolução de um incidente já comunicado.
Alertas do domínio. Com a replicação de alertas pelo domínio Meshwork, o script também recebe os alertas dos vizinhos; o nó de origem é indicado em
PSS_ALERT_NODE(Replicação de alertas em todo o domínio). Se o canal estiver ativado em vários nós do domínio, cada um deles comunicará o mesmo incidente. Para evitar duplicatas, ative o canal em um único nó.Incidentes oscilantes. Se um incidente é gerado e resolvido repetidamente (por padrão, três ciclos seguidos com intervalos de no máximo 600 segundos), o nó deixa de resolvê-lo a cada ciclo: o incidente permanece ativo, e uma única resolução chega quando a condição se mantiver restabelecida por pelo menos 600 segundos. O script recebe uma geração e uma resolução, em vez de um par a cada ciclo. Os valores são definidos pelas chaves
flap-suppress,flap-cycles,flap-window-seceflap-settle-secda seçãoalerterdo arquivo de configuração (O arquivo de configuração pss.json).Alertas persistentes, que só são resolvidos manualmente, enviam a resolução quando o operador resolve o alerta (Visualização e resolução de alertas).
Reinício do serviço. A lista de incidentes ativos fica na memória e se perde no reinício do serviço, sem resoluções. Uma condição que ainda persiste é gerada de novo, e o script recebe um segundo
raisedcom o mesmoPSS_ALERT_ID.
Por isso, é melhor tratar o evento por PSS_ALERT_ID e PSS_ALERT_STATE: um raised repetido atualiza um incidente já conhecido em vez de abrir um novo.
Fila e ordem¶
O script é executado estritamente um de cada vez: a execução seguinte começa depois que a anterior termina, e os eventos seguem a ordem de chegada.
As execuções são conduzidas por uma thread em segundo plano própria do canal. Um script lento não atrasa os outros canais nem o funcionamento do nó, mas atrasa os eventos seguintes do mesmo canal.
Enquanto o script está em execução, os novos eventos aguardam na fila do canal, com capacidade para 512 eventos. Quando a fila está cheia, os eventos mais novos são descartados, e o registro do serviço recebe a linha
delivery queue full, dropped alert(s).Na parada e no reinício do serviço, os eventos da fila são descartados; o serviço aguarda apenas a execução que já está em andamento.
Faça as contas: em uma tempestade de cem eventos, um script que leva dois segundos fica mais de três minutos atrás dos eventos. Mova a entrega demorada para segundo plano (veja a próxima seção).
Resultado, tempo limite e registro¶
Código de saída 0: o evento foi entregue.
Código diferente de zero: falha na entrega. No registro do serviço (Registro do serviço) é gravada uma linha como
[alert:process] deliver failed: process-command '/usr/local/bin/pss-alert.sh' exit 1: <texto do stderr>com os últimos 4 KB da saída de erro.A saída de erro é lida pelo nó enquanto o script é executado e só é exibida junto com um código diferente de zero. A saída no stderr com o código 0 não é considerada erro e não afeta nada; uma saída abundante no stderr não deixa o script mais lento e não leva ao tempo limite.
Tempo limite. Se o script não terminar dentro do «Timeout, ms», o nó o encerra à força (
SIGKILL) e gravatimed out after <N> ms. Só o próprio script é encerrado; os processos iniciados por ele continuam em execução.Não há novas tentativas. Um evento que falhou não é reenviado; o canal passa ao seguinte.
Limitação da frequência de registros. O primeiro erro do canal é gravado imediatamente; os seguintes, no máximo uma vez por minuto, com o número de erros desse período no fim da linha (
(x12)).
Nota
Processos em segundo plano. Um processo que o script deixa em segundo plano não atrasa os próximos eventos do canal, mesmo que tenha herdado a saída de erro. Nas versões anteriores à 2.0.2.415, esse processo parava o canal até terminar; por isso, nos nós onde podem funcionar versões anteriores, e em geral por cuidado, inicie trabalhos em segundo plano com a entrada e a saída fechadas:
some-command </dev/null >/dev/null 2>&1 &
ou com redirecionamento para um arquivo de registro próprio:
setsid some-command </dev/null >>/var/log/pss-alerts/bg.log 2>&1 &
Segurança¶
O script é executado com os privilégios do serviço, do usuário
pss. Definarootcomo dono do arquivo do script e dê ao usuáriopssapenas leitura e execução (modo750, grupopss): assim, o serviço não consegue alterar o script que ele mesmo executa.Não passe segredos nos argumentos do «Comando». Os argumentos ficam visíveis para todos os usuários do sistema na lista de processos (
ps), são armazenados no arquivo de configuração e aparecem na interface web para o papel Admin (para os papéis Restricted admin e Viewer, a linha de comando é exibida oculta). Guarde tokens e senhas no próprio script ou em um arquivo separado que só o usuáriopsspossa ler.O texto do alerta (o nome do fluxo, a mensagem) vem das configurações e das fontes. Não o insira em comandos de shell sem aspas e não o passe para
eval. Os modelos abaixo passam os valores somente entre aspas.
Modelo em /bin/sh¶
O script grava uma linha no registro a cada evento e termina com o código 0. O lugar da entrega real está marcado com um comentário.
#!/bin/sh
# Receptor de alertas do Perfect Streamer (canal «Processo»).
# O nó executa o script a cada geração e resolução de um incidente.
# Os campos do evento estão nas variáveis PSS_ALERT_*, o JSON inteiro - na entrada padrão.
LOG=/var/log/pss-alerts/alerts.log
# JSON da entrada padrão: uma linha sem quebra de linha no fim,
# por isso lemos com cat, e não com read.
json=$(cat)
# Uma linha no registro. Erro de gravação - código diferente de zero: o nó o grava
# no próprio registro junto com o texto do stderr.
printf '%s %s %s %s [%s] %s: %s\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$PSS_ALERT_STATE" "$PSS_ALERT_SEVERITY_STR" \
"$PSS_ALERT_NODE" "$PSS_ALERT_CODE_STR" "$PSS_ALERT_DISPLAY_NAME" \
"$PSS_ALERT_MESSAGE" >> "$LOG" || exit 1
# Aqui entra a entrega real, por exemplo um webhook.
# A chamada síncrona deve caber no «Timeout, ms» (padrão 5 s):
#
# printf '%s' "$json" | curl -sS -m 3 -X POST \
# -H 'Content-Type: application/json' --data-binary @- \
# https://hooks.example.com/pss >/dev/null || exit 1
#
# Entrega demorada - só em segundo plano e só com entrada e saída fechadas,
# senão o canal para (veja «Resultado, tempo limite e registro»):
#
# ( printf '%s' "$json" | curl -sS -m 30 -X POST \
# -H 'Content-Type: application/json' --data-binary @- \
# https://hooks.example.com/pss ) </dev/null >/dev/null 2>&1 &
exit 0
Modelo em Python 3¶
O mesmo em Python 3, apenas com a biblioteca padrão (Python 3.6 ou mais recente).
#!/usr/bin/env python3
# Receptor de alertas do Perfect Streamer (canal «Processo»).
# O nó executa o script a cada geração e resolução de um incidente.
# Os campos do evento estão nas variáveis PSS_ALERT_*, o JSON inteiro - na entrada padrão.
import json
import os
import sys
import time
LOG = "/var/log/pss-alerts/alerts.log"
def main():
# Toda a entrada padrão: uma linha de JSON sem quebra de linha no fim.
raw = sys.stdin.read()
alert = json.loads(raw) if raw else {}
env = os.environ
line = "{} {} {} {} [{}] {}: {}\n".format(
time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
alert.get("state", env.get("PSS_ALERT_STATE", "")),
env.get("PSS_ALERT_SEVERITY_STR", ""),
alert.get("node", ""),
env.get("PSS_ALERT_CODE_STR", ""),
alert.get("display_name", ""),
alert.get("message", ""),
)
# Um erro de gravação encerra o script com código diferente de zero, e o texto
# do erro vai para o stderr - o nó o grava no próprio registro.
with open(LOG, "a", encoding="utf-8") as f:
f.write(line)
# Aqui entra a entrega real, por exemplo um webhook.
# A chamada deve caber no «Timeout, ms» (padrão 5 s):
#
# import urllib.request
# req = urllib.request.Request(
# "https://hooks.example.com/pss",
# data=raw.encode("utf-8"),
# headers={"Content-Type": "application/json"},
# )
# urllib.request.urlopen(req, timeout=3).close()
#
# Inicie processos em segundo plano (subprocess.Popen) com
# stdin/stdout/stderr=subprocess.DEVNULL e start_new_session=True
# (veja «Resultado, tempo limite e registro»).
return 0
if __name__ == "__main__":
sys.exit(main())
Instalação e verificação¶
Crie o diretório de registro com permissão de escrita para o usuário
pss:sudo install -d -o pss -g pss -m 750 /var/log/pss-alerts
Salve o modelo, por exemplo como
/usr/local/bin/pss-alert.sh(oupss-alert.py), e defina o dono e as permissões:sudo chown root:pss /usr/local/bin/pss-alert.sh sudo chmod 750 /usr/local/bin/pss-alert.sh
Teste o script manualmente como usuário
pss, com um evento de exemplo:echo '{"id":"2:12:3","state":"raised","severity":4,"node":"edge-1","display_name":"Sport HD","code":3,"message":"test"}' | \ sudo -u pss env PSS_ALERT_ID=2:12:3 PSS_ALERT_STATE=raised \ PSS_ALERT_SEVERITY=4 PSS_ALERT_SEVERITY_STR=Warning \ PSS_ALERT_CODE=3 PSS_ALERT_CODE_STR=no-data \ PSS_ALERT_NODE=edge-1 PSS_ALERT_DISPLAY_NAME='Sport HD' \ PSS_ALERT_MESSAGE=test \ /usr/local/bin/pss-alert.sh echo "exit=$?" tail -n 1 /var/log/pss-alerts/alerts.log
Espera-se
exit=0e uma nova linha no registro. Aproveite para medir o tempo: ele deve ser bem menor que o «Timeout, ms».Na aba «Processo» da janela «Destinos de alerta», informe em «Comando» o caminho completo
/usr/local/bin/pss-alert.sh, defina a «Severidade mínima», ative o destino e salve.Verifique a entrega em um fluxo de teste: interrompa o envio de dados para a entrada dele (por exemplo, pare o transmissor). Após o tempo limite de ausência de dados (padrão 15 segundos, Configurações do alerter), será gerado um alerta
no-datacom severidadeWarninge, quando os dados voltarem, a sua resolução. No registro do script devem aparecer as linhasraisede, depois,cleared.
Problemas comuns¶
O script não é executado em nenhum caso. Verifique o interruptor principal do alertador e «Ativar destino de processo», se o «Comando» não está vazio e se a severidade do evento não está abaixo do limiar. Por padrão, o limiar é
Warning: eventosInfonão são enviados para fora.No registro do serviço:
process-command not executable. O arquivo no caminho indicado não existe ou o usuáriopssnão tem permissão de execução. Verifique comls -l, incluindo as permissões de todos os diretórios do caminho.No registro do serviço:
posix_spawnp failed. Não foi possível iniciar o programa: na maioria das vezes, o nome foi indicado sem caminho e não foi encontrado noPATHdo serviço, ou a primeira linha do script (#!) está errada. Indique o caminho completo.No registro do serviço:
exit <N>. O script terminou com erro, e o texto do stderr aparece na mesma linha. Uma causa frequente é a falta de permissão de escrita no diretório de registro ou nos arquivos de trabalho: o script é executado comopss, e não comoroot, e no diretório/.No registro do serviço:
timed out after <N> ms. O script não terminou dentro do tempo limite: acelere a entrega (com um tempo limite da chamada de rede menor que o «Timeout, ms»), aumente o «Timeout, ms» ou mova a parte demorada para segundo plano com a saída fechada.Os alertas chegam com grande atraso ou se perdem; no registro,
delivery queue full. O script é lento demais para o volume de eventos: a fila do canal está cheia. Acelere o script ou aumente a «Severidade mínima».O canal ficou mudo sem nenhum erro no registro (versões anteriores à 2.0.2.415). O mais provável é que o script tenha deixado um processo em segundo plano com a saída de erro herdada e que o nó esteja aguardando o término dele. Encontre o processo (
ps -u pss -f), encerre-o e corrija a forma de iniciar o segundo plano: com</dev/null >/dev/null 2>&1ou com redirecionamento para um arquivo. A partir da versão 2.0.2.415, esse processo não para o canal.O script funciona manualmente, mas não a partir do nó. A causa está no ambiente: outro usuário (
pss), o diretório de trabalho/, outroPATH, a ausência das variáveis da sua sessão. Teste com o comando do passo 3 da seção Instalação e verificação, viasudo -u pss.Chegou um segundo
raisedcom o mesmoPSS_ALERT_ID. O serviço foi reiniciado, e o incidente ativo foi detectado novamente (veja Quando o script é executado).Não chegou
cleared. O alertador do nó foi desativado enquanto o incidente estava ativo, ou o serviço foi reiniciado antes da resolução (veja Quando o script é executado).O mesmo alerta chega de vários nós. O canal está ativado em vários nós do domínio que replicam alertas. Deixe-o ativado em apenas um.
A saída do script não aparece. A saída padrão vai para
journalctl -u pss, e a saída de erro só chega ao registro do serviço com um código de saída diferente de zero. Grave o diagnóstico em um arquivo de registro próprio.