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