02 noviembre 2011

Personalizando mi Sistema de Alimentación Ininterrumpida (SAI)

Dentro del mundo de la seguridad física, mantener los sistemas protegidos ante desastres naturales, picos de tensión producidos por una señal de corriente poco limpia o porque cae un rayo cerca (oye, que son cosas que pueden pasar…) puede generar que nuestros equipos, tanto los que dan un servicio como los nuestros de casa, sufran graves consecuencias.

Por otra parte, una pérdida repentina de corriente eléctrica, puede derivar en una pérdida de datos, tanto por no haber salvado convenientemente el trabajo, como por una corrupción de datos en los sistemas de ficheros que, repentinamente dejan de disponer de su energía vital.

Para evitarlo, lo mejor a pequeña y gran escala, es utilizar servicios de alimentación ininterrumpida, más conocidos como SAI en español o UPS (Uninterruptible Power System) en inglés.

Por supuesto no es lo mismo provisionar un SAI para suministrar energía de forma secundaria a un datacenter completo, que para un hogar o una pequeña oficina. Para los primeros se suelen disponer de generadores de electricidad usando gas-oil por ejemplo, como de sistemas especiales para limpiar de ruidos la señal eléctrica y proveer de una tensión estable a todos los equipos durante el rato que dure el "apagón". Para el segundo caso, hay alternativas más asequibles y menos aparatosas, que nos permitan dar unos minutos más de vida en caso de apagón.

En mi caso, el SAI que disponía para un servidor y un PC con mi FreeNAS, funcionando 24x7, me dio la sorpresa de pasar a mejor vida la semana pasada. Así pues me tuve que poner a la busca y captura de un nuevo SAI que evite los picos de tensión de la instalación de mi casa que puedan derivar en males mayores, como roturas de fuentes de alimentación y otros componentes enchufados.

Gracias al consejo de mi amigo Juanjo, me decidí por un SAI del fabricante Eaton. En este caso, y ya que va a tener que suministrar corriente al menos a dos máquinas, el que adquirí es de 1200 VA (Voltiamperios). Cuanto mayor sea este valor, mayor cantidad de tiempo y a mayor número de dispositivos, será capaz de suministrar corriente el SAI. El anterior, un Belkin, era de 600VA, lo que normalmente suele ser suficiente para un único PC. El Eaton permite además enchufar cuantas cosas se desee al disponer de enchufes normales y corrientes, por lo que aproveché a enchufar otros cacharros que no afectarán en demasía el consumo de la batería.

Después de una primera carga de 12 horas sin tener nada enchufado en el SAI, me dispuse a averiguar qué capacidades de monitorización ofrecía. Mediante el cable USB suministrado, venía un software propio llamado Eaton-IPP que permite, de forma web, analizar y configurar el equipo. Asimismo permite establecer alarmas, ejecutar comandos, etc,… Bonito es, pero… no todo lo personalizable que a mí me gustaría.


Me puse a investigar en uno de mis sitios favoritos, http://search.cpan.org, qué modulitos Perl existen para poder trastear con un SAI.

En general, el esquema de componentes se intuye sencillo: Tiene que haber un módulo que es capaz de "hablar" con el SAI a través del cable USB. A partir de ahí, alguna herramienta cliente que permita preguntarle cosas al SAI y tomar decisiones según mi propio criterio. Navegando, dí con una página albergada por Eaton con diversas opciones, entre ellas NUT, compatible, al menos en principio, con el módulo Perl UPS-NUT que encontré en CPAN.

Después de pegarme un rato con temas variopintos para hacer que NUT detecte el SAI (importante instalar el paquete nut-hal que es el que incorpora el driver usbhid-ups en /usr/libexec/hald-addon-usbhid-ups), logré hacer que funcionase KNut y upsc, herramienta cliente que es capaz de devolvernos información suficiente sobre cómo está el SAI:

[root@Carmen tmp]# upsc mge@localhost
battery.charge: 100
battery.charge.low: 20
battery.runtime: 2102
battery.type: PbAc
device.mfr: EATON
device.model: Ellipse ECO 1200
device.serial: 000000000
device.type: ups
driver.name: usbhid-ups
driver.parameter.pollfreq: 30
driver.parameter.pollinterval: 2
driver.parameter.port: auto
driver.parameter.vendorid: 0463
driver.version: 2.4.3
driver.version.data: MGE HID 1.18
driver.version.internal: 0.34
input.transfer.high: 264
input.transfer.low: 184
outlet.1.desc: PowerShare Outlet 1
outlet.1.id: 2
outlet.1.status: on
outlet.1.switchable: no
outlet.2.desc: PowerShare Outlet 2
outlet.2.id: 3
outlet.2.status: on
outlet.2.switchable: no
outlet.desc: Main Outlet
outlet.id: 1
outlet.switchable: no
output.frequency.nominal: 50
output.voltage: 230.0
output.voltage.nominal: 230
ups.beeper.status: enabled
ups.delay.shutdown: 20
ups.delay.start: 30
ups.firmware: 01
ups.load: 12
ups.mfr: EATON
ups.model: Ellipse ECO 1200
ups.power.nominal: 1200
ups.productid: ffff
ups.serial: 000000000
ups.status: OL CHRG
ups.timer.shutdown: 0
ups.timer.start: 0
ups.vendorid: 0463

El servicio "upsd" se configura para escuchar en el puerto 3493

[root @Carmen tmp]# netstat -tanp |grep -i upsd|grep -v cups
tcp        0      0 127.0.0.1:3493     0.0.0.0:*                  LISTEN      4465/upsd  

Contando con el módulo perl UPS-NUT, en mi cabeza sonaba divertido poder monitorizar la carga del SAI para poder actuar como yo considere necesario si éste se descarga completamente. Es decir, para un corte puntual de un breve espacio de tiempo, el SAI es capaz de soportar perfectamente los requerimientos de energía. Sin embargo, si el corte de corriente es de más tiempo, hay que actuar para evitar que existan corrupciones de datos de las aplicaciones más críticas o que utilicen sistemas de ficheros del NAS, por ejemplo.

Además, dado lo que viajo, el hacer que los servidores se apaguen de forma ordenada está muy bien, pero, cuando la energía vuelva, no siempre hay alguien en casa para darle al botón de encendido. Las BIOS de ambos servidores, disponen de una opción que permite, al detectar una fuente de energía, que recuperen el estado anterior. Es decir, que si se han apagado de forma controlada, no se encenderán, y si se han apagado de forma repentina, sí que lo harán, por lo que entonces interesa que se apaguen de golpe, pero mitigando las pérdidas de datos. 

Así, necesitaba que en mi rutina de "apagado", cuando no quede más vida en el SAI, se deshabiliten servicios que utilicen sistemas de ficheros remotos del NAS, máquinas virtuales, etc, etc,… de manera que, aunque se apague solo el servidor, exista el menor número de servicios corriendo y sistemas de ficheros remotos montados para evitar corrupciones de datos.

Sin embargo, imaginemos que, en el impás de tiempo en el que hemos dejado el servidor con lo mínimo esperando a que se apague solo, se restablece el suministro de corriente eléctrica: El SAI empieza a cargar las baterías de nuevo y nos quedamos con el servidor pletórico de vitalidad, pero muerto en cuanto a funcionalidad.

Para haceros la historia corta, no he logrado hacer un programa que funcione de forma correcta con el módulo perl UPS-NUT. No me devuelve ni un solo valor…

Menos mal que el comando anterior upsc permite pedir diferentes valores al SAI en modo línea de comandos.

[root@Carmen tmp]# upsc mge@localhost ups.status
OL CHRG
[root@Carmen tmp]# upsc mge@localhost battery.charge
100
[root@Carmen tmp]# upsc mge@localhost ups.load
12

Sí, como bien estáis pensando, es exactamente lo que he hecho: Quick & dirty, pero funciona!

Finalmente me hice mi propio monitor de SAI como otro script más en perl,… total uno más corriendo en el servidor, no se va a notar.

Entre otras cosas, además monitorizo la relación entre el voltaje saliente actual y el nominal, de manera que me notifique en el caso que haya una sobretensión o una caída de tensión... No vale para mucho si no estás cerca, pero puede permitir la detección de alguna anomalía en el SAI. Una lástima que este equipo no permita monitorizar la temperatura de las baterías puesto que en verano suelen recalentarse y es interesante saber si hay que ir pensando en llamar al 112 o no.

Aquí comparto el script con vosotros, lectores, por si os es de alguna utilidad:

[root@Carmen tmp]# more /usr/local/bin/sai_monitor.pl

#!/usr/bin/perl

#Monitorizando mi SAI

my $minutes=10; #num minutos umbral de aguante
my $freq_check=60; #num segundos para cada poll
my $umbral_load=50; #Umbral de carga de suministro del SAI
my $umbral_batt=10; #Umbral de carga de bateria (no retorno)

sub agonizando
{#Pre-apagado de la máquina cuando el SAI ya no aguanta más
       #Tiro servicios que dependen de sitios remotos
       system "/etc/init.d/mldonkey stop"; #Apago el emule

       #Apago maquinas virtuales de forma ordenada
       system "/usr/bin/VBoxManage controlvm WXP poweroff";
       system "/usr/bin/VBoxManage controlvm pinsafe poweroff";

       #Apago servicios en los que requiero integridad
       system "killall -9 httpd"; #Apago servidor web publico
       system "/etc/init.d/httpd stop"; #Apago servidor web interno
       system "/etc/init.d/syslog-ng stop"; #Apago syslog-ng
       system "/etc/init.d/mysql_chroot stop"; #Apago mysql publico
       system "/etc/init.d/mysqld stop"; #Apago mysql privado

       #Desmonto sistema de ficheros remoto NFS para emule almacenado en NAS
       system "/bin/umount /var/mlnet/temp";

       #Desmonto sistema de ficheros Samba almacenados en NAS
       system "/bin/umount /mnt/samba_remote"; 
       system "/bin/umount /var/backup";

       #Apago servicios necesarios para montaje remoto via NFS
       system "/etc/init.d/rpcidmapd stop";
       system "/etc/init.d/rpcgssd stop";
       system "/etc/init.d/rpcbind stop";

       my $TTL = $minutes * 60; #Calculo segundos a esperar

       sleep $TTL; #Espero X minutos -> Si aguanta el SAI y no vuelve el suministro de corriente,...

       #TO DO: Sync en NAS. Aseguro que no hay "clientes" usando ficheros remotos       

       system "reboot"; 
# Si después de X minutos sigue viva, es que la corriente volvió y la máquina no se apagó sola, así que fuerzo el reinicio para que los servicios de la máquina y sistemas de ficheros vuelvan a la vida de forma limpia y ordenada
}

#MAIN

for (;;)
{#bucle infinito
       my $ups_status = `upsc mge@localhost ups.status`;
       #print "STATUS ES: $ups_status\n";
       
       if ($ups_status =~ /OB/)
       {#Significa que estamos con Bateria
             my $ups_carga = `upsc mge@localhost battery.charge`;
             #Veamos con cuanta
             #print "CARGA DE BATERIA ES: $ups_carga\n";

             if ($ups_carga < $umbral_batt) 
             {#Hay que actuar 
                     #Notifico con alta prioridad 
                     system ("/usr/local/bin/notifier.pl 3 \"SAI agonizando. Autodestrucción en 3,2,1. Carga $ups_carga\""); 
                     agonizando; #Ejecuto rutina 'agonizando' 
              } 
        } 
        elsif ($ups_status =~ /OL/) 
        {#Significa que está con corriente 
              if ($curr_status != $ups_status) 
              {#Si antes estaba en baterías y ahora con corriente es q se ha recuperado 
                     #Notifico nada más con prioridad 2
                     system ("/usr/local/bin/notifier.pl 2 \"SAI se ha recuperado\""); 
              } 
        } 
    
        $curr_status=$ups_status; #Guardo estado nuevo de ups 
        
        my $ups_load = `upsc mge@localhost ups.load`; 
        #print "CARGA DE SUMINISTRO DE SAI ES: $ups_load\n"; #comprobamos carga de suministro del SAI 
        if ($ups_load > $umbral_load)
        {#Si la carga de suministro del SAI es mayor que lo definido, notificamos
               system ("/usr/local/bin/notifier.pl 3 \"Carga del SAI es $ups_load\"");
        }

        my $ups_current_voltage=`upsc mge@localhost output.voltage`; #voltaje actual
        my $ups_nominal=`upsc mge@localhost output.voltage.nominal`; #voltaje nominal
        chomp ($ups_current_voltage);
        chomp ($ups_nominal);
        
        #Calculamos relacion entre voltaje saliente actual y el nominal
        my $voltaje = ($ups_current_voltage/$ups_nominal) * 100;

        if ($voltaje > 109)
        {#Más de 110% entre voltaje actual y nominal: Se detecta sobretensión, notifico
                system ("/usr/local/bin/notifier.pl 3 \"Sobretensión en el SAI $voltaje%\"");
        }
        elsif (($voltaje > 79) && ($voltaje < 86))
        {#Si la relacion entre voltaje actual y nominal está entre 80% y 85%, se detecta caída de tensión. Notifico
                system ("/usr/local/bin/notifier.pl 3 \"Caída de tensión en el SAI $voltaje%\"");
        }

        sleep ($freq_check);
}

3 comments :

coder dijo...

     #Apago servicios en los que requiero integridad       system "killall -9 httpd"; #Apago servidor web publicoUn poco hardcore ese kill, ¿no? xD

Lorenzo_Martinez dijo...

Un poco sí. Pero sin embargo, todo tiene su explicación ;D
Créeme, es correcto y funciona de momento bien así

funky dijo...

a mí lo que me ha hecho gracia es ver a alguien usando todavía el mldonkey :D