diff --git a/example/backup_config.py b/example/backup_config.py new file mode 100644 index 0000000..af5a1c9 --- /dev/null +++ b/example/backup_config.py @@ -0,0 +1,14 @@ +import os +import sys + +backup = True +backup_basepath = '/data/backup/ahorn' + +installations = ['mint', 'kubuntu', 'openSuSE'] +for installation in installations: + setattr(sys.modules[__name__], 'home_%s_remotepath' % installation, 'dirk@ahorn:/home') + setattr(sys.modules[__name__], 'home_%s_localpath' % installation, os.path.join(installation, 'home')) + +user_data_remotepath = 'dirk@ahorn:/user_data' +user_data_localpath = 'user_data' +user_data_skip = ['static_data'] diff --git a/example/sync_config.py b/example/sync_config.py new file mode 100644 index 0000000..c3b562e --- /dev/null +++ b/example/sync_config.py @@ -0,0 +1,25 @@ +import os +import sys + +backup = False + +hosts = ['ahorn', 'erle', 'linde'] +__basepath__ = '/user_data' +entries = ( + ('dirk@%s:/user_data/bin', os.path.join(__basepath__, 'bin'), None), + ('dirk@%s:/user_data/data', os.path.join(__basepath__, 'data'), None), + ('dirk@%s:/user_data/static_data', os.path.join(__basepath__, 'static_data'), ['Audio', 'timeshift', 'lost+found']), + ('dirk@%s:/home', '/home', None), +) + + +for host in hosts: + for remote, local, skip in entries: + setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_remotepath', remote % host) + setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_localpath', local) + if skip is not None: + setattr(sys.modules[__name__], host + '_' + os.path.basename(local) + '_skip', skip) + + +mount_mockery_audio_remotepath = 'root@mount-mockery.de:/data/audio/items' +mount_mockery_audio_localpath = os.path.join(__basepath__, 'static_data', 'dirk', 'Audio') diff --git a/mysync b/mysync new file mode 100755 index 0000000..d7cb6f9 --- /dev/null +++ b/mysync @@ -0,0 +1,137 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# + +import config +import os +import sys +import time +import subprocess + +DEBUG = False + +RSYNC_REMOTE_COMMAND = 'sudo rsync' +TIME_FORMAT = "%Y-%m-%d_%H-%M-%S" +try: + basepath = config.backup_basepath +except AttributeError: + basepath = os.path.dirname(os.path.abspath(__file__)) +timestamp = time.strftime(TIME_FORMAT) +output_lines = 5 + +PROP_REMOTE = 'remotepath' +PROP_LOCAL = 'localpath' +PROP_SKIP = 'skip' + + +HEADER = '\033[95m' +OKBLUE = '\033[94m' +OKGREEN = '\033[92m' +WARNING = '\033[93m' +FAIL = '\033[91m' +ENDC = '\033[0m' +BOLD = '\033[1m' +UNDERLINE = '\033[4m' +CLEAR_REST_OF_LINE = '\033[K' + + +def status_line(line): + sys.stdout.write('\r .....' + line.replace('\n', '')[-60:] + CLEAR_REST_OF_LINE) + sys.stdout.flush() + + +def parse_config_to_dict(): + d = {} + config_dict = dir(config) + for key in config_dict: + if key.endswith('_' + PROP_REMOTE): + entry_name = key[:-len(PROP_REMOTE) - 1] + if entry_name + '_' + PROP_LOCAL in config_dict: + d[entry_name] = {} + for key in config_dict: + if key.startswith(entry_name): + d[entry_name][key[key.rfind('_') + 1:]] = getattr(config, key) + return d + + +def rsync_command(**kwargs): + def path_filter(path): + return path if path.endswith('/') else path + '/' + # + if config.backup: + localpath = path_filter(os.path.join(basepath, timestamp, kwargs[PROP_LOCAL])) + if timestamp not in localpath: + sys.stderr.write('Unable to backup to %s. Please give a relative path for a backup!\n' % localpath) + localpath = None + else: + localpath = path_filter(os.path.join(basepath, kwargs.get('localpath', ''))) + if localpath is not None: + remotepath = path_filter(kwargs.get(PROP_REMOTE)) + skip = kwargs.get(PROP_SKIP) + cmd_l = ['rsync', '-avn' if DEBUG else '-av', '--delete', '--rsync-path="%s"' % RSYNC_REMOTE_COMMAND, remotepath, localpath] + if skip is not None: + cmd_l.append('--exclude=%s' % ','.join(skip)) + if not os.path.exists(localpath): + return 'mkdir -p %s && ' % localpath + ' '.join(cmd_l) + return ' '.join(cmd_l) + + +def link_copy_command(): + newest_backup = None + if not os.path.exists(basepath): + os.system('mkdir -p ' + basepath) + for path in os.listdir(basepath): + try: + t = time.strptime(path, TIME_FORMAT) + if newest_backup is None or t > newest_backup: + newest_backup = t + except ValueError: + pass # seems to be not one of my backups + if newest_backup is not None: + newest_backup = os.path.join(basepath, time.strftime(TIME_FORMAT, newest_backup)) + return 'cp -alv ' + newest_backup + ' ' + os.path.join(basepath, timestamp) + + +def help_msg(): + backup_dict = parse_config_to_dict() + print("Possible arguments are:") + for entry in backup_dict: + print(" * %s (%s -> %s)" % (entry + (20 - len(entry)) * ' ', backup_dict[entry][PROP_REMOTE] + (40 - len(backup_dict[entry][PROP_REMOTE])) * ' ', backup_dict[entry][PROP_LOCAL] + (40 - len(backup_dict[entry][PROP_LOCAL])) * ' ')) + +def make_link_copy(): + cmd = link_copy_command() + if cmd is not None: + sys.stdout.write(BOLD + HEADER + UNDERLINE + 'Creating Link copy of last backup.' + ENDC + '\n\n') + with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: + err = b"" + for line in p.stdout: + status_line(line.decode('utf-8')) + err += p.stderr.read() + sys.stdout.write('\n' + FAIL + BOLD + err.decode('utf-8') + ENDC) + sys.stdout.write('\n' + 80 * '-' + '\n') + +# +if __name__ == '__main__': + backup_dict = parse_config_to_dict() + # + backup_dict = parse_config_to_dict() + did_nothing = True + for entry in backup_dict: + if entry in sys.argv: + cmd = rsync_command(**backup_dict[entry]) + if cmd is not None: + if config.backup and did_nothing: + make_link_copy() + did_nothing = False + sys.stdout.write(BOLD + HEADER + UNDERLINE + 'Doing %s for %s:' % ('backup' if config.backup else 'sync', repr(entry)) + ENDC + '\n\n') + with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: + err = b"" + for line in p.stdout: + status_line(line.decode('utf-8')) + err += p.stderr.read() + sys.stdout.write('\n' + FAIL + BOLD + err.decode('utf-8') + ENDC) + sys.stdout.write('\n' + 80 * '-' + '\n') + if did_nothing: + help_msg() +else: + help_msg()