| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 | #!/usr/bin/env python3# PYTHON_ARGCOMPLETE_OKimport hashlibimport osimport shleximport subprocessimport tempfileimport timeimport urllib.parseimport yamldef command_join(args):    return ' '.join([shlex.quote(a) for a in args])def sshfs_mount(url, path):    url_attr = urllib.parse.urlparse(url)    assert url_attr.scheme in ['sftp']    mount_command = [        'sshfs',        '{}:{}'.format(url_attr.netloc, url_attr.path),        path,        ]    print('+ {}'.format(command_join(mount_command)))    subprocess.check_call(mount_command)def sshfs_unmount(path):    unmount_command = [        'fusermount',        '-u',        path,        ]    print('+ {}'.format(command_join(unmount_command)))    subprocess.check_call(unmount_command)def backup(config, no_print_statistics, tab_dry):    for backup in config:        print(yaml.dump({"backup": backup}, default_flow_style = False))        backup_command = ['duplicity']        # encryption        try:            encryption = backup['encryption']        except KeyError:            encryption = True        if 'encryption' in backup and not backup['encryption']:            backup_command += ['--no-encryption']        else:            if 'encrypt_key' in backup:                backup_command += ['--encrypt-key', backup['encrypt_key']]        # selectors        try:            selectors = backup['selectors']        except KeyError:            selectors = []        for selector in selectors:            if selector['option'] in ['include', 'exclude']:                backup_command += ['--{}'.format(selector['option']), selector['shell_pattern']]            else:                raise Exception("unsupported selector option '{}'".format(selector['option']))        # statistics        if no_print_statistics:            backup_command.append('--no-print-statistics')        # source path        backup_command += [backup['source_path']]        # target path        try:            target_mount_path = None            if 'target_via_sshfs' in backup and backup['target_via_sshfs']:                target_mount_path = tempfile.mkdtemp(prefix = 'duplitab-target-sshfs-')                backup_command += ['file://' + target_mount_path]                sshfs_mount(backup['target_url'], target_mount_path)                # set backup name to make archive dir persistent                # (default name: hash of target url)                backup_command += ['--name', hashlib.sha1(backup['target_url'].encode('utf-8')).hexdigest()]            else:                backup_command += [backup['target_url']]            try:                if tab_dry:                    print('* {}'.format(command_join(backup_command)))                else:                    print('+ {}'.format(command_join(backup_command)))                    subprocess.check_call(backup_command)            finally:                if target_mount_path:                    try:                        sshfs_unmount(target_mount_path)                    except subprocess.CalledProcessError:                        time.sleep(1)                        sshfs_unmount(target_mount_path)        finally:            if target_mount_path:                os.rmdir(target_mount_path)def run(command, config_path, no_print_statistics, tab_dry):    with open(config_path) as config_file:        config = yaml.load(config_file.read())    if command == 'backup':        backup(            config = config,            no_print_statistics = no_print_statistics,            tab_dry = tab_dry,            )def _init_argparser():    import argparse    argparser = argparse.ArgumentParser(description = None)    argparser.add_argument(            '-c',            '--config',            dest = 'config_path',            default = '/etc/duplitab',            )    subparsers = argparser.add_subparsers(            dest = 'command',            )    subparser_backup = subparsers.add_parser('backup')    subparser_backup.add_argument(            '--no-print-statistics',            action = 'store_true',            )    subparser_backup.add_argument(            '--tab-dry',            action = 'store_true',            )    return argparserdef main(argv):    argparser = _init_argparser()    try:        import argcomplete        argcomplete.autocomplete(argparser)    except ImportError:        pass    args = argparser.parse_args(argv)    run(**vars(args))    return 0if __name__ == "__main__":    import sys    sys.exit(main(sys.argv[1:]))
 |