"Después del juego es antes del juego"
Sepp Herberger

sábado, 12 de mayo de 2018

Monitorizando nuestro SAI con nut (Parte 4)

Hace ya tiempo que no dedico una entrada al SAI, pero como se me estropeó el que tenía y recibí uno de reemplazo, al conectarlo y monitorizarlo de nuevo he aprovechado para probar y cerrar temas pendientes. Las entradas precedentes eran:


En el apartado 5 de la última entrada dejaba abierto el problema del apagado del servidor cuando está funcionando con baterías, un tema peliagudo que no tenía muy probado por falta de tiempo y oportunidad. No es plan desconectar el SAI de la corriente y ver que pasa en horario de producción. Ahora si he probado con calma y estas son las conclusiones.

1. Ficheros de configuración básica.

Empecemos repasando como tenemos los ficheros básicos en /etc/nut:

/etc/nut/hosts.conf
MONITOR salicru@localhost "UPS Virgen de Guadalupe"
/etc/nut/nut.conf
MODE=standalone
/etc/nut/ups.conf
maxretry = 3
[salicru]
driver = blazer_usb
port = auto
subdriver = cypress
protocol = mustek
desc = "SAI Salicru"
vendorid = 0665
productid = 5161
bus = "004" # adaptalo a tu caso 
/etc/nut/upsd.conf
LISTEN 127.0.0.1 3493
#IP del servidor donde esta conectado el SAI con el cable USB.
LISTEN 172.20.123.2 3493
/etc/nut/upsd.users
[admin_sai]
password = 13578axd
upsmon master
/etc/nut/upsset.conf
deny from all
allow from 172.20.123.0  
/etc/nut/upsmon.conf
MONITOR salicru@localhost 1 admin_sai 13578axd master
RUN_AS_USER nut
MINSUPPLIES 1
SHUTDOWNCMD "/root/scripts/apagar_servidores.sh"
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 15
DEADTIME 15
POWERDOWNFLAG /etc/killpower
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5
NOTIFYCMD "/sbin/upssched"
NOTIFYMSG ONLINE "UPS: Normal state"
NOTIFYMSG ONBATT "UPS: On battery"
NOTIFYMSG LOWBATT "UPS: Battery low"
NOTIFYMSG FSD "UPS: Starting shutdown"
NOTIFYMSG COMMOK "UPS: Communication restored"
NOTIFYMSG COMMBAD "UPS: Communication lose"
NOTIFYMSG SHUTDOWN "UPS: Shutting down"
NOTIFYMSG REPLBATT "UPS: Replace battery"
NOTIFYMSG NOCOMM      "UPS %s is unavailable"
NOTIFYMSG NOPARENT    "upsmon parent process died - shutdown impossible"
NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC
NOTIFYFLAG FSD SYSLOG+WALL+EXEC
NOTIFYFLAG COMMOK SYSLOG+WALL+EXEC
NOTIFYFLAG COMMBAD SYSLOG+WALL+EXEC
NOTIFYFLAG SHUTDOWN SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT SYSLOG+WALL+EXEC
NOTIFYFLAG NOCOMM     SYSLOG+WALL+EXEC
NOTIFYFLAG NOPARENT   SYSLOG+WALL+EXEC
/etc/nut/upssched.conf:
CMDSCRIPT /usr/bin/upssched-cmd
PIPEFN /tmp/upssched.pipe
LOCKFN /tmp/upssched.lock
AT ONBATT * START-TIMER ups-on-battery 15
AT ONLINE * CANCEL-TIMER ups-on-battery
AT ONLINE * EXECUTE ups-back-on-line
AT REPLBATT * EXECUTE ups-change-battery  
AT LOWBATT * EXECUTE ups-low-battery  
AT FSD * EXECUTE ups-fsd
AT SHUTDOWN * EXECUTE ups-shutdown
AT COMMOK * EXECUTE ups-comunication-ok 
AT COMMBAD * EXECUTE ups-comunication-bad
Of course, en todo lo anterior pondremos el usuario que nos apetezca (en lugar de admin_sai), contraseña y direccionamiento IP correspondiente. Los que he puesto yo son de pega.

2. Proceso de apagado desde nut.

Con calma he leído todo lo que he encontrado sobre el proceso de eventos que genera NUT cuando el SAI está funcionando con batería y empieza a descargarse. Repasamos la secuencia de eventos:

  • El SAI está operando con corriente y con los 2 leds de la derecha del frontal encendidos. Se va la corriente eléctrica y se activa la batería del SAI. De los 6 leds que tiene se encienden en verde los 5 de la derecha, a modo de indicador de carga máxima y comienza a sonar un beep cada pocos segundos. Se irán apagando conforme se descargue la bateria y el beep sonará cada vez con mayor cadencia.
  • Si miramos el estado del SAI con "upsc salicru ups.status" vemos que está en "OB", On Battery. El demonio ups-monitor ha enviado el evento ONBATT para que lo procese el ups-scheduler.
  • Pasa el tiempo y la batería se va descargando. Con "upsc salicru battery.charge" podemos ver el % de carga.
  • Algunos SAI tienen un parámetro battery.charge.low que permite definir el umbral de carga mínima de la batería. Ese umbral se puede redefinir poniendo "override.battery.charge.low = 25" en /etc/nut/ups.conf, pero nuestro Salicru no lo tiene y el umbral está fijado en el 10%. Podemos poner dicho parámetro, reiniciar el demonio y ver con "upsc salicru" que battery.charge.low = 25, pero a la hora de la verdad dicho valor es ignorado y todo se comporta como ese 10% estuviese escrito en piedra.
  • En general: la forma de determinar si la batería está baja depende el SAI/driver concreto y se calcula en función de battery.charge/battery.charge.low y battery.runtime/battery.runtime.low.
  • Pasado el umbral de batería baja se genera un evento LOWBATT para que lo procese el ups-scheduler. Aquí empieza la fiesta.
  • Si nuestro nut tiene esclavos (como vimos en la entrada anterior del blog), se les notifica un evento FSD (forced shutdown). A su vez, cada esclavo:
    • Genera un evento SHUTDOWN.
    • Espera FINALDELAY segundos(tipicamente 5).
    • Ejecuta su propio SHUTDOWNCMD.
    • Desconecta del upsd maestro.
  • El nut maestro, si ha enviado el FSD a los esclavos espera HOSTSYNC segundos (típicamente 15) para darles tiempo a desconectar. Pasado esto se envía un FSD a si mísmo, lo cual:
    • Genera un evento SHUTDOWN.
    • Espera FINALDELAY segundos(tipicamente 5).
    • Crea el fichero POWERDOWNFLAG - usualmente /etc/killpower.
    • Ejecuta su propio SHUTDOWNCMD.
  • A diferencia del resto de código, el comando SHUTDOWNCMD se ejecuta con permisos de root. Esto es debido a que siempre hay en marcha dos instancias de upsmon, una con el usuario "nut" y otra con el usuario "root". Está última está casi siempre ociosa y solo existe para ejecutar lo que diga el parámetro SHUTDOWNCMD definido en upsmon.conf.
Esta es la teoría, haciendo una prueba en entorno real vamos a describir lo que pasa en la práctica:
  • Tengo conectado al SAI una fuente del servidor principal, un servidor auxiliar y un monitor. Desconecto el SAI de la corriente.
  • El parámetro ups.status se pone en OB. battery.charge baja rápidamente en menos de un minuto del 100% al 85-88%. Esto pinta mal.
  • Se estabiliza y empieza a bajar despacio, con los leds frontales apagándose conforme avanza la carga. Este es el ritmo:

    HORA CARGA STATUS EVENTO
    16:40 100% OL
    16:41 100% OB ONBATT
    16:42 88% OB
    17:09 69% OB
    17:24 65% OB
    17:42 29% OB
    17:45:11 15% OB
    17:45:25 12% OB
    17:45:51 10% OB LB FSD LOWBAT, FSD
    17:45:56 4% OB LB FSD EJECUCIÓN SHUTDOWNCMD
    17:46:11 0% OB LB FSD
    17:46:26 42% OL FSD VUELVE LA CORRIENTE
    ONLINE

    Para hacer el cuadrante anterior he capturado los datos con este script:
    #!/bin/bash
    
    while true
    do
       hora=$(date)
       carga=$(upsc salicru battery.charge 2> /dev/null)
       estado=$(upsc salicru ups.status  2> /dev/null)
       voltio=$(upsc salicru battery.voltage  2> /dev/null)
       echo "$hora -> ${carga}% $estado $voltio" >> /root/sai-status.txt
       echo "$hora -> ${carga}% $estado $voltio"
       sleep 15
    done
    
  • Conclusiones:
    • Hay una descarga rapidísima al comienzo, de un 12% en cuestión de segundos.
    • Luego va bajando con suavidad durante una hora. A los 30 minutos está al 70%. A los 60 llega al 15%.
    • Cuando llega al 15% empieza a bajar de nuevo en caída libre y en 2 minutos llega al 0%.
    • Al bajar del 10% se generan los eventos LOWBATT y FSD. Pocos segundos después se llama a SHUTDOWNCMD con credenciales de root.
    • Con la batería al 0% no se corta la corriente, pero no he querido tentar cuanto aguanta así hasta el apagado físico real.
    • Al conectar de nuevo el SAI a la corriente vuelve a cargar y el estado OB se transforma en OL, pero se queda activado el flag FSD ya que no ha llegado a suceder el apagado del servidor.

En resumen: el SAI aguanta 60 minutos o más con 2 servidores y un monitor, pero tiene una curva de descarga bastante acelerada al comienzo y al final. Desde que se genera el evento LOWBATT hasta que se llega al 0% de carga pasan segundos, lo que queda muy poco tiempo de maniobra si queremos apagar varias máquinas.

Una segunda cosa a que destacar que el SAI queda en estado "FSD OL" al volver la corriente en el último momento. Para quitar el flag FSD sin reiniciar el servidor hay que reiniciar el demonio nut-server, pero al hacer eso algunas veces (no siempre) se genera un evento FSD y se ejecuta un SHUTDOWCMD. Esto puede provocar que si no tenemos precauciones con lo que se hace en SHUTDOWNCMD se apague el servidor estando el SAI ya con corriente externa.

En general, siempre que llegamos al estado FSD (incluso manualmente haciendo "/sbin/upsmon -c fsd") tarde o temprano se ejecutará SHUTDOWNCMD, por lo que es algo que habrá que tener en cuenta.

En el siguiente apartado veremos como he abordado las cuestiones planteadas.

3. Configuración del apagado.

Ya que los pocos segundos que transcurren desde que se genera el LOWBATT+FSD hasta que la batería llega al 0% no me inspiran ninguna confianza lo que voy a hacer es que cuando se lleven 30 minutos de batería (momento en que está está sobre el 70%) se apague el servidor de forma segura.

Podría forzar más tiempo, pero si en media hora no ha vuelto la corriente parece que la cosa va para largo y... ¿para que esperar lo inevitable?

Modificamos /etc/nut/upssched.conf para incluir un nuevo timer de 1800 segundos:
CMDSCRIPT /usr/bin/upssched-cmd
PIPEFN /tmp/upssched.pipe
LOCKFN /tmp/upssched.lock
AT ONBATT * START-TIMER  ups-on-battery-shutdown 1800
AT ONLINE * CANCEL-TIMER  ups-on-battery-shutdown
AT ONBATT * START-TIMER ups-on-battery 15
AT ONLINE * CANCEL-TIMER ups-on-battery
AT ONLINE * EXECUTE ups-back-on-line
AT REPLBATT * EXECUTE ups-change-battery  
AT LOWBATT * EXECUTE ups-low-battery  
AT FSD * EXECUTE ups-fsd
AT SHUTDOWN * EXECUTE ups-shutdown
AT COMMOK * EXECUTE ups-comunication-ok 
AT COMMBAD * EXECUTE ups-comunication-bad
Los eventos son gestionados con /usr/bin/upssched-cmd:
#! /bin/bash
#
# This script should be called by upssched via the CMDSCRIPT directive.
# 
# Here is a quick example to show how to handle a bunch of possible
# timer names with the help of the case structure.
#
# This script may be replaced with another program without harm.
#
# The first argument passed to your CMDSCRIPT is the name of the timer
# from your AT lines.

function mailSend() {

   echo "$2" | mail -s "$1" -a "From: Avisos.ies "  tu.correo@gmail.com

}

MESSAGE_MAIL=""
EVENT_TYPE=$1
APAGADO=0
FECHA=$(date)

case "$EVENT_TYPE" in
    "ups-on-battery-shutdown")
      #Saltado evento AT ONBATT * START-TIMER  ups-on-battery-shutdown 1800: llevamos 30 minutos con bateria. Apagamos.
      MESSAGE_MAIL="UPS on battery: shutdown now"
      APAGADO=1
    ;;
    "ups-on-battery")
      MESSAGE_MAIL="UPS on battery: warning"
    ;;
    "ups-comunication-bad")
      MESSAGE_MAIL="Communications with UPS lost"
    ;;
    "ups-change-battery")
      MESSAGE_MAIL="UPS battery needs to be replaced"
    ;;
    "ups-back-on-line")
      MESSAGE_MAIL="UPS on line power"
    ;;
    "ups-low-battery")
      MESSAGE_MAIL="UPS battery is low"
    ;;
    "ups-comunication-ok")
      MESSAGE_MAIL="Communications with UPS established"
    ;;
    "ups-shutdown")  # Llega tras el FSD
      MESSAGE_MAIL="FSD: shutdown message"
    ;;
    *)
      MESSAGE_MAIL="Aviso tipo: $EVENT_TYPE"
    ;;
esac

mailSend  "$FECHA => $MESSAGE_MAIL"  "[UPS] Event $EVENT_TYPE de SAI Virgen de Guadalupe:"
echo "$FECHA => SAI: $MESSAGE_MAIL" >> /var/log/sai.log  # /var/log/sai.log permisos 664 y root:nut

if [ $APAGADO = "1" ]
then
    sleep 30
    /sbin/upsmon -c fsd #  Esto genera un evento ups-fsd y un evento ups-shutdown, que a su vez llama a SHUTDOWNCMD como root.
fi
La mayoría de los eventos mandan un correo y escriben en el log /var/log/sai.log. Tan sólo es difrente el salto del timer ups-on-battery-shutdown lanza una orden "/sbin/upsmon -c fsd", que genera a su vez un evento FSD y posteriormente dispara la ejecución de SHUTDOWNCMD, que apunta a /root/scripts/apagar_servidores.sh:
#!/bin/bash

function mailSend() {

   echo "$2" | mail -s "$1" -a "From: Avisos.ies "  tu.correo@gmail.com

}

FECHA=$(date)
estado=$(upsc salicru ups.status 2> /dev/null)
en_bateria=$(echo $estado | tr ' ' '\n' | grep "OB")

if [ -z $en_bateria ]  #Si en up.status no parece OB, no estamos en bateria. Es un SHUTDOWNCMD innecesario
then
   MESSAGE_MAIL="Evento SHUTDOWNCMD pero estado $estado. No apagamos el servidor"
   mailSend  "$FECHA => $MESSAGE_MAIL"  "[UPS] Evento SHUTDOWNCMD"
   echo "$FECHA => SAI: $MESSAGE_MAIL" >> /var/log/sai.log  # /var/log/sai.log permisos 664 y root:nut
else
   MESSAGE_MAIL="Apagado servidor por evento SHUTDOWNCMD"
   mailSend  "$FECHA => $MESSAGE_MAIL"  "[UPS] Evento SHUTDOWNCMD"
   echo "$FECHA => SAI: $MESSAGE_MAIL" >> /var/log/sai.log  # /var/log/sai.log permisos 664 y root:nut
   ssh root@servidor-auxiliar "/sbin/shutdown -h +0"
   sleep 10
   /sbin/shutdown -h +0
fi
exit 0
El script anterior recordemos que se ejecuta como root.

Primero comprueba que el SAI sigue en estado OB (en bateria). Si no es así, manda un mensaje diciendo que aunque se haya recibido un FSD-SHUTDOWN no se apagará nada. Esto previene que si tenemos el flag FSD activado estando OL (ON LINE, con corriente) se apague el servidor accidentalmente al reiniciar el demonio nut-server.

Si el SAI está en estado OB se mandan mensajes, se apaga un servidor auxiliar (con el que tenemos una relación de confianza para ejecutar comandos por ssh en él) y finalmente se apaga el servidor que ejecuta nut. Todo esto se hace a la media hora de estar funcionando con el SAI, por lo que da tiempo a cerrar todo tranquilamente ya que queda mas de la mitad de la batería.

4. Fuentes.

Acabo haciendo honores a las fuentes que he seguido para redactar esta entrada:

Y con esto creo que quedo cerrado del todo el tema de nut mientras que no suceda nada excepcional.

Nos leemos pronto.

No hay comentarios:

Publicar un comentario