SyntaxHighlighter

viernes, 21 de marzo de 2014

Data Protector. Recuperación usando la directiva POST-EXEC y SCRIPTING

Con Data Protector podemos programar una agenda de copias de seguridad fácilmente usando la GUI Java, lo que no es tan simple es planificar una calendario de restauración que, por ejemplo, recupere la última copia de seguridad realizada en otra localización.

La primera aproximación sería intentar ejecutar esta restauración llamando al programa omnir con los argumentos necesarios e incluir la llamada en el cron del servidor para que se ejecute periódicamente. Lo que a priori parece algo sencillo se complica con sólo mirar la la página de manual de omnir (o buscar "data protector cli reference" en cualquier buscador). El Backend de comandos de Data Protector da toda la funcionalidad que necesitamos para restaurar una copia de seguridad, pero es necesario la ejecución de varias órdenes para "montar" los argumentos que necesita omnir.

Hay otro problema en cuanto a la inclusión en cron de esta orden podría dar lugar a solapamientos en el uso de la unidad sobre la que estemos haciendo la copia de seguridad.

Imaginad que la copia de seguridad programada comienza a las 2:00 am, y nuestra orden de restauración se ejecuta vía cron a las 4:00 am. Si la copia de seguridad tarda más de 2h, la restauración se podrá en cola. Pasado cierto tiempo de espera configurable en Data Protector, si aún no ha comenzado el trabajo de restauración, se procederá a su cancelación. Esto se puede evitar usando las opciones POST-EXEC y PRE-EXEC del trabajo de copia de seguridad.

Estas dos opciones se encargan de ejecutar un script en el cliente de data protector que indiquemos antes (PRE-EXEC) o después (POST-EXEC) de que se realice el trabajo de backup. El uso más extendido de estas dos opciones es el de parar un servicio antes de realizar el trabajo de copia y levantarlo después.

Para conseguir el comportamiento que necesitamos lo ideal sería ejecutar un script en POST-EXEC que llame a omnir con los datos de la última copia de seguridad. Para conseguir los datos necesarios de la última sesión de copia de seguridad hay que ejecutar la orden onmidb, que consulta la BD de Data Protector para extraer la información que requiere omnir:

/opt/omni/bin/omnir -filesystem [datalist] [label] -session [session] -tree [path] -as [target_path] -target [target_client]

Pasos del script:
  1. Considera un número de días hacia atrás desde la fecha actual para buscar la última sesión de la agenda especificada.
  2. Determinar sesión más reciente que se terminó satisfactoriamente en el intervalo definido en el paso anterior.
  3. Buscar el árbol de directorios que queremos restaurar y extraer el datalist y label, argumentos necesarios para ejecutar omnir.
  4. Ejecutar omnir con los parámetros adecuados.
El código del script (un poco sucio):

#!/bin/bash
OMNI_PATH=/opt/omni/bin/

if [ $# -ne 5 ]; then
  echo "Ilegal number of arguments" >&2
  echo "Use: $0 [dataList] [label] [targetClient] [targetDir] [RestoreOnlyIfFull 0/1]" >&2
  exit 1
fi

ARG_DATALIST=$1
ARG_LABEL=$2
ARG_TARGETCLIENT=$3
ARG_TARGETDIR=$4
ARG_ONLYFULL=$5
DAYSTOINSPECT=30

INITIALDATE=$(date '+%Y/%m/%d' --date="${DAYSTOINSPECT} days ago")
LASTSESSION=$(${OMNI_PATH}omnidb -session -datalist $ARG_DATALIST -since $INITIALDATE | grep -i backup | grep -i completed | tail -n1 | cut -f 1 -d " ")
FILESYSTEM=$(${OMNI_PATH}omnidb -session $LASTSESSION | grep \'${ARG_LABEL}\' | cut -f1 -d " ")
LABEL=$(${OMNI_PATH}omnidb -session $LASTSESSION | grep \'${ARG_LABEL}\' | cut -f2 -d " ")
BACKUP_LEVEL=$(${OMNI_PATH}omnidb -filesystem $FILESYSTEM ${ARG_LABEL} -session $LASTSESSION | egrep -o "Incremental|Full")

if [ -z "$LASTSESSION" ]; then
 echo "Session not found: $ARG_DATALIST. Available schedules:"
 ls /etc/opt/omni/server/schedules >&2
 exit 1
fi

if [[ $ARG_ONLYFULL -eq 1 && $BACKUP_LEVEL != "Full" ]]; then
 echo "Mode is set to full but cannot find full backup" >&2
 exit 1
fi

if [ -z "$LABEL" ]; then 
 echo "Label not found: $ARG_LABEL. Backed up filesystems and labels:"
 omnidb -filesystem >&2
 exit 1
fi

#Print command options
echo "Restoring label: ${LABEL}, filesystem: < $FILESYSTEM > from Session: $LASTSESSION into ${ARG_TARGETCLIENT}:${ARG_TARGETDIR}, backup_level: $BACKUP_LEVEL"
#Print omnir command used to perform the restore job. Debug.
echo ${OMNI_PATH}omnir -filesystem ${FILESYSTEM} ${LABEL} -session ${LASTSESSION} -tree ${ARG_LABEL} -as ${ARG_TARGETDIR} -target ${ARG_TARGETCLIENT}
OMNI_COMMAND="${OMNI_PATH}omnir -filesystem ${FILESYSTEM} ${LABEL} -session ${LASTSESSION} -tree ${ARG_LABEL} -as ${ARG_TARGETDIR} -target ${ARG_TARGETCLIENT}"
eval $OMNI_COMMAND

Si a la orden omnir que se llama al final del script añadimos -full antes del argumento -filesystem se restaurará una copia completa del árbol de directorios usando la última copia completa que se realizó y todas las incrementales hasta la sesión que estamos restaurando. De otra manera sólo se restaurarán los ficheros que se respaldaron en la sesión en cuestión.  El último argumento del script controla los problemas que se puedan derivar de esto: sólo se ejecutará la restauración si la última copia ha sido full.

No está de más ejecutar el script comentando la última línea (eval ...), para comprobar que todo es correcto antes de empezar a utilizarlo. Si alguno de los parámetros no es correcto imprimirá posibles soluciones.

Por último, el directorio en el que tenemos que incluir el script para que no haya problemas es /opt/omni/lbin y así podremos llamarlo sin usar path completo, algo que parece que no funciona bien si configuramos el POST-EXEC desde la GUI Java.

NOTA. Si la restauración toma demasiado tiempo, Data Protector puede abortar el Script. Para evitarlo se puede modificar la opción ScriptOutputTimeout en el fichero /etc/opt/omni/server/options/global

jueves, 20 de marzo de 2014

Netstat y Zabbix

Ya he hablado otras veces de la herramienta de monitorización de servidores Zabbix y de lo fácil que resulta extenderla.

Hoy hemos recibido una petición por parte de compañeros para que añadiéramos varios monitores más a los datos que se recogen de los servidores, en particular, datos derivados de la ejecución de la orden netstat que devuelve datos sobre las conexiones de red establecidas con el servidor. Por ejemplo, para ver el número de conexiones establecidas a un determinado puerto, ya sean de salida o de entrada. La orden en cuestión es: netstat -an | grep STABLISHED  | grep [port]

Para poder parametrizar la orden he creado un fichero de configuración de zabbix similar al que hay en varios foros para valores de mysql, que nos permite utilizar argumentos. Fichero userparameter_netstat.conf (crear en /etc/zabbix/ e incluir desde el dichero /etc/zabbix/zabbix_agentd.conf):

# Flexible parameter to grab global variables. On the frontend side, use keys like netstat.port[Port_number].
# Key syntax is netstat.port[Port_number].
UserParameter=netstat.port[*],netstat -na | grep $1 | grep ESTABLISH | wc -l
O mejor aún (actualizado):
UserParameter=netstat.port[*],netstat -na | grep ESTABLISH | egrep ':$1[[:blank:]]+' | wc -l

Para considerar sólo cadenas que del tipo: "dos puntos + puerto especificado + espacios" y así, evitar que se incluyan en el conteo líneas que en realidad no son conexiones a ese puerto.
Con esto conseguimos que zabbix_agent responda a peticiones de un nuevo parámetro: netstat.port[puerto] con la orden

netstat-na | grep puerto | grep STABLISH | wc -l

que devuelve el número de conexiones al puerto que se le pase.

Activamos los cambios reiniciando el servicio zabbix-agent. Y probamos que la instrucción funciona correctamente llamando al parámetro por línea de órdenes:
[root@host zabbix]# zabbix_agentd --test netstat.port[80]
netstat.port[80]                            [t|4]

En caso de no estar bien configurado la respuesta sería
[root@host zabbix]# zabbix_agentd --test netstat.port[80]
netstat.port[80]                            [t|NOT_SUPPORTED]

Ya tenemos la "parte cliente" hecha. Ahora toca crear una plantilla que podamos asociar a nuestros hosts para que se llame a ese parámetro de usuario.

Vamos a la sección de plantillas dentro de la administración de Zabbix y creamos una nueva plantilla. Añadimos tantos items como puertos queramos monitorizar. Por ejemplo para el puerto HTTP usaríamos la siguiente definición de item:

netstat.port[80]

Añadimos los items que queramos y por último podemos incluso añadir un gráfico a la plantilla añadiendo todos los monitores de netstat.

Una vez creada la plantilla hay que enlazarla al host que queremos monitorizar y que debe tener en su fichero de configuración la definición del userparameter. Activamos los items para que comience su monitorización y listo.

Archivo XML de la plantilla:

    2.0
    2014-03-20T12:10:04Z
    
        
            Templates
        
    
    
        
    
    
        
            Connections (netstat)
            500
            200
            0.0000
            100.0000
            1
            1
            0
            1
            0
            0.0000
            0.0000
            0
            0
            0
            0
            
                
                    0
                    0
                    00DD00
                    0
                    2
                    0
                    
                        Template_Netstat
                        netstat.port[80]
                    
                
                
                    1
                    0
                    BBBB00
                    0
                    2
                    0
                    
                        Template_Netstat
                        netstat.port[43]
                    
                
                
                    2
                    0
                    CC00CC
                    0
                    2
                    0
                    
                        Template_Netstat
                        netstat.port[3306]
                    
                
                
                    3
                    0
                    0000CC
                    0
                    2
                    0
                    
                        Template_Netstat
                        netstat.port[1251]