Scripts externos para alertas¶
El canal «Proceso» de la entrega externa de alertas ejecuta en el nodo su programa o script con cada evento de alerta: la generación de un incidente y su resolución. Así las alertas llegan a cualquier sistema al que se pueda llamar desde un script: una pasarela SMS, una aplicación de mensajería, un webhook, su propia monitorización o un sistema de gestión de incidencias. Esta página describe cómo el nodo ejecuta el comando, qué recibe este y cómo se procesa el resultado, y ofrece dos plantillas listas para usar: en /bin/sh y en Python 3.
El modelo general de la entrega externa y los otros dos canales, Telegram y el correo, se describen en Entrega externa de alertas, y la ventana de configuración en Destinos de alerta.
Configuración del canal¶
El canal se configura en la pestaña «Proceso» de la ventana «Destinos de alerta». La ventana se abre con el botón «Destinos de alerta…» en la configuración del alertador (Destinos de alerta). Los mismos valores se guardan en la sección alerter del archivo de configuración.
Campo |
Clave |
Descripción |
|---|---|---|
«Habilitar destino de proceso» |
|
Activa el canal. Desactivado por defecto. |
«Comando» |
|
Línea de comandos: el programa y sus argumentos. Vacía por defecto; un canal activado con el comando vacío no ejecuta nada. |
«Severidad mínima» |
|
Umbral inferior de gravedad del evento: |
«Tiempo de espera, ms» |
|
Cuántos milisegundos se conceden a cada ejecución: de 100 a 600000, 5000 por defecto. |
Los cuatro valores se leen con cada evento, por lo que un cambio tiene efecto desde la siguiente alerta, sin reiniciar el servicio.
Cómo se ejecuta el comando¶
Sin shell. La línea «Comando» se divide en palabras según las reglas del shell, pero el shell en sí no se inicia. Las palabras se separan con espacios; el texto entre comillas simples se toma tal cual; entre comillas dobles, la barra invertida solo escapa " y \; fuera de las comillas, la barra invertida escapa el carácter siguiente; unas comillas vacías '' dan un argumento vacío. La primera palabra es el programa y las demás, sus argumentos. Las tuberías |, las redirecciones >, la sustitución de variables $VAR, ~ y los patrones de nombres de archivo no funcionan: llegan al programa como texto normal. Todo eso se hace dentro del script.
Búsqueda del programa. Si la primera palabra contiene /, es la ruta a un archivo: el archivo debe existir y ser ejecutable; de lo contrario, cada evento termina con el error process-command not executable. Un nombre sin / se busca en el PATH del servicio. Es más seguro indicar siempre la ruta completa.
Entorno de ejecución.
El usuario y el grupo son los mismos que los del servicio:
pss.El directorio de trabajo es la raíz
/; indique las rutas completas en el script.Las variables de entorno se heredan del servicio (incluido su
PATH), y se les añaden las variablesPSS_ALERT_*.Los archivos y sockets abiertos del servicio no se pasan al script: este solo dispone de la entrada estándar, la salida estándar y la salida de errores.
La salida estándar del script va al registro del sistema del servicio, es decir, a la salida de
journalctl -u pss(Registro del servicio). La salida de errores la lee el propio nodo, una vez terminado el script (Resultado, tiempo de espera y registro).
Qué recibe el script¶
Cada evento se transmite de dos formas a la vez: por campos en variables de entorno y completo en formato JSON por la entrada estándar. El nodo no pasa argumentos: solo existen los que están escritos en «Comando».
Variables de entorno
Variable |
Valor |
|---|---|
|
Identificador estable del incidente, el mismo en su generación y en su resolución, por ejemplo |
|
|
|
Gravedad en número: 3–7 (véase «Severidad mínima» más arriba). |
|
Gravedad en palabra: |
|
Código numérico del tipo de incidente (Catálogo de códigos de alerta). |
|
El mismo código como designación textual, por ejemplo |
|
Tipo de fuente: |
|
Identificador de la fuente, por ejemplo el número del flujo. |
|
Nombre del nodo en el que se generó el incidente. En una alerta recibida de un vecino del dominio Meshwork, es el nombre del vecino. |
|
Nombre legible del objeto, por ejemplo el nombre del flujo. |
|
Texto de la alerta. |
|
Hora de este evento, en segundos Unix (UTC). |
|
Hora de generación del incidente. |
|
Hora de resolución; |
Las variables son cómodas en /bin/sh: el texto con espacios y saltos de línea se transmite en ellas sin alteraciones, y no se ven en la lista de procesos (ps).
JSON en la entrada estándar. Una sola línea sin salto de línea final; después, la entrada se cierra. El JSON contiene la misma información, pero la gravedad y el tipo de fuente se indican solo con números (tipo de fuente: 1 — System, 2 — Stream, 3 — Cluster, 4 — EPG, 5 — Mosaic, 6 — DVB, 7 — CS), y no hay designaciones textuales del código ni de la gravedad. Campos:
siempre:
id,state,ts,raised_ts,cleared_ts,severity,src_type,src_id,node,display_name,code,message;en los incidentes de una entrada o salida concreta:
io_idyio_kind(inputuoutput);en las alertas
AdminAction:admin_action_src;en un incidente «oscilante»:
flapping(true) yflap_cycles, su número de ciclos (Cuándo se ejecuta el script).
El script no está obligado a leer la entrada estándar: todo lo necesario está también en las variables. Pero si la lee, debe leerla hasta el final, no solo una línea.
Ejemplo: en el nodo edge-1, el flujo Sport HD (número 12) lleva 15 segundos sin datos. Generación:
{"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)"}
Resolución del mismo incidente dos minutos después:
{"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)"}
Aquí el JSON se ha dividido en líneas para mayor claridad; el nodo lo transmite en una sola línea. La resolución conserva la gravedad y el texto del incidente: no hay ningún texto «recuperado», la resolución la indica el campo state. Variables de entorno de esa misma generación:
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
Cuándo se ejecuta el script¶
Generación y resolución. El script se ejecuta una vez cuando se genera el incidente y otra vez cuando se resuelve. Las actualizaciones intermedias de un incidente activo (repetición, texto nuevo, cambio de gravedad) no se pasan al script.
Umbral de gravedad. La «Severidad mínima» se aplica al incidente, no a cada evento. Un incidente por debajo del umbral no se transmite. Si más tarde su gravedad subió hasta el umbral, el script recibe
raiseduna sola vez, en el momento de esa subida.clearedllega si y solo si se transmitióraised, sea cual sea la gravedad en el momento de la resolución. Se considera transmitido el evento entregado al canal: si la entrega falló, el incidente se sigue considerando abierto y su resolución se transmitirá.Interruptor principal. Con el alertador del nodo desactivado (Alertador), el script no se ejecuta en absoluto, tampoco para la resolución de un incidente ya notificado.
Alertas del dominio. Con la replicación de alertas por el dominio Meshwork, el script recibe también las alertas de los vecinos; el nodo de origen se indica en
PSS_ALERT_NODE(Replicación de alertas por el dominio). Si el canal está activado en varios nodos del dominio, cada uno de ellos informará del mismo incidente. Para evitar duplicados, active el canal en un solo nodo.Incidentes oscilantes. Si un incidente se genera y se resuelve una y otra vez (por defecto, tres ciclos seguidos con intervalos de no más de 600 segundos), el nodo deja de resolverlo en cada ciclo: el incidente permanece activo y llega una única resolución cuando la condición se ha mantenido recuperada al menos 600 segundos. El script recibe una generación y una resolución en lugar de un par por cada ciclo. Los valores se definen con las claves
flap-suppress,flap-cycles,flap-window-secyflap-settle-secde la secciónalerterdel archivo de configuración (El archivo de configuración pss.json).Las alertas persistentes, que solo se resuelven manualmente, envían la resolución cuando el operador resuelve la alerta (Consulta y resolución de alertas).
Reinicio del servicio. La lista de incidentes activos se guarda en memoria y se pierde al reiniciar el servicio, sin resoluciones. Una condición que sigue vigente se genera de nuevo, y el script recibe un segundo
raisedcon el mismoPSS_ALERT_ID.
Por eso conviene procesar cada evento según PSS_ALERT_ID y PSS_ALERT_STATE: un raised repetido actualiza un incidente conocido en lugar de abrir uno nuevo.
Cola y orden¶
El script se ejecuta estrictamente de uno en uno: la siguiente ejecución empieza cuando termina la anterior, y los eventos se procesan en el orden de llegada.
Las ejecuciones las gestiona un hilo en segundo plano propio del canal. Un script lento no retrasa ni los demás canales ni el funcionamiento del nodo, pero sí los siguientes eventos de ese mismo canal.
Mientras el script trabaja, los eventos nuevos esperan en la cola del canal, de 512 eventos. Cuando la cola está llena, se descartan los eventos más recientes y en el registro del servicio se escribe
delivery queue full, dropped alert(s).Al detener o reiniciar el servicio, los eventos de la cola se descartan; el servicio solo espera a que termine la ejecución que ya está en curso.
Calcule el margen: ante una avalancha de cien eventos, un script que tarda dos segundos se retrasa más de tres minutos respecto a los eventos. Pase la entrega lenta a segundo plano (véase la sección siguiente).
Resultado, tiempo de espera y registro¶
Código de salida 0: el evento se ha entregado.
Código distinto de cero: error de entrega. En el registro del servicio (Registro del servicio) se escribe una línea del tipo
[alert:process] deliver failed: process-command '/usr/local/bin/pss-alert.sh' exit 1: <texto de stderr>con los últimos 4 KB de la salida de errores.La salida de errores la lee el nodo mientras el script se ejecuta y solo la muestra junto con un código distinto de cero. La salida a stderr con el código 0 no se considera un error y no influye en nada; una salida abundante a stderr no ralentiza el script ni provoca un tiempo de espera agotado.
Tiempo de espera. Si el script no ha terminado dentro de «Tiempo de espera, ms», el nodo lo termina a la fuerza (
SIGKILL) y escribetimed out after <N> ms. Solo se termina el propio script; los procesos que haya lanzado siguen funcionando.Sin reintentos. Un evento fallido no se vuelve a enviar; el canal pasa al siguiente.
Limitación de la frecuencia de registro. El primer error del canal se escribe de inmediato; los siguientes, como máximo una vez por minuto, con el número de errores de ese intervalo al final de la línea (
(x12)).
Nota
Procesos en segundo plano. Un proceso que el script deja en segundo plano no retrasa los siguientes eventos del canal, aunque haya heredado la salida de errores. En las versiones anteriores a 2.0.2.415, un proceso así detenía el canal hasta terminar, por lo que en los nodos donde puedan funcionar versiones anteriores, y en general por pulcritud, lance el trabajo en segundo plano con la entrada y la salida cerradas:
some-command </dev/null >/dev/null 2>&1 &
o con redirección a un archivo de registro propio:
setsid some-command </dev/null >>/var/log/pss-alerts/bg.log 2>&1 &
Seguridad¶
El script se ejecuta con los permisos del servicio, los del usuario
pss. Haga que el propietario del archivo del script searooty conceda al usuariopsssolo lectura y ejecución (modo750, grupopss): así el servicio no puede modificar el script que él mismo ejecuta.No pase secretos en los argumentos de «Comando». Los argumentos son visibles para todos los usuarios del sistema en la lista de procesos (
ps), se guardan en el archivo de configuración y se ven en la interfaz web con el rol Admin (a los roles Restricted admin y Viewer la línea de comandos se les muestra oculta). Guarde los tokens y las contraseñas en el propio script o en un archivo aparte que solo el usuariopsspueda leer.El texto de la alerta (nombre del flujo, mensaje) procede de la configuración y de las fuentes. No lo inserte en comandos del shell sin comillas ni lo pase a
eval. Las plantillas de abajo pasan los valores siempre entre comillas.
Plantilla en /bin/sh¶
El script escribe una línea en el registro por cada evento y termina con el código 0. El lugar para la entrega real está marcado con un comentario.
#!/bin/sh
# Receptor de alertas de Perfect Streamer (canal «Proceso»).
# El nodo ejecuta el script en cada generación y resolución de un incidente.
# Los campos del evento están en las variables PSS_ALERT_*; el JSON completo, en la entrada estándar.
LOG=/var/log/pss-alerts/alerts.log
# JSON de la entrada estándar: una sola línea sin salto de línea final,
# por eso se lee con cat y no con read.
json=$(cat)
# Una línea en el registro. Un error de escritura da un código distinto de cero: el nodo lo anotará
# en su registro junto con el texto de 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
# Aquí se conecta la entrega real, por ejemplo un webhook.
# La llamada síncrona debe caber en «Tiempo de espera, ms» (5 s por defecto):
#
# 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
#
# La entrega lenta, solo en segundo plano y solo con la entrada y la salida cerradas;
# si no, el canal se detendrá (véase «Resultado, tiempo de espera y 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
Plantilla en Python 3¶
Lo mismo en Python 3, solo con la biblioteca estándar (Python 3.6 o posterior).
#!/usr/bin/env python3
# Receptor de alertas de Perfect Streamer (canal «Proceso»).
# El nodo ejecuta el script en cada generación y resolución de un incidente.
# Los campos del evento están en las variables PSS_ALERT_*; el JSON completo, en la entrada estándar.
import json
import os
import sys
import time
LOG = "/var/log/pss-alerts/alerts.log"
def main():
# Toda la entrada estándar: una línea JSON sin salto de línea final.
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", ""),
)
# Un error de escritura terminará el script con un código distinto de cero, y el texto
# del error irá a stderr: el nodo lo anotará en su registro.
with open(LOG, "a", encoding="utf-8") as f:
f.write(line)
# Aquí se conecta la entrega real, por ejemplo un webhook.
# La llamada debe caber en «Tiempo de espera, ms» (5 s por defecto):
#
# 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()
#
# Lance los procesos en segundo plano (subprocess.Popen) con
# stdin/stdout/stderr=subprocess.DEVNULL y start_new_session=True
# (véase «Resultado, tiempo de espera y registro»).
return 0
if __name__ == "__main__":
sys.exit(main())
Instalación y comprobación¶
Cree el directorio de registro con permiso de escritura para el usuario
pss:sudo install -d -o pss -g pss -m 750 /var/log/pss-alerts
Guarde la plantilla, por ejemplo como
/usr/local/bin/pss-alert.sh(opss-alert.py), y establezca el propietario y los permisos:sudo chown root:pss /usr/local/bin/pss-alert.sh sudo chmod 750 /usr/local/bin/pss-alert.sh
Compruebe el script manualmente como usuario
psscon un evento de muestra: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
Se espera
exit=0y una línea nueva en el registro. Mida también el tiempo: debe ser bastante menor que «Tiempo de espera, ms».En la pestaña «Proceso» de la ventana «Destinos de alerta», indique en «Comando» la ruta completa
/usr/local/bin/pss-alert.sh, ajuste «Severidad mínima», active el destino y guarde.Compruebe la entrega con un flujo de prueba: interrumpa el envío de datos a su entrada (por ejemplo, detenga el emisor). Transcurrido el tiempo de espera por ausencia de datos (15 segundos por defecto, Ajustes del alerter), se generará una alerta
no-datacon gravedadWarningy, al reanudarse los datos, su resolución. En el registro del script deben aparecer las líneasraisedy despuéscleared.
Problemas comunes¶
El script no se ejecuta en absoluto. Compruebe el interruptor principal del alertador y «Habilitar destino de proceso», que «Comando» no esté vacío y que la gravedad del evento no sea inferior al umbral. Por defecto el umbral es
Warning: los eventosInfono salen al exterior.En el registro del servicio aparece
process-command not executable. El archivo de la ruta indicada no existe o el usuariopssno tiene permiso de ejecución. Compruebels -ly los permisos de todos los directorios de la ruta.En el registro del servicio aparece
posix_spawnp failed. No se pudo iniciar el programa: lo más frecuente es que el nombre se haya indicado sin ruta y no se encuentre en elPATHdel servicio, o que la primera línea del script (#!) sea incorrecta. Indique la ruta completa.En el registro del servicio aparece
exit <N>. El script terminó con un error, cuyo texto de stderr figura en la misma línea. Una causa frecuente es la falta de permiso de escritura en el directorio de registro o en los archivos de trabajo: el script se ejecuta comopss, no comoroot, y en el directorio/.En el registro del servicio aparece
timed out after <N> ms. El script no terminó dentro del tiempo de espera: acelere la entrega (con un tiempo de espera de la llamada de red menor que «Tiempo de espera, ms»), aumente «Tiempo de espera, ms» o pase la parte lenta a segundo plano con la salida cerrada.Las alertas llegan con mucho retraso o se pierden; en el registro aparece
delivery queue full. El script es demasiado lento para el volumen de eventos: la cola del canal está llena. Acelere el script o suba la «Severidad mínima».El canal ha enmudecido sin ningún error en el registro (versiones anteriores a 2.0.2.415). Lo más probable es que el script haya dejado un proceso en segundo plano con la salida de errores heredada y que el nodo esté esperando a que termine. Localice el proceso (
ps -u pss -f), termínelo y corrija el lanzamiento en segundo plano: con</dev/null >/dev/null 2>&1o con redirección a un archivo. Desde la versión 2.0.2.415, un proceso así no detiene el canal.El script funciona a mano, pero no desde el nodo. La causa está en el entorno: otro usuario (
pss), el directorio de trabajo/, otroPATH, sin las variables de su sesión. Compruebe con el comando del paso 3 de la sección Instalación y comprobación, mediantesudo -u pss.Llegó un segundo
raisedcon el mismoPSS_ALERT_ID. El servicio se reinició y el incidente activo se detectó de nuevo (véase Cuándo se ejecuta el script).No llegó
cleared. El alertador del nodo se desactivó mientras el incidente estaba activo, o el servicio se reinició antes de la resolución (véase Cuándo se ejecuta el script).La misma alerta llega desde varios nodos. El canal está activado en varios nodos del dominio que replican alertas. Déjelo en uno solo.
La salida del script no se ve. La salida estándar va a
journalctl -u pss, y la salida de errores llega al registro del servicio solo con un código de salida distinto de cero. Escriba los diagnósticos en un archivo de registro propio.