| 
					
				 | 
			
			
				@@ -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:])) 
			 |