#!/usr/bin/python """ Differential backup script using rsync with possibility to define multiple media Author : Marc SCHNEIDER """ import getopt, os, sys NICE_LEVEL = 3 USER_HOME = '/home/arch' DIR_TO_BACKUP = { 'icybox': "/home/arch/scripts --exclude=temp/ --exclude=tmp/", 'lacie' : "/home/data /home/data2 --exclude=temp/ --exclude=tmp/", 'darmstadt' : "/home/arch/scripts", } BACKUP_DEST = { 'icybox': {'dir': '/media/icybox/backup',}, 'lacie': {'dir': '/media/lacie/backup'}, 'darmstadt': { 'dir': '/home/backup', 'server': 'darmstadt.plop.org', 'port': '2222', 'login': 'toto', }, } ARCHIVE_NB = { 'icybox': 15, 'lacie': 15, 'darmstadt': 10, } def backup(media_name): is_remote = BACKUP_DEST[media_name].has_key('server') ssh_string = "" if is_remote: ssh_string = "ssh -p %s -l %s %s"\ % (BACKUP_DEST[media_name]['port'], BACKUP_DEST[media_name]['login'],\ BACKUP_DEST[media_name]['server']) print "Running backup on %s..." % media_name # Remove oldest backup directory dir_to_remove = BACKUP_DEST[media_name]['dir']+"/daily."+str(ARCHIVE_NB[media_name] - 1) exist_dir_cmd = "[ -d %s ]" % dir_to_remove rm_dir_cmd = "rm -rf %s" % dir_to_remove if is_remote: exist_dir_cmd = "%s %s" % (ssh_string, exist_dir_cmd) rm_dir_cmd = "%s %s" % (ssh_string, rm_dir_cmd) if os.system(exist_dir_cmd) == 0: print "Removing directory", dir_to_remove if os.system(rm_dir_cmd) != 0: print "Couldn't remove %s" % dir_to_remove # Shift the snapshots back by one if they exist for i in reversed(range(2, ARCHIVE_NB[media_name])): src_dir = BACKUP_DEST[media_name]['dir']+"/daily."+str(i-1) dest_dir = BACKUP_DEST[media_name]['dir']+"/daily."+str(i) exist_dir_cmd = "[ -d %s ]" % src_dir mv_dir_cmd = "mv %s %s" % (src_dir, dest_dir) if is_remote: exist_dir_cmd = "%s %s" % (ssh_string, exist_dir_cmd) mv_dir_cmd = "%s %s" % (ssh_string, mv_dir_cmd) if os.system(exist_dir_cmd) == 0: print "Moving %s to %s" % (src_dir, dest_dir) if os.system(mv_dir_cmd) != 0: print "Couldn't move directory %s to %s" % (src_dir, dest_dir) # Make a hard-link copy (except for directories) of the latest snapshot last_backup_dir = BACKUP_DEST[media_name]['dir']+"/daily.0" dest_backup_dir = BACKUP_DEST[media_name]['dir']+"/daily.1" copy_cmd = "cp -al %s %s" % (last_backup_dir, dest_backup_dir) mkdir_cmd = "mkdir %s" % last_backup_dir exist_dir_cmd = "[ -d %s ]" % last_backup_dir if is_remote: copy_cmd = "%s %s" % (ssh_string, copy_cmd) mkdir_cmd = "%s %s" % (ssh_string, mkdir_cmd) exist_dir_cmd = "%s %s" % (ssh_string, exist_dir_cmd) if os.system(exist_dir_cmd) == 0: # Directory exists print "Executing", copy_cmd if os.system(copy_cmd) !=0: print "Couldn't execute", copy_cmd else: print "Creating directory", last_backup_dir if os.system(mkdir_cmd) != 0: print "Couldn't create directory", last_backup_dir # Run rsync from local directories to the latest snapshot (daily.0) # rsync behaves like cp --remove-destination by default, so the destination # is unlinked first. If it were not so, this would copy over the other snapshot(s) too! rsync_cmd = "nice --adjust %s rsync -va --progress --relative --delete --delete-excluded %s "\ % (NICE_LEVEL, DIR_TO_BACKUP[media_name]) if is_remote: rsync_cmd += " -e \"ssh -p %s\" %s@%s:"\ % (BACKUP_DEST[media_name]['port'], BACKUP_DEST[media_name]['login'],\ BACKUP_DEST[media_name]['server']) rsync_cmd = "%s%s" % (rsync_cmd, BACKUP_DEST[media_name]['dir']+"/daily.0") print "Executing", rsync_cmd if os.system(rsync_cmd) != 0: print "Couldn't execute", rsync_cmd # Update mtime of daily.0 to reflect the snapshot time touch_cmd = "touch %s/daily.0" % BACKUP_DEST[media_name]['dir'] if is_remote: touch_cmd = ssh_string + " " + touch_cmd if os.system(touch_cmd) != 0: print "Couldn't execute touch command", touch_cmd def usage(): print "Usage : backup.py command" print "Where command can be one of :" print "-i, --icybox" print "-c, --lacie" print "-d, --darmstadt" if __name__ == "__main__": if len(sys.argv[1:]) == 0: usage() sys.exit(2) try: opts, args = getopt.getopt(sys.argv[1:], "hicd", ["help", "icybox", "lacie", "darmstadt"]) except getopt.GetoptError: usage() sys.exit(2) if os.getuid() != 0: print "Sorry you must be 'root' to run this program" sys.exit(2) for opt, arg in opts: if opt in ('-h', '--help'): usage() sys.exit() if opt in ('-i', '--icybox'): backup('icybox') if opt in ('-c', '--lacie'): backup('lacie') if opt in ('-d', '--darmstadt'): backup('darmstadt')