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 etDgayForCalcul
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 }