591
by Kenneth Loafman
* Fixed #409593 deja-dup (or duplicity) deletes all signatures |
1 |
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
|
2 |
#
|
|
3 |
# Copyright 2002 Ben Escoto <ben@emerose.org>
|
|
4 |
# Copyright 2007 Kenneth Loafman <kenneth@loafman.com>
|
|
5 |
#
|
|
6 |
# This file is part of duplicity.
|
|
7 |
#
|
|
8 |
# Duplicity is free software; you can redistribute it and/or modify it
|
|
9 |
# under the terms of the GNU General Public License as published by the
|
|
10 |
# Free Software Foundation; either version 2 of the License, or (at your
|
|
11 |
# option) any later version.
|
|
12 |
#
|
|
13 |
# Duplicity is distributed in the hope that it will be useful, but
|
|
14 |
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
16 |
# General Public License for more details.
|
|
17 |
#
|
|
18 |
# You should have received a copy of the GNU General Public License
|
|
19 |
# along with duplicity; if not, write to the Free Software Foundation,
|
|
20 |
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
21 |
||
22 |
import config |
|
23 |
import sys, os, unittest |
|
24 |
||
25 |
import duplicity.backend |
|
26 |
from duplicity import path |
|
27 |
||
28 |
config.setup() |
|
29 |
||
30 |
# This can be changed to select the URL to use
|
|
31 |
backend_url = "file://testfiles/output" |
|
32 |
||
33 |
# Extra arguments to be passed to duplicity
|
|
597
by Kenneth Loafman
Merged in ~l2g/duplicity/test-compat from Larry Gilbert which made |
34 |
other_args = ["-v0", "--no-print-statistics"] |
35 |
#other_args = []
|
|
591
by Kenneth Loafman
* Fixed #409593 deja-dup (or duplicity) deletes all signatures |
36 |
|
37 |
# If this is set to true, after each backup, verify contents
|
|
38 |
verify = 1 |
|
39 |
||
40 |
class CmdError(Exception): |
|
41 |
"""Indicates an error running an external command"""
|
|
42 |
pass
|
|
43 |
||
44 |
class CleanupTest(unittest.TestCase): |
|
45 |
"""
|
|
46 |
Test cleanup using duplicity binary
|
|
47 |
"""
|
|
48 |
def setUp(self): |
|
607
by Kenneth Loafman
Fix problems with unittests under Jaunty. It appears that redirection |
49 |
assert not os.system("tar xzf testfiles.tar.gz > /dev/null 2>&1") |
591
by Kenneth Loafman
* Fixed #409593 deja-dup (or duplicity) deletes all signatures |
50 |
|
51 |
def tearDown(self): |
|
52 |
assert not os.system("rm -rf testfiles tempdir temp2.tar") |
|
53 |
||
54 |
def run_duplicity(self, arglist, options = [], current_time = None): |
|
55 |
"""
|
|
56 |
Run duplicity binary with given arguments and options
|
|
57 |
"""
|
|
58 |
options.append("--archive-dir testfiles/cache") |
|
59 |
cmd_list = ["../duplicity-bin"] |
|
60 |
cmd_list.extend(options + ["--allow-source-mismatch"]) |
|
61 |
if current_time: |
|
62 |
cmd_list.append("--current-time %s" % (current_time,)) |
|
63 |
if other_args: |
|
64 |
cmd_list.extend(other_args) |
|
65 |
cmd_list.extend(arglist) |
|
66 |
cmdline = " ".join(cmd_list) |
|
67 |
#print "Running '%s'." % cmdline
|
|
68 |
if not os.environ.has_key('PASSPHRASE'): |
|
69 |
os.environ['PASSPHRASE'] = 'foobar' |
|
70 |
# print "CMD: %s" % cmdline
|
|
71 |
return_val = os.system(cmdline) |
|
72 |
if return_val: |
|
73 |
raise CmdError(return_val) |
|
74 |
||
75 |
def backup(self, type, input_dir, options = [], current_time = None): |
|
76 |
"""
|
|
77 |
Run duplicity backup to default directory
|
|
78 |
"""
|
|
79 |
options = options[:] |
|
80 |
if type == "full": |
|
81 |
options.insert(0, 'full') |
|
82 |
args = [input_dir, "'%s'" % backend_url] |
|
83 |
self.run_duplicity(args, options, current_time) |
|
84 |
||
85 |
def restore(self, file_to_restore = None, time = None, options = [], |
|
86 |
current_time = None): |
|
87 |
options = options[:] # just nip any mutability problems in bud |
|
88 |
assert not os.system("rm -rf testfiles/restore_out") |
|
89 |
args = ["'%s'" % backend_url, "testfiles/restore_out"] |
|
90 |
if file_to_restore: |
|
91 |
options.extend(['--file-to-restore', file_to_restore]) |
|
92 |
if time: |
|
93 |
options.extend(['--restore-time', str(time)]) |
|
94 |
self.run_duplicity(args, options, current_time) |
|
95 |
||
96 |
def verify(self, dirname, file_to_verify = None, time = None, options = [], |
|
97 |
current_time = None): |
|
98 |
options = ["verify"] + options[:] |
|
99 |
args = ["'%s'" % backend_url, dirname] |
|
100 |
if file_to_verify: |
|
101 |
options.extend(['--file-to-restore', file_to_verify]) |
|
102 |
if time: |
|
103 |
options.extend(['--restore-time', str(time)]) |
|
104 |
self.run_duplicity(args, options, current_time) |
|
105 |
||
106 |
def cleanup(self, options = []): |
|
107 |
"""
|
|
108 |
Run duplicity cleanup to default directory
|
|
109 |
"""
|
|
110 |
options = ["cleanup"] + options[:] |
|
111 |
args = ["'%s'" % backend_url] |
|
112 |
self.run_duplicity(args, options) |
|
113 |
||
114 |
def deltmp(self): |
|
115 |
"""
|
|
116 |
Delete temporary directories
|
|
117 |
"""
|
|
118 |
assert not os.system("rm -rf testfiles/output " |
|
119 |
"testfiles/restore_out testfiles/cache") |
|
120 |
assert not os.system("mkdir testfiles/output testfiles/cache") |
|
121 |
backend = duplicity.backend.get_backend(backend_url) |
|
122 |
bl = backend.list() |
|
123 |
if bl: |
|
124 |
backend.delete(backend.list()) |
|
125 |
backend.close() |
|
126 |
||
127 |
def runtest(self, dirlist, backup_options = [], restore_options = []): |
|
128 |
"""
|
|
129 |
Run backup/restore test on directories in dirlist
|
|
130 |
"""
|
|
131 |
assert len(dirlist) >= 1 |
|
132 |
self.deltmp() |
|
133 |
||
134 |
# Back up directories to local backend
|
|
135 |
current_time = 100000 |
|
136 |
self.backup("full", dirlist[0], current_time = current_time, |
|
137 |
options = backup_options) |
|
138 |
for new_dir in dirlist[1:]: |
|
139 |
current_time += 100000 |
|
140 |
self.backup("inc", new_dir, current_time = current_time, |
|
141 |
options = backup_options) |
|
142 |
||
143 |
# Restore each and compare them
|
|
144 |
for i in range(len(dirlist)): |
|
145 |
dirname = dirlist[i] |
|
146 |
current_time = 100000*(i + 1) |
|
147 |
self.restore(time = current_time, options = restore_options) |
|
148 |
self.check_same(dirname, "testfiles/restore_out") |
|
149 |
if verify: |
|
150 |
self.verify(dirname, |
|
151 |
time = current_time, options = restore_options) |
|
152 |
||
153 |
def check_same(self, filename1, filename2): |
|
154 |
"""
|
|
155 |
Verify two filenames are the same
|
|
156 |
"""
|
|
157 |
path1, path2 = path.Path(filename1), path.Path(filename2) |
|
158 |
assert path1.compare_recursive(path2, verbose = 1) |
|
159 |
||
160 |
def test_cleanup_after_partial(self): |
|
161 |
"""
|
|
162 |
Regression test for https://bugs.launchpad.net/bugs/409593
|
|
163 |
where duplicity deletes all the signatures during a cleanup
|
|
164 |
after a failed backup.
|
|
165 |
"""
|
|
166 |
#TODO: find something better than /etc for source
|
|
167 |
self.deltmp() |
|
168 |
self.backup("full", "/etc", options = ["--vol 1"]) |
|
169 |
self.backup("inc", "/etc", options = ["--vol 1"]) |
|
170 |
self.backup("inc", "/etc", options = ["--vol 1"]) |
|
171 |
# we know we're going to fail these, they are forced
|
|
172 |
try: |
|
173 |
self.backup("full", "/etc", options = ["--vol 1", "--fail 1"]) |
|
174 |
except CmdError: |
|
175 |
pass
|
|
176 |
# the cleanup should go OK
|
|
177 |
self.cleanup(options = ["--force"]) |
|
178 |
self.backup("inc", "/etc", options = ["--vol 1"]) |
|
179 |
self.verify("/etc") |
|
180 |
||
181 |
||
182 |
if __name__ == "__main__": |
|
183 |
unittest.main() |