|  | @@ -0,0 +1,112 @@
 | 
	
		
			
				|  |  | +#!/usr/bin/env python3
 | 
	
		
			
				|  |  | +# PYTHON_ARGCOMPLETE_OK
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import os
 | 
	
		
			
				|  |  | +import shlex
 | 
	
		
			
				|  |  | +import subprocess
 | 
	
		
			
				|  |  | +import tempfile
 | 
	
		
			
				|  |  | +import urllib.parse
 | 
	
		
			
				|  |  | +import yaml
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def 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):
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for backup in config:
 | 
	
		
			
				|  |  | +        print(yaml.dump({"backup": backup}, default_flow_style = False))
 | 
	
		
			
				|  |  | +        try:
 | 
	
		
			
				|  |  | +            encryption = backup['encryption']
 | 
	
		
			
				|  |  | +        except KeyError:
 | 
	
		
			
				|  |  | +            encryption = True
 | 
	
		
			
				|  |  | +        try:
 | 
	
		
			
				|  |  | +            target_via_sshfs = backup['target_via_sshfs']
 | 
	
		
			
				|  |  | +        except KeyError:
 | 
	
		
			
				|  |  | +            target_via_sshfs = False
 | 
	
		
			
				|  |  | +        backup_command = ['duplicity']
 | 
	
		
			
				|  |  | +        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']]
 | 
	
		
			
				|  |  | +        backup_command += [backup['source_path']]
 | 
	
		
			
				|  |  | +        try:
 | 
	
		
			
				|  |  | +            target_mount_path = None
 | 
	
		
			
				|  |  | +            if 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)
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                backup_command += [backup['target_url']]
 | 
	
		
			
				|  |  | +            try:
 | 
	
		
			
				|  |  | +                print('+ {}'.format(command_join(backup_command)))
 | 
	
		
			
				|  |  | +                subprocess.check_call(backup_command)
 | 
	
		
			
				|  |  | +            finally:
 | 
	
		
			
				|  |  | +                if target_mount_path:
 | 
	
		
			
				|  |  | +                    sshfs_unmount(target_mount_path)
 | 
	
		
			
				|  |  | +        finally:
 | 
	
		
			
				|  |  | +            if target_mount_path:
 | 
	
		
			
				|  |  | +                os.rmdir(target_mount_path)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def run(command, config_path):
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    with open(config_path) as config_file:
 | 
	
		
			
				|  |  | +        config = yaml.load(config_file.read())
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if command == 'backup':
 | 
	
		
			
				|  |  | +        backup(config)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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',
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +    subparsers.add_parser('backup')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return argparser
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def main(argv):
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    argparser = _init_argparser()
 | 
	
		
			
				|  |  | +    try:
 | 
	
		
			
				|  |  | +        import argcomplete
 | 
	
		
			
				|  |  | +        argcomplete.autocomplete(argparser)
 | 
	
		
			
				|  |  | +    except ImportError:
 | 
	
		
			
				|  |  | +        pass
 | 
	
		
			
				|  |  | +    args = argparser.parse_args(argv)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    run(**vars(args))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return 0
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +if __name__ == "__main__":
 | 
	
		
			
				|  |  | +    import sys
 | 
	
		
			
				|  |  | +    sys.exit(main(sys.argv[1:]))
 |