4Trabes Historias de una empresa en 100 metros cuadrados

El blog de Trabe Soluciones

Sincronizar con un ftp utilizando perl

| | Comentarios

Cuando necesitamos mantener dos sistemas de ficheros sincronizados hay varias opciones razonables. Podemos utilizar alguna utilidad tipo rsync, o multitud de aplicaciones de propósito específico. Incluso podemos utilizar un cvs o svn para conseguir este fin. Pero cuando el “servidor remoto” solo es accesible a través de ftp las opciones se reducen bastante.

Cuando es razonable suponer que el tamaño de los datos va a ser escaso, podemos optar por hacer backups periódicos de la totalidad del sistema de ficheros, y luego restaurarlos en el sistema a mantener sincronizado. Pero cuando el sistema de ficheros va a contener ficheros grandes, eso no es una gran idea.

El script perl que se muestra a continuación no es ni mucho menos una solución general de sincronización. Tampoco considera todos los casos posibles, ni hace sincronización recursiva, pero es una solución que funciona correctamente en el escenario para el que fué creada.

El funcionamiento es muy sencillo:

  • El script se conecta al servidor ftp indicado en las variables de configuración.
  • Para cada archivo del sistema remoto comprueba si existe en el sistema local. Si no existe lo descarga. Si existe y su fecha es anterior a la instancia del sistema remoto, también lo descarga.
  • Para cada archivo del sistema local comprueba si existe en el sistema remoto. Si no es así, lo elimina.

Partiendo de la premisa de que en el sistema local NUNCA se realizan cambios sobre los ficheros, al final del proceso tendremos en la carpeta local una réplica del contenido del sistema remoto, minimizando las transferencias de ficheros en la medida de lo posible.

A continuación se incluye el script en cuestión:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/perl -w
use strict;
use warnings;
use Net::FTP;

# Algunas variables
my $host = "dirección_host";
my $user = "usuario_ftp";
my $pass = "contraseña";
my $local_app_root = "ruta_local";
my $remote_app_root = "ruta_remota";

# Conectamos al servidor ftp
print "Conectando al servidor $host " ;
my $ftp = Net::FTP->new($host, Passive => 1, Debug => 0 ) or die "Imposible conectar al servidor FTP  en  $host";
$ftp->login( $user, $pass ) or die "..fallo en la autenticación $!";
print "[OK]\n";

# Sincronizamos las carpetas de ficheros
print "\n:::: Sincronizando Ficheros ::::\n";
repo_sync($ftp, $local_app_root, $remote_app_root);

# Desconectamos del ftp
print "\n\nDesconectando...";
$ftp->quit;
print "[OK]\n";


# Funcion para sincronizar FTP<-Local
sub repo_sync {
	my $ftp = shift;
	my $local_path = shift;
	my $remote_path = shift;
	my $rtime;


	# Obtenemos el listado de ficheros Remotos
	$ftp->cwd($remote_path);
	my @remote_files = $ftp->ls();

	# Bajamos todo lo nuevo
	for my $file (@remote_files) {

		if ( !( $rtime = $ftp->mdtm($file) ) ) {
			# Dado quer el archivo SIEMPRE existe, y mdtm solo es false cuando el archivo no existe o 
			# bien cuando es un directorio, en este casp estamos en un directorio. 
			# No hacemos nada en este caso
	    }
	    else
	   	{
			# Es un fichero, comenzamos a sincronizar
			print "\nSincronizando: $file\n";
			if ( !( my  $ltime = (stat("$local_path/$file") )[9] ) ) {
	    		# No existe el fichero en el repositorio local
	    		print "-> No existe en repositorio local";
	    		print ".Actualizando...";
				# Obtenemos el fichero por ftp
				$ftp->get ($file, "$local_path/$file");
				print "[OK]";

	    	}
	    	else
	    	{
	    		# Existe el fichero en el repositorio local
	    		print "-> Existe en repositorio local";
	    		if ($rtime > $ltime) {
			    	# La copia local no está al día
					print ". No está al día. Actualizando...";
			    	$ftp->get ($file, "$local_path/$file");
			    	print "[OK]";
			    }
			    else  {
					print". Está al día";
	    		}
	    	}

	    }

	}

	# Borramos lo que ya no está en el repositorio remoto, para ello necesitaremos iterar sobre los ficheros del sistema local
	my (@local_files, $f);
	opendir DIR, $local_path or
	      die "Imposible abrir $local_path: $!\n";
	while($f = readdir DIR) {
		if(-d "$local_path/$f"){
			# Esto son directorios. De nuevo no nos interesa tratarlos en nuestro script,
			# dejamos este comentario a modo de recordatorio para quien necesite 
			# hacer algo con ellos
		}
		else{
			# Guardamos los ficheros en un array
			push (@local_files, $f);
		}
	}
	# Iteramos sobre los ficheros
	for my $file (@local_files) {
		if ( !( $rtime = $ftp->mdtm($file) ) ) {
			# El archivo NO existe en el repositorio remoto, pues mdtm solo es false  cuando el fichero no existe o cuando es 
			# un directorio, caso imposible en esta rama.
			print "\nSincronizando: $file\n->NO existe en el repositorio. Eliminando..."  ;
			unlink "$local_path/$file";
			print "[OK]";
	    }

}
}

Espero que alguien pueda ahorrarse un rato de trabajo gracias a este post :D

Lo sentimos, pero los comentarios están cerrados

y ahora de deberes, pasar ese script a ruby, a ver cuántas lineas salen :)

Hola. El script debe ejecutarse en un servidor del cliente que no tiene ruby instalado (ni puede tenerlo). Una pena, pero hay que saber un poquito de todo para poder sobrevivir :D. De todos modos apunto los deberes para cuando tenga tiempo (si algún día lo tengo). Un saludo

28/May/2007 Marcos

Nosotros lo hacemos con sitecopy, que se encarga de mantener en un XML la información de los archivos y la fecha de cambio. Además, funciona sobre FTP, con lo que no hay problemas para actualizar. Claro que si no puedes instalarlo, es más dificil.

Un saludo