sábado, 20 de abril de 2013

Python: Comprimir archivos y enviarlos por SSH.

Hoy mientras hacía un backup de mi directorio de trabajo, el cual suelo hacer copia en tres sitios distintos, en una de estas lo que hago es subirlo a mi servidor por SSH, para lo cual lo empaqueto y comprimo y ya lo subo usando scp, y después compruebo su hash.

$ tar -zcvf /home/manu/file_20042013.tar.gz /home/manu/files
$ scp -P 22 /home/manu/file_20042013.tar.gz sink@192.168.1.10:/home/sink/file_20042013.tar.gz

Para hacer más ágil y rápido esto, decidí hacer un script en python el cual pasandole los parámetros necesarios te hace esto y además te comprueba el hash de ambos archivos.

Aquí os pongo el script en cuestion:
#!/usr/bin/env python


from optparse import OptionParser
import sys
import getpass
import string
import pexpect
import gzip
import tarfile
import hashlib

def md5_file(filepath):
 fi = open(filepath, 'rb')
 m = hashlib.md5()
 while True:
  data = fi.read(8192)
  if not data:
   break
  m.update(data)
 return m.hexdigest()

def comprimeD(path,tarball):
 tar = tarfile.open(path+tarball.split('/')[3], "w:gz")
 tar.add(path)
 tar.close()
 print "Compress in: %s      md5 checksum:%s"%(path+tarball.split('/')[3],md5_file(path+tarball.split('/')[3]))
 return path+tarball.split('/')[3]

def comprime(path,tarball):
 if (tarball.split(".")[1] == "gz"):
  f_in = open(path, 'rb')
  f_out = gzip.open(path+'.gz', 'wb')
  f_out.writelines(f_in)
  f_out.close()
  f_in.close()
  print "Compress in: %s.gz      md5 checksum:%s"%(path,md5_file(path+'.gz'))
  path = path+'.gz'
 elif (tarball.split(".")[1] == "tar"):
  tar = tarfile.open(path+'.tar.gz', "w:gz")
  tar.add(path)
  tar.close()
  print "Compress in: %s.tar.gz      md5 checksum:%s"%(path,md5_file(path+'.tar.gz'))
  path = path+ '.tar.gz'
 return path

def conexion(host,port,user,password,filebackup,path):
 child = pexpect.spawn('scp -P %s %s %s@%s:%s'%(port,filebackup,user,host,path))
 child.expect ('assword:',timeout=10)
     child.sendline (password)
 data = child.read()
 print data

 child = pexpect.spawn('ssh %s@%s -p %s \'md5sum %s\''%(user,host,port,path))
 child.expect ('assword:',timeout=10)
 child.sendline (password)
 data = child.read()
 child.close()
 if (data.replace('\r\n','').split(' ')[1] == md5_file(filebackup)):
  print "Backup complete (md5sum OK)"
 else:
  print "Error: bad checksum"

def opciones():
 parser = OptionParser("usage: %prog [options] \nExample: ./backupSSH.py -f /home/manu/file -u sink -s 192.168.1.10:22 -o /home/sink/file2 \nExample: ./backupSSH.py -c -d /home/manu/ -u sink -s 192.168.1.10:22 -o /home/sink/home_manu.tar.gz \nExample: ./backupSSH.py -c -u sink -f /home/manu/file -s 192.168.1.10:22 -o /home/sink/file.tar.gz")
 parser.add_option("-u", "--user",
                  action="store", type="string", dest="user", help="Nombre del usuario")
 parser.add_option("-d", "--directory",
                  action="store", type="string", dest="directory", help="Directorio del cual queremos hacer backup")
 parser.add_option("-f", "--file",
                  action="store", type="string", dest="file", help="Fichero del cual queremos hacer backup")
 parser.add_option("-o", "--output",
                  action="store", type="string", dest="output", help="Fichero o directorio destino en el servidor remoto")
 parser.add_option("-c", "--compress",
                  action="store_true", dest="compress", help="Comprimir el backup usando gzip")
 parser.add_option("-s", "--server",
                  action="store", type="string", dest="server", help="Servidor SSH y puerto. Sintaxis server:port")
 

 (options, args) = parser.parse_args()


 if (len(sys.argv) == 1):
  parser.print_help()
 elif (options.compress):
  if (options.user != None) and (options.server != None) and ((options.directory != None) or (options.file != None)) and (options.output != None):
   if (options.directory != None):
    password = getpass.getpass()
    conexion(options.server.split(":")[0],options.server.split(":")[1],options.user,password,comprimeD(options.directory,options.output),options.output)
   elif (options.file != None):
    password = getpass.getpass()
    conexion(options.server.split(":")[0],options.server.split(":")[1],options.user,password,comprime(options.file,options.output),options.output)
 elif (options.user != None) and (options.server != None) and ((options.directory != None) or (options.file != None)) and (options.output != None):
  password = getpass.getpass()
  conexion(options.server.split(":")[0],options.server.split(":")[1],options.user,password,options.file,options.output)

if __name__=="__main__":
 opciones()


Espero que os sirva. :)