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