]> arthur.barton.de Git - bup.git/blob - cmd/import-duplicity-cmd.py
565aca629c8cbca365152a4badddb1d11a794942
[bup.git] / cmd / import-duplicity-cmd.py
1 #!/bin/sh
2 """": # -*-python-*-
3 bup_python="$(dirname "$0")/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
5 """
6 # end of bup preamble
7
8 from __future__ import absolute_import
9 from calendar import timegm
10 from pipes import quote
11 from subprocess import check_call
12 from time import strftime, strptime
13 import sys
14 import tempfile
15
16 from bup import git, options
17 from bup.helpers import handle_ctrl_c, log, readpipe, saved_errors, unlink
18 import bup.path
19
20 optspec = """
21 bup import-duplicity [-n] <duplicity-source-url> <bup-save-name>
22 --
23 n,dry-run  don't do anything; just print what would be done
24 """
25
26
27 def logcmd(cmd):
28     if isinstance(cmd, basestring):
29         log(cmd + '\n')
30     else:
31         log(' '.join(map(quote, cmd)) + '\n')
32
33
34 def exc(cmd, shell=False):
35     global opt
36     logcmd(cmd)
37     if not opt.dry_run:
38         check_call(cmd, shell=shell)
39
40 def exo(cmd, shell=False):
41     global opt
42     logcmd(cmd)
43     if not opt.dry_run:
44         return readpipe(cmd, shell=shell)
45
46
47 handle_ctrl_c()
48
49 log('\nbup: import-duplicity is EXPERIMENTAL (proceed with caution)\n\n')
50
51 o = options.Options(optspec)
52 opt, flags, extra = o.parse(sys.argv[1:])
53
54 if len(extra) < 1 or not extra[0]:
55     o.fatal('duplicity source URL required')
56 if len(extra) < 2 or not extra[1]:
57     o.fatal('bup destination save name required')
58 if len(extra) > 2:
59     o.fatal('too many arguments')
60
61 source_url, save_name = extra
62 bup = bup.path.exe()
63
64 git.check_repo_or_die()
65
66 tmpdir = tempfile.mkdtemp(prefix='bup-import-dup-')
67 try:
68     dup = ['duplicity', '--archive-dir', tmpdir + '/dup-cache']
69     restoredir = tmpdir + '/restore'
70     tmpidx = tmpdir + '/index'
71     collection_status = \
72         exo(' '.join(map(quote, dup))
73             + ' collection-status --log-fd=3 %s 3>&1 1>&2' % quote(source_url),
74             shell=True)
75     # Duplicity output lines of interest look like this (one leading space):
76     #  full 20150222T073111Z 1 noenc
77     #  inc 20150222T073233Z 1 noenc
78     dup_timestamps = []
79     for line in collection_status.splitlines():
80         if line.startswith(' inc '):
81             assert(len(line) >= len(' inc 20150222T073233Z'))
82             dup_timestamps.append(line[5:21])
83         elif line.startswith(' full '):
84             assert(len(line) >= len(' full 20150222T073233Z'))
85             dup_timestamps.append(line[6:22])
86     for i, dup_ts in enumerate(dup_timestamps):
87         tm = strptime(dup_ts, '%Y%m%dT%H%M%SZ')
88         exc(['rm', '-rf', restoredir])
89         exc(dup + ['restore', '-t', dup_ts, source_url, restoredir])
90         exc([bup, 'index', '-uxf', tmpidx, restoredir])
91         exc([bup, 'save', '--strip', '--date', str(timegm(tm)), '-f', tmpidx,
92              '-n', save_name, restoredir])
93 finally:
94     exc(['rm', '-rf', tmpdir])
95
96 if saved_errors:
97     log('warning: %d errors encountered\n' % len(saved_errors))
98     sys.exit(1)