19 dic 2014

IBM i (iSeries / AS/400) - Haciendo respaldos en la nube/red (Cloud Backup)

Hoy voy a volverme algo tecnócrata. No creo que sea necesario que empiece con una explicación sobre que es la nube, pero para quienes no sepan de que se trata, pueden consultar información en WikiPedia http://es.wikipedia.org/wiki/Cloud_computing .

Lo cierto es que hoy disponemos de espacio en múltiples servidores, algunos gratuitos, otros los estamos pagando.
Podemos mencionar los casos de Microsoft OneDrive (ex-SkyDrive), que al comprar Microsoft Office 365 por 1 año nos da espacio de 1 TB, Dropbox que nos "regala" 5 GB o nuestro hosting web, que nos da una cantidad de espacio que dificilmente llenemos con nuestros archivitos HTML.

También es cierto que a pesar de seguir usando medios magnéticos como cartuchos de cinta para hacer respaldos, estos requieren de ser migrados cada tantos años, pues la tecnología avanza y los dispositivos que permiten leer estos medios se vuelven obsoletos.

Y por último, y por qué no decirlo, la moda de hacer algo en "la nube". Hoy las empresas están explorando y haciendo uso de servicios como Amazon EC2, ES2, Google Drive y Google Storage, Servidores Dedicados (el que prefieran), Dropbox para empresas, OneDrive empresarial, Softlayer y su nube "bare-metal", etc.

Pero cómo hacemos para subir información a esos servicios?
En el caso de DropBox es relativamente fácil: Se dispone de una suerte de "USB Virtual" donde copiamos los archivos desde Windows, y alguna gente ha podido crear scripts usando cURL para subirlos desde Linux e incluso Unix. Con OneDrive, al ser de Microsoft, sólo se puede hacer desde Windows, y una gran desventaja a mi criterio, es que deja una copia "local" en el disco de la PC.

Pero volvamos al mundo de los servidores de las empresas. Cómo saco jugo a tanto espacio tan barato para almacenar mis respaldos que no siempre son indispensables para mi operación, como podrían ser los servidores de desarrollo y pruebas de la empresa. También podemos hacerlo sobre servidores productivos, pero a mi criterio sería bueno contar con un servicio de buen desempeño y con ciertas garantías como es el caso de SoftLayer.

Como siempre, este tipo de cosas requieren de algún tipo de "solución". En nuestro caso hablamos de diferentes componentes que se integran para poder hacer el respaldo:

1) Un lugar donde salvarlo (asumamos un servidor FTP en nuestro hosting, que es lo más simple)
2) Un medio de transporte/comunicación. Lo ideal es usar Internet por su costo, pero si los datos son confidenciales (como suele ser el caso), debemos pensar en mecanismos de seguridad en los datos, en el medio de comunicación, o en ambos.
3) Un mecanismo de respaldo. Hasta ahora usamos medios físicos. Para medios electrónicos necesitamos un software de respaldo que guarde a disco en un formato de archivo que podamos transferir, o bien algún tipo de sistema de archivado, como solía ser el WINZIP de Windows.
4) Un mecanismo de reducción/compresión de la información. Existen diferentes mecanismos para hacer esto, tanto a nivel servidor como en una PC. Los mejores mecanismos son los que usan deduplicación, pero requieren producos licenciados o el uso de algunas componentes "experimentales" como podrían poner en riezgo la integridad de los datos, adicionado a un poder de cómputo más elevado. En esta nota trataremos de mecanismos de compresión y de algunas técnicas de respaldo que permiten reducir el volumen de la información.
5) Un mecanismo de restauración. Si respaldamos debemos poder restaurar la información.

Si hablamos de sistemas Linux/*nix (AIX/HP-Ux/Solaris/etc.) la solución suele ser medianamente simple, tar+gzip. Generamos un archivo tar del "filesystem" o directorio deseado y lo "g-zipeamos" para reducir su tamaño. El gzip es, según diferentes análisis, el mecanismo más veloz a la hora de comprimir información, aunque quizá no sea el más eficiente en cuanto a tamaño de los archivos comprimidos.

Volviendo al mundo de las pantallas verdes, todo es bastante diferente cuando se trata del AS/400-iSeries-IBM i. Su sistema operativo tiene algunas restricciones en cuanto a nombres largos, compatibilidad con Linux/*nix y por lo tanto con programas de compresión.
La gran ventaja que tenemos es el poderoso comando SAVLIB y sus hermanos SAVOBJ y SAVCHGOBJ, así como las posibilidad de respaldar en cintas o en SAVE FILES (*SAVF)
Por otro lado, tenemos herramientas como el PASE o el QSHELL, que nos permiten ejecutar algunos comandos de AIX (el sabor IBM del Unix).

Juntando todo esto podemos crear un mecanismo de respaldo que haga lo siguiente:

1) Que diariamente haga un salvado de cada biblioteca que indicemos en un SAVF (archivo de respaldo)
2) Comprima la información con algún mecanismo de encrypción que nos permita asegurar la información
3) Transfiera los datos a nuestro sitio, en este caso FTP.

Las componentes de nuestra "solución" son las siguientes:

a) Un programa/script que llame a mis rutinas: BP7ZI (CLLE)

PGM                                                              
    SNDMSG     MSG('Creando archivo de comandos FTP') +          
                 TOUSR(*REQUESTER)                               
    CALL MYLIB/BKPFTP01b                                     
    SNDMSG     MSG('Salvando bibliotecas a disco') +             
                 TOUSR(*REQUESTER)                               
    CALL MYLIB/BKPSAVF01i                                    
    SNDMSG     MSG('Enviando respaldos por FTP') +               
                 TOUSR(*REQUESTER)                               
    CALL MYLIB/BKPFTP02b                                     
ENDPGM  
  
      
                     
                            


b) Un programa  que cree los comandos que ejecutaré en modo batch para transferir los archivos


BKPFTP01b (CLLE)                                

PGM                                                 
DCL        VAR(&SYSDATE) TYPE(*CHAR) LEN(6)         
DCL        VAR(&LILIAN) TYPE(*CHAR) LEN(4)          
DCL        VAR(&WDATE) TYPE(*CHAR) LEN(8)           
DCL        VAR(&JUNK1) TYPE(*CHAR) LEN(8)           
DCL        VAR(&JUNK2) TYPE(*CHAR) LEN(23)          
DCL        VAR(&FTPCMD) TYPE(*CHAR) LEN(256)        
DCLF       FILE(MYLIB/BKPLIB)                   
RTVSYSVAL  SYSVAL(QDATE) RTNVAR(&SYSDATE)           
CALLPRC    PRC(CEELOCT) PARM((&LILIAN) (&JUNK1) +   
             (&JUNK2) (*OMIT))                      
CHGVAR     VAR(%BIN(&LILIAN)) VALUE(%BIN(&LILIAN) ) 
CALLPRC    PRC(CEEDATE) PARM((&LILIAN) ('YYYYMMDD') +
             (&WDATE) (*OMIT))                      
QSH        CMD('echo open ftp.myserver.com.mx > +  
           /home/backupsav/backupsav.ftp')          
QSH        CMD('echo user myuser
mypassword +

           >>/home/backupsav/backupsav.ftp')
QSH        CMD('echo bin    >> +                   
           /home/backupsav/backupsav.ftp')         
QSH        CMD('echo namefmt 1 >> +                
           /home/backupsav/backupsav.ftp')         
CHGVAR     VAR(&FTPCMD) VALUE('echo mkdir ' *bcat +
             &WDATE *BCAT ' >> +                   
             /home/backupsav/backupsav.ftp')       
QSH        CMD(&ftpcmd )                                
CHGVAR     VAR(&FTPCMD) VALUE('echo cd ' *bcat &WDATE + 
             *BCAT ' >> /home/backupsav/backupsav.ftp') 
QSH        CMD(&ftpcmd )                                
CHGVAR     VAR(&FTPCMD) VALUE('echo put +               
             /home/backupsav/savsecdta.7z  +            
           >> /home/backupsav/backupsav.ftp')           
                                                        
QSH        CMD(&ftpcmd )                                
                                                        
LEE:        

RCVF                                                
MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(FIN))    
                                                                
CHGVAR     VAR(&FTPCMD) VALUE('echo put +           
                         /home/backupsav/'  *TCAT &ODOBNM *TCAT +
                         '.7z' *BCAT +                          
                       '>> /home/backupsav/backupsav.ftp')      
QSH        CMD(&FTPCMD)                             
GOTO       CMDLBL(LEE)                              
FIN:

QSH    CMD('echo quit >>/home/backupsav/backupsav.ftp')   
CPYFRMSTMF FROMSTMF('/home/backupsav/backupsav.ftp') +    
             TOMBR('/QSYS.LIB/MYLIB.LIB/FTPBKP.FILE/+ 
             FTPIN.MBR') MBROPT(*REPLACE)                 
ENDPGM


c) un archivo MYLIB/BKPLIB que contenga los nombres de las bibliotecas a respaldar. Este lo he creado usando DSPOBJD (Display Object Description) de la biblioteca QSYS para todos los objetos de tipo *LIB y luego depuré lo que no deseaba.

d) Un servidor de FTP: en este caso se llama ftp.myserver.com.mx con usuario "myuser" y password "mypassword" (son sólo ejemplos)

e) Un directorio en el IFS llamado /home/backupsav. Lo podemos crear con 
el siguiente comando en pantalla verde:
QSH CMD('mkdir /home/backupsav')     
f) Un programa para hacer el salvado
 BKPFTP01b (CLLE)

             PGM                                                   
             DCL        VAR(&ENCKEY) TYPE(*CHAR) LEN(64)           
             DCL        VAR(&zipCMD) TYPE(*CHAR) LEN(256)          
             dcl        var(&FECHA) type(*CHAR) LEN(8)             
             DCL        VAR(&DIA)  TYPE(*CHAR) LEN(2)              
             DCL        VAR(&NDIA) TYPE(*DEC) LEN(2 0)             
             DCL        VAR(&MES)  TYPE(*CHAR) LEN(2)              
             DCL        VAR(&ANIO)  TYPE(*CHAR) LEN(2)             
             DCL        VAR(&DOW) TYPE(*CHAR) LEN(4)               
             DCLF       FILE(MYLIB/BKPLIB)                     
             RTVDTAARA  DTAARA(MYLIB/BKP7ZPSWD *ALL) +         
                          RTNVAR(&ENCKEY)                          
             CRTLIB LIB(BACKUPSAV) TEXT('Biblioteca de Backup')    
             MONMSG     MSGID(CPF2111)                             
             CLRLIB     LIB(BACKUPSAV)                             
             /* Borro los archivos  SAVF y 7z que hayan quedado */ 
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +          
                       rm /home/backupsav/*.7z"')                
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +          
                       rm /home/backupsav/*.savf"')              
            /* Creo el archivo  SAVF con perfiles de usuarios */ 
            CRTSAVF    FILE(BACKUPSAV/SAVSECDTA) TEXT('Seguridad')
            SAVSECDTA  DEV(*SAVF) SAVF(BACKUPSAV/SAVSECDTA) +    
                         DTACPR(*NO) COMPACT(*NO)                
            /* Muevo el archivo  SAVF al IFS */                  
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +              
                         mv /QSYS.LIB/BACKUPSAV.LIB/SAVSECDTA.FILE + 
                         /home/backupsav/SAVSECDTA.savf"')           
            /* Comprimo el archivo  SAVF */                          
            CHGVAR     VAR(&zipCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                        7za a -p' *tcat +                            
                        &ENCKEY *BCAT '/home/backupsav/savsecdta.7z +
                          /home/backupsav/savsecdta.savf"')          
                                                                     
            QSH        CMD(&ZIPCMD)                               
            /* Borro el archivo  SAVF */                          
            QSH        CMD('/QOpenSys/usr/bin/sh -c " +           
                       rm /home/backupsav/SAVSECDTA.savf"')       
            MONMSG     MSGID(CPF0000)                             
LEE:        RCVF                                                  
            MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(FIN))      
            CRTSAVF    FILE(BACKUPSAV/&ODOBNM) TEXT(&ODOBTX)      
            MONMSG     MSGID(CPF0000)                             
            RTVSYSVAL  SYSVAL(QDAYOFWEEK) RTNVAR(&DOW)               
            RTVSYSVAL  SYSVAL(QDAY) RTNVAR(&DIA)                     
            RTVSYSVAL  SYSVAL(QMONTH) RTNVAR(&MES)                   
            RTVSYSVAL  SYSVAL(QYEAR) RTNVAR(&ANIO)                   
            CHGVAR     VAR(&FECHA) VALUE('01' *TCAT &MES *TCAT +     
                         '20' *TCAT &ANIO)                           
            CHGVAR     VAR(&NDIA) VALUE(&DIA)                        
            IF         COND((&DOW  = '*MON') *OR (&NDIA=1)) THEN( +  
                       SAVLIB LIB(&ODOBNM) +                         
                         DEV(*SAVF) SAVF(BACKUPSAV/&ODOBNM) +        
                         SAVACT(*LIB) ACCPTH(*YES) DTACPR(*NO) +     
                         COMPACT(*NO) )                              
            ELSE   CMD(   +                                          
                SAVCHGOBJ  OBJ(*ALL) LIB(&ODOBNM) DEV(*SAVF) +       
                         REFDATE(&FECHA) REFTIME(000000) +           
                         SAVF(BACKUPSAV/&ODOBNM) SAVACT(*LIB) +      
                         ACCPTH(*YES) DTACPR(*NO) COMPACT(*NO))      
            /* MUEVO el archivo  SAVF al IFS */                       
            CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                         mv /QSYS.LIB/BACKUPSAV.LIB/' *TCAT &ODOBNM + 
                         *TCAT '.FILE /home/backupsav/' *TCAT +       
                         &ODOBNM *TCAT '.savf"')                      
                                                                      
            QSH        CMD(&zipCMD)                                   
            /* Comprimo con 7zip y clave el archivo  SAVF */          
            CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                       7za a -p' *tcat +                              
                       &ENCKEY *BCAT '/home/backupsav/' *TCAT +       
                       &ODOBNM *TCAT '.7z /home/backupsav/' *TCAT +   
                       &ODOBNM *TCAT '.savf"')                        
                                                                      
            QSH        CMD(&zipCMD)                                   
            /* Borro el archivo  SAVF */                              
            CHGVAR     VAR(&ZIPCMD) VALUE('/QOpenSys/usr/bin/sh -c " +
                         rm /home/backupsav/' +        
                        *tcat &ODOBNM *TCAT '.savf"') 
             QSH        CMD(&zipCMD)                  
                                                      
                                                      
             GOTO       CMDLBL(LEE)                   
 FIN:        ENDPGM                                   


g) Un programa que envíe los archivos a nuestro sitio FTP

BKPFTP02b (CLLE)

pgm                                                                    
           OVRDBF     FILE(INPUT) TOFILE(MYLIB/FTPBKP) MBR(FTPIN)  
           CLRPFM     FILE(MYLIB/FTPBKP) MBR(FTPOUT)               
           OVRDBF     FILE(OUTPUT) TOFILE(MYLIB/FTPBKP) +          
                        MBR(FTPOUT)                                    
           FTP RMTSYS(NOTFOUND)                                        
           DLTOVR FILE(*ALL)                                           
endpgm                                                                 


h)  Una Data Area (*DTAARA) donde guardaremos la llave de encripción
 MYLIB/BKP7ZPSWD

i) Un archivo donde pondremos la información del FTP de entrada y el log de salida:

Un archivo MYLIB/FTPBKP que contenga un miembro llamado FTPIN y otro FTPOUT

j) El programa 7-ZIP compilado para IBM i. Lo podemos encontrar junto con las instrucciones en la página http://www.scottklement.com/p7zip/

k) Obviamente necesitaremos el QSHELL y el PASE instalados, así como IBM i V5R4 en adelante (posiblemente funcione en versiones anteriores, pero no puedo garantizarlo)

l) Espacio para alojar nuestra biblioteca más grande x 2, y un 15% de espacio del total del disco libre.

m) Una entrada en JOBSCDE (podemos usar WRKJOBSCDE) para que ejecute de lunes a viernes (al menos así lo tengo yo)

Lo que hace este programa es ejecutarse todos los días a una hora determinada, y si el día es el 1ero de cada mes o lunes ejecuta un respaldo "completo" de las bibliotecas indicadas en el archivo BKPLIB. Si es OTRO día, simplemente ejecutará un respaldo incremental desde el día 1ero del mes hasta la fecha, para reducir el espacio.
El programa ejecutará un SAVLIB en los respaldo completos por cada biblioteca, luego la comprimirá con 7-ZIP y encriptará usando AES256 con la llave que guardemos en la DTAARA BKP7ZPSWD para que podamos dormir medianamente tranquilos.
Una vez comprimido borrará el archivo SAVE FILE del disco, para liberar espacio y continuará con la biblioteca siguiente.
Los días que NO son 1ero de mes ni lunes hará un salvado incremental desde el 1ero a la fecha usando SAVCHGOBJ, y lo comprimirá con el mismo mecanismo. Esto reduce el tamaño notablemente (en mi caso a un 30% en el peor de los casos).

Una vez terminado, el respaldo se transfiere a un sitio FTP (estoy trabajando en un mecanismo que use SCP, mucho más seguro, pero no funciona con un hosting web común).

Obviamente que es un proceso perfectible, pues se puede crear un mecanismo eficiente para editar las bibliotecas, agregar filtros sobre los archivos, crear una clave diferente por cada biblioteca, agregar algún tipo de GUI para revisar los logs de respaldo y transferencia, etc.

La idea era brindarles un mecanismo aceptable que permita subir información segura a un sitio FTP. Las mejoras se las dejo a Uds.

Cómo conectarme a un servidor remoto en una red protegida - Versión actualizada

En un artículo anterior describí cómo conectarse a un equipo remoto en una red protegida http://diego-k.blogspot.mx/2014/12/como-conectarme...