diff --git a/rdiff-manager.py b/rdiff-manager.py old mode 100644 new mode 100755 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ad3c08b77c2d003ef618dfbc08f2139e0fa2417b --- a/rdiff-manager.py +++ b/rdiff-manager.py @@ -0,0 +1,91 @@ +#! /usr/bin/python3 + +# rdiff-manager - a tool to run more than one rdiff-backup process concurrently +# and report their output in one hit + +configs_path = '/backups/conf' +backups_base = '/backups' +# number of jobs to run concurrently (0 for all) +concurrent_num = 0 +duration_to_keep = '4W' + +from multiprocessing import Pool +import subprocess, os, glob + +run_rdiff = lambda *args: subprocess.check_output(('/usr/bin/rdiff-backup',) + args, stderr=subprocess.STDOUT).decode('utf-8') + +class Host(object): + def __init__(self, config): + self.hostname = config['hostname'] + self.duration_to_keep = config['duration_to_keep'] + self.run_backup_flags = config ['backup_flags'] + self.destination = config['backup_destination'] + self.include_file = config['include_file'] + + def test_server(self): + return run_rdiff('--test-server', 'root@%s::/' % self.hostname) + + def test_directory(self): + try: + os.makedirs(self.destination) + except OSError as e: + if os.access(self.destination, os.F_OK): + pass + else: + raise e + + def remove_old(self): + return run_rdiff('--remove-older-than', self.duration_to_keep, self.destination) + + def run_backup(self): + return run_rdiff(*(self.run_backup_flags.split(' ') + [ '--include-globbing-filelist', + self.include_file, 'root@%s::/' % (self.hostname), self.destination ] ) ) + + def run_all(self): + result = '' + try: + # only add output if error occurs + self.test_server() + self.test_directory() + # always print output + result += self.remove_old() + result += self.run_backup() + except subprocess.CalledProcessError as e: + result += e.output.decode('utf-8') + result += 'Backup failed with error %d' % e.returncode + except OSError as e: + result += 'Backup failed: %s (%d)' % (e.strerror, e.errno) + return result + +def rdiff_backup(config): + target = Host(config) + return target.run_all() + +if __name__ == '__main__': + config_files = glob.iglob(configs_path + '/*.conf') + filenames = (os.path.basename(x) for x in config_files) + hostnames = (x.replace('.conf', '') for x in filenames) + + hosts = [] + + # TODO: add hosts in a sensible order + # e.g. alternating slowest & fastest + for host in hostnames: + hosts.append( { 'hostname': host, + 'include_file': '%s/%s.conf' % (configs_path, host), + 'duration_to_keep': duration_to_keep, + 'backup_flags': '--print-statistics --ssh-no-compression --exclude-sockets --exclude-device-files --exclude-fifos', + 'backup_destination': '%s/%s' % (backups_base, host), + } ) + + if concurrent_num == 0: + concurrent_num = len(hosts) + pool = Pool(concurrent_num) + + results = [ (host, pool.apply_async(rdiff_backup, (host, )) ) for host in hosts ] + + for host, r in results: + print("Backup results for", host['hostname']) + print(r.get()) + print('-' * 40) + print()