]> arthur.barton.de Git - bup.git/blob - cmd/import-duplicity-cmd.py
import-duplicity-cmd: adjust for python 3 and enable test
[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 os
14 import sys
15 import tempfile
16
17 from bup import git, helpers, options
18 from bup.compat import argv_bytes, str_type
19 from bup.helpers import (handle_ctrl_c,
20                          log,
21                          readpipe,
22                          shstr,
23                          saved_errors,
24                          unlink)
25 import bup.path
26
27 optspec = """
28 bup import-duplicity [-n] <duplicity-source-url> <bup-save-name>
29 --
30 n,dry-run  don't do anything; just print what would be done
31 """
32
33 def logcmd(cmd):
34     log(shstr(cmd).decode('iso-8859-1', errors='replace') + '\n')
35
36 def exc(cmd, shell=False):
37     global opt
38     logcmd(cmd)
39     if not opt.dry_run:
40         check_call(cmd, shell=shell)
41
42 def exo(cmd, shell=False, preexec_fn=None, close_fds=True):
43     global opt
44     logcmd(cmd)
45     if not opt.dry_run:
46         return helpers.exo(cmd, shell=shell, preexec_fn=preexec_fn,
47                            close_fds=close_fds)[0]
48
49 def redirect_dup_output():
50     os.dup2(1, 3)
51     os.dup2(1, 2)
52
53
54 handle_ctrl_c()
55
56 log('\nbup: import-duplicity is EXPERIMENTAL (proceed with caution)\n\n')
57
58 o = options.Options(optspec)
59 opt, flags, extra = o.parse(sys.argv[1:])
60
61 if len(extra) < 1 or not extra[0]:
62     o.fatal('duplicity source URL required')
63 if len(extra) < 2 or not extra[1]:
64     o.fatal('bup destination save name required')
65 if len(extra) > 2:
66     o.fatal('too many arguments')
67
68 source_url, save_name = extra
69 source_url = argv_bytes(source_url)
70 save_name = argv_bytes(save_name)
71 bup = bup.path.exe()
72
73 git.check_repo_or_die()
74
75 tmpdir = tempfile.mkdtemp(prefix=b'bup-import-dup-')
76 try:
77     dup = [b'duplicity', b'--archive-dir', tmpdir + b'/dup-cache']
78     restoredir = tmpdir + b'/restore'
79     tmpidx = tmpdir + b'/index'
80
81     collection_status = \
82         exo(dup + [b'collection-status', b'--log-fd=3', source_url],
83             close_fds=False, preexec_fn=redirect_dup_output)  # i.e. 3>&1 1>&2
84     # Duplicity output lines of interest look like this (one leading space):
85     #  full 20150222T073111Z 1 noenc
86     #  inc 20150222T073233Z 1 noenc
87     dup_timestamps = []
88     for line in collection_status.splitlines():
89         if line.startswith(b' inc '):
90             assert(len(line) >= len(b' inc 20150222T073233Z'))
91             dup_timestamps.append(line[5:21])
92         elif line.startswith(b' full '):
93             assert(len(line) >= len(b' full 20150222T073233Z'))
94             dup_timestamps.append(line[6:22])
95     for i, dup_ts in enumerate(dup_timestamps):
96         tm = strptime(dup_ts.decode('ascii'), '%Y%m%dT%H%M%SZ')
97         exc([b'rm', b'-rf', restoredir])
98         exc(dup + [b'restore', b'-t', dup_ts, source_url, restoredir])
99         exc([bup, b'index', b'-uxf', tmpidx, restoredir])
100         exc([bup, b'save', b'--strip', b'--date', b'%d' % timegm(tm),
101              b'-f', tmpidx, b'-n', save_name, restoredir])
102     sys.stderr.flush()
103 finally:
104     exc([b'rm', b'-rf', tmpdir])
105
106 if saved_errors:
107     log('warning: %d errors encountered\n' % len(saved_errors))
108     sys.exit(1)