IES Miguel Romero Esteo - Departamento de Informática - Linux/Unix

Bash y Programación bash / Práctica 14

Comandos:

Aplicaciones:

Enunciado:

    Esta práctica implementa un script de archivado incremental y transferencia sftp automática. Está resuelto y comentado para análisis y estudio del alumno. Una vez comprendido es recomendable que lo implemente desde cero usando como servidores de producción y copias de seguridad sendas máquinas virtuales.

1. Objetivo


    Se trata de escribir un script que guarde una copia de seguridad de forma incremental de un directorio de una máquina de producción. Los archivos de copia (archivos cpio comprimidos) se transferirán a un servidor de copias de seguridad (servidor venus), en un directorio cuyo nombre dependerá del mes y año de la copia.

Directorios de la máquina de producción:

/root/admin/backup: directorio de los scripts de copia de seguridad.
/home/document: directorio de los documentos que se han de guardar.
/home/lbackup: directorio local de archivos. Este directorio se limpiará todos los meses.


Directorios de la máquina de copias de seguridad:

    /home/dbackup/2015/01: archivos del mes de enero de 2015.
    /home/dbackup/2015/02: archivos del mes de febrero de 2015.

    En el ejemplo que se presenta a continuación, estos directorios estarán creados previamente. No es el script de copia de seguridad el que los crea (pero sería fácilmente realizable).

    La figura 2 representa el sistema de archivos de los dos servidores.

    La copia de seguridad incremental usará tantos niveles de copia de seguridad como días tenga el mes. En principio, una copia de nivel 0 (copia de todos los archivos del directorio /home/document) se realiza el primer día de cada mes. Los días siguientes, solamente se archivarán los archivos modificados desde el día anterior.

    Se utilizaran archivos indicadores de nivel (nivel0, nivel1...) para reflejar la fecha en la que las copias se llevan a cabo.

Ejemplo

El 01/01/2015: Copia de seguridad de nivel 0: creación del archivo de control "nivel0" y copia de seguridad de todos los archivos y directorios que estén en /home/document. A continuación, transferencia del archivo al servidor de copias.

El 02/01/2015: Copia de seguridad de nivel 1: creación del archivo de control "nivel1". A continuación, los archivos que sean más recientes que el archivo de control "nivel0" se copian. Transferencia del archivo al servidor de copia.

El 03/01/2015: Copia de seguridad de nivel 2: creación del archivo de control "nivel2". A continuación, los archivos que sean más recientes que el archivo de control "nivel1" se copian. Transferencia del archivo al servidor de copias.

etc.

El 01/02/2015: Copia de seguridad de nivel 0: borrado de todos los archivos de control del mes anterior. Creación de un nuevo archivo de control "nivel0" y copia de seguridad de todos los archivos y directorios que estén en /home/document. A continuación, transferencia del archivo al servidor de copias.

etc.


    La copia de seguridad incremental permite tener una copia diaria, archi-vando únicamente los archivos modificados o añadidos desde el día anterior. En caso de pérdida de un archivo, es suficiente con restaurar la última copia que contenga dicho archivo. Si el usuario desea restaurar todo el directorio /home/document, hay que restaurar todos los archivos del mes en curso, empezando por el archivo de nivel 0.




Figura 2: Sistemas de archivos de los servidores de producción y de copias de seguridad


El script de copias de seguridad se descompone en dos archivos:

    El archivo uploadBackup.sh es el programa principal.
    El archivo funciones.inc.sh contiene funciones llamadas desde el archivo uploadBackup.sh.

2. El archivo uploadBackup.sh

A continuación se muestra el código fuente con comentarios del script uploadBackup.sh:

         1  #! /bin/bash
     2  # ------------------
     3  # Variables globales
     4  # ------------------
     5  # Directorio de los scripts en shell de backup
     6  SCRIPTDIR="/root/admin/backup"
 
     7  # Directorio de los archivos que se han de copiar
     8  DATADIR=/home/document
     
      9  # Directorio local de los archivos
     10  ARCHIVEDIR="/home/lbackup"
     11  # ----------------------
     12  # Librerías de funciones
     13  # ----------------------
     14  # Incluir funciones
     15  .  $SCRIPTDIR/funciones.inc.sh
 
     16  # Archivo de log 
     17  LOG=$ARCHIVEDIR/`getDate`.log
     18  # -------------------
     19  # Programa
     20  # -------------------
     21  exec 1>$LOG 2>&1
 
     22  # ----------------------------------
     23  # Determinar el nivel de copia de seguridad
     24  # El día 1 de cada mes => nivel 0
     25  # ----------------------------------
     26  diaDeMes=`getDayForCalcul` 
 
     27  ((nivel=$diaDeMes-1))
 
     28  case $nivel in
     29  0) # Copia de seguridad total
     30          # Limpieza del directorio de archivado
     31          rm -i $ARCHIVEDIR/*.bz2 $ARCHIVEDIR/nivel*
     32
     33          # Creación del archivo de nivel (nivel 0) 
     34          touch $ARCHIVEDIR/nivel0
     35          archivado="$ARCHIVEDIR/`getDate`_nivel0.cpio"
 
     36          find $DATADIR | cpio -ocv | bzip2 -c > $archivado.bz2 
     37          ;;
     38  *) 
     39          touch $ARCHIVEDIR/nivel$nivel
     40          archivado="$ARCHIVEDIR/`getDate`_nivel${nivel}.cpio"
 
     41          ((nivelAnt=$nivel-1))
     42          if [[ ! -f $ARCHIVEDIR/nivel$nivelAnt ]] ; then
     43            echo "Archivo de nivel $nivelAnt ausente" 
     44            exit 1
     45          fi
 
     46          # Copia de seguridad
     47          find $DATADIR -newer $ARCHIVEDIR/nivel$nivelAnt\
                         | cpio -ocv | bzip2 -c > archivado.bz2
     48          ;;
     49  esac
 
     50  # Comprobación de la validez del archivado
     51  if isArchivadoInvalido $archivado.bz2 ; then
     52    echo "Archivado $archivado.bz2 INVÁLIDO - Archivo no transferido"
     53    exit 1 ; 
     54  fi
     55
     56  if transferir ${archivado}.bz2 ; then
     57    echo "Transferencia realizada con éxito"
     58    exit 0
     59  fi
 
     60  echo "Error en la transferencia"
     61  exit 1


Comentarios:

    Líneas 6, 8 y 10: inicialización de tres variables globales que representan los nombres de los directorios que se manipularán en este script.

    Línea 15: inclusión en el entorno actual (mediante el comando .) de las funciones definidas en el archivo funciones.inc.sh. La variable global SCRIPTDIR se utiliza por la orden de inclusión; por tanto, hay que definirla previamente: hay que poner especial atención en el orden de las instrucciones.

    Línea 17: definición de otra variable global. La variable global LOG se inicializa realizando la llamada a la función getDate definida en el archivo funciones.inc.sh. Por tanto, hay que incluir este archivo previamente.

    Línea 21: redirección de la salida estándar y de la salida de error estándar del script al archivo de log.

    Líneas 26 y 27: cálculo del nivel de copia de seguridad a partir del día actual.

Caso de la copia de seguridad total (nivel 0)

    Línea 29: el primer día del mes todos los archivos se almacenan en formato cpio y se comprimen en formato bz2.

    Línea 31: los archivos locales del mes anterior se eliminan.

    Línea 34: creación del archivo de control nivel0, cuya fecha de última modificación (en este caso, la fecha de creación) se usará para realizar las copias de seguridad futuras.

Caso de las copias de seguridad incrementales:

    Línea 39: creación del archivo de control reflejando el nivel de copia de seguridad.

    Línea 47: copia de seguridad incremental: solamente los archivos modificados después del nivel anterior se tratan. La opción -newer del comando find permite realizar esta operación.

Vuelta al tratamiento común:

    Línea 51: verificación de la validez del archivado con la función isArchivadoInvalido.

    Línea 56: transfererencia del archivado al servidor de copias de seguridad con la función transferir.
3. El archivo funciones.inc.sh
La función transferir

Recibe un nombre de archivo como argumento y lo transfiere a la máquina remota venus, usando la cuenta dbackup. La función transferir devuelve un estado verdadero (0) en el caso de éxito o falso (1) de lo contrario.

      1  # -------------------------------
     2  # Función transferir 
     3  # Argumento: archivado a transferir
     4  # -------------------------------
     5  function transferir {
     6    typeset mes
     7    typeset ano
     8
     9    typeset archATransferir=$1
    10
    11    mes=`getMonth`
    12    ano=`getYear`
    13
    14    sftp -b - "dbackup@$venus" <<FIN
    15    cd /home/dbackup/$ano/$mes
    16    pwd 
    17    put $archATransferir
    18    FIN
    19    
    20    (( $? != 0 )) && return 1
    21     
    22    # Comprobar si el archivado es válido en la máquina de copias de seguridad
    23
    24    archEnMaquinaDest=$(basename $archATransferir)
    25    ssh dbackup@$venus bzip2 -t  /home/dbackup/$ano/$mes/$archEnMaquinaDest
    26    
    27    case $? in
    28  0);;
    29  255)
    30     echo "Error del comando ssh"
    31     return 1
    32     ;;
    33  *) 
    34     echo "Archivado\
              /home/dbackup/$ano/$mes/$archEnMaquinaDest INVÁLIDO"
    35      return 1
    36      ;;
    37   esac 
    38    
    39   return 0
    40  }


Comentarios:

    Línea 9: el nombre del archivo que se transfiere se recibe por parámetro ($1).

    Línea 14: transferencia del archivo a la máquina venus, de manera no interactiva. Los comandos de sftp se leen por la entrada estándar (opción -b y doble redirección en lectura). El entorno de trabajo ha sido configurado para que la contraseña no se tenga que informar (ver el comando sftp del capítulo Los comandos filtro - Comandos de red seguros). La cuenta de la máquina remota es dbackup.

    Línea 20: si el comando sftp genera error, la función devuelve el estado falso.

    Línea 25: verificación (bzip2 -t) de la validez del archivado en la máquina distante.

    Líneas de la 27 a la 37: comprobación del código de retorno de ssh y bzip2.

    Línea 28: si el código de retorno vale 0, ssh y bzip2 se han ejecutado sin problemas y el archivado es válido.

    Línea 29: si el código de retorno vale 255, ssh ha provocado un error.

    Línea 33: si el código de retorno no es ni 0 ni 255, el archivado es inválido.

La función isArchivadoInvalido

Esta función recibe un archivo de tipo bz2 como argumento, devuelve verdadero si el archivado es inválido, falso en caso contrario.

     41  #------------------------------
    42  # Argumento: nombre del archivado
    43  # Verificación de la validez del archivado
    44  # Retorno: verdadero si el archivado es válido, falso en caso contrario
    45  #------------------------------
    46  function isArchivadoInvalido {
    47    typeset archivado=$1
    48    bzip2 -t $archivado 2>/dev/null && return 1
    49    return 0
    50  }


La función getDate

La función getDate muestra la fecha del día, en formato aaaa_mm_dd.

       51  # -------------------------------
    52  # Función getDate
    53  # Genera la fecha en formato aaaa_mm_dd
    54  # -------------------------------
    55  function getDate {
    56     date ’+%Y_%m_%d’
    57  }


La función getYear

La función getYear muestra el año actual, en formato aaaa.

       58  # -------------------------------
    59  # Función getYear
    60  # Genera el año en formato aaaa
    61  # -------------------------------
    62  function getYear {
    63    date ’+%Y’
    64  }


La función getMonth

La función getMonth muestra el mes actual, en formato mm.

      65  # -------------------------------
    66  # Función getMonth
    67  # Genera el mes en formato mm
    68  # -------------------------------
    69  function getMonth {
    70    date ’+%m’
    71  }


La función getDayForCalcul

La función etD
gayForCalcul muestra el día actual numéricamente con 1 o 2 cifras.

Este valor se destina para cálculos, no es necesario prefijarlo con 0 (que significa número octal para ciertos comandos de cálculo), ni espacio (que no es un carácter numérico).

       72  # -------------------------------
    73  # Función getDayForCalcul
    74  # Genera el día en formato d o dd
    75  # -------------------------------
    76  function getDayForCalcul {
    77     date ’+%e’ | sed ’s/ //’
    78  }