3 # https://sourceware.org/bugzilla/show_bug.cgi?id=26034
4 export "BUP_ARGV_0"="$0"
7 export "BUP_ARGV_${arg_i}"="$arg"
11 # Here to end of preamble replaced during install
12 bup_python="$(dirname "$0")/../../config/bin/python" || exit $?
13 exec "$bup_python" "$0"
17 from __future__ import absolute_import
18 from calendar import timegm
19 from pipes import quote
20 from subprocess import check_call
21 from time import strftime, strptime
27 sys.path[:0] = [os.path.dirname(os.path.realpath(__file__)) + '/..']
29 from bup import compat, git, helpers, options
30 from bup.compat import argv_bytes, str_type
31 from bup.helpers import (handle_ctrl_c,
40 bup import-duplicity [-n] <duplicity-source-url> <bup-save-name>
42 n,dry-run don't do anything; just print what would be done
46 log(shstr(cmd).decode(errors='backslashreplace') + '\n')
48 def exc(cmd, shell=False):
52 check_call(cmd, shell=shell)
54 def exo(cmd, shell=False, preexec_fn=None, close_fds=True):
58 return helpers.exo(cmd, shell=shell, preexec_fn=preexec_fn,
59 close_fds=close_fds)[0]
61 def redirect_dup_output():
68 log('\nbup: import-duplicity is EXPERIMENTAL (proceed with caution)\n\n')
70 o = options.Options(optspec)
71 opt, flags, extra = o.parse(compat.argv[1:])
73 if len(extra) < 1 or not extra[0]:
74 o.fatal('duplicity source URL required')
75 if len(extra) < 2 or not extra[1]:
76 o.fatal('bup destination save name required')
78 o.fatal('too many arguments')
80 source_url, save_name = extra
81 source_url = argv_bytes(source_url)
82 save_name = argv_bytes(save_name)
85 git.check_repo_or_die()
87 tmpdir = tempfile.mkdtemp(prefix=b'bup-import-dup-')
89 dup = [b'duplicity', b'--archive-dir', tmpdir + b'/dup-cache']
90 restoredir = tmpdir + b'/restore'
91 tmpidx = tmpdir + b'/index'
94 exo(dup + [b'collection-status', b'--log-fd=3', source_url],
95 close_fds=False, preexec_fn=redirect_dup_output) # i.e. 3>&1 1>&2
96 # Duplicity output lines of interest look like this (one leading space):
97 # full 20150222T073111Z 1 noenc
98 # inc 20150222T073233Z 1 noenc
100 for line in collection_status.splitlines():
101 if line.startswith(b' inc '):
102 assert(len(line) >= len(b' inc 20150222T073233Z'))
103 dup_timestamps.append(line[5:21])
104 elif line.startswith(b' full '):
105 assert(len(line) >= len(b' full 20150222T073233Z'))
106 dup_timestamps.append(line[6:22])
107 for i, dup_ts in enumerate(dup_timestamps):
108 tm = strptime(dup_ts.decode('ascii'), '%Y%m%dT%H%M%SZ')
109 exc([b'rm', b'-rf', restoredir])
110 exc(dup + [b'restore', b'-t', dup_ts, source_url, restoredir])
111 exc([bup, b'index', b'-uxf', tmpidx, restoredir])
112 exc([bup, b'save', b'--strip', b'--date', b'%d' % timegm(tm),
113 b'-f', tmpidx, b'-n', save_name, restoredir])
116 exc([b'rm', b'-rf', tmpdir])
119 log('warning: %d errors encountered\n' % len(saved_errors))