Commit 348ef5ce authored by bescoto's avatar bescoto

Restore excludes now leave existing files on target alone


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@497 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent ef4fc2dd
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
"""Start (and end) here - read arguments, set global settings, etc.""" """Start (and end) here - read arguments, set global settings, etc."""
from __future__ import generators from __future__ import generators
import getopt, sys, re, os import getopt, sys, re, os, cStringIO
from log import Log, LoggerError, ErrorLog from log import Log, LoggerError, ErrorLog
import Globals, Time, SetConnections, selection, robust, rpath, \ import Globals, Time, SetConnections, selection, robust, rpath, \
manage, backup, connection, restore, FilenameMapping, \ manage, backup, connection, restore, FilenameMapping, \
...@@ -501,16 +501,26 @@ def restore_set_fs_globals(target): ...@@ -501,16 +501,26 @@ def restore_set_fs_globals(target):
else: SetConnections.UpdateGlobal('chars_to_quote', "") else: SetConnections.UpdateGlobal('chars_to_quote', "")
def restore_set_select(mirror_rp, target): def restore_set_select(mirror_rp, target):
"""Set the selection iterator on mirror side from command line args """Set the selection iterator on both side from command line args
Here we set the selector on the mirror side, because that is where We must set both sides because restore filtering is different from
we will be filtering, but the pathnames are relative to the target select filtering. For instance, if a file is excluded it should
directory. not be deleted from the target directory.
The StringIO stuff is because filelists need to be read and then
duplicated, because we need two copies of them now.
""" """
def fp2string(fp):
buf = fp.read()
assert not fp.close()
return buf
select_data = map(fp2string, select_files)
if select_opts: if select_opts:
mirror_rp.conn.restore.MirrorStruct.set_mirror_select( mirror_rp.conn.restore.MirrorStruct.set_mirror_select(
target, select_opts, *select_files) target, select_opts, *map(cStringIO.StringIO, select_data))
target.conn.restore.TargetStruct.set_target_select(
target, select_opts, *map(cStringIO.StringIO, select_data))
def restore_start_log(rpin, target, time): def restore_start_log(rpin, target, time):
"""Open restore log file, log initial message""" """Open restore log file, log initial message"""
......
...@@ -25,11 +25,6 @@ import Globals, Time, Rdiff, Hardlink, rorpiter, selection, rpath, \ ...@@ -25,11 +25,6 @@ import Globals, Time, Rdiff, Hardlink, rorpiter, selection, rpath, \
log, static, robust, metadata, statistics, TempFile, eas_acls log, static, robust, metadata, statistics, TempFile, eas_acls
# This should be set to selection.Select objects over the source and
# mirror directories respectively.
_select_source = None
_select_mirror = None
# This will be set to the time of the current mirror # This will be set to the time of the current mirror
_mirror_time = None _mirror_time = None
# This will be set to the exact time to restore to (not restore_to_time) # This will be set to the exact time to restore to (not restore_to_time)
...@@ -263,9 +258,16 @@ static.MakeClass(MirrorStruct) ...@@ -263,9 +258,16 @@ static.MakeClass(MirrorStruct)
class TargetStruct: class TargetStruct:
"""Hold functions to be run on the target side when restoring""" """Hold functions to be run on the target side when restoring"""
def get_initial_iter(cls, target): _select = None
def set_target_select(cls, target, select_opts, *filelists):
"""Return a selection object iterating the rorpaths in target""" """Return a selection object iterating the rorpaths in target"""
return selection.Select(target).set_iter() cls._select = selection.Select(target)
cls._select.ParseArgs(select_opts, filelists)
cls._select.set_iter()
def get_initial_iter(cls, target):
"""Return selector previously set with set_initial_iter"""
return cls._select or selection.Select(target).set_iter()
def patch(cls, target, diff_iter): def patch(cls, target, diff_iter):
"""Patch target with the diffs from the mirror side """Patch target with the diffs from the mirror side
......
...@@ -694,10 +694,3 @@ class FilterIterITRB(rorpiter.ITRBranch): ...@@ -694,10 +694,3 @@ class FilterIterITRB(rorpiter.ITRBranch):
assert s == 2, s assert s == 2, s
self.base_queue = next_rorp self.base_queue = next_rorp
...@@ -102,6 +102,20 @@ class PathSetter(unittest.TestCase): ...@@ -102,6 +102,20 @@ class PathSetter(unittest.TestCase):
print "Restoring via cmdline: " + cmdstr print "Restoring via cmdline: " + cmdstr
assert not os.system(cmdstr) assert not os.system(cmdstr)
def exec_rb_restore_extra_args(self, time, extra_args, *args):
"""Like exec_rb_restore, but can provide extra arguments"""
arglist = []
arglist.append("--restore-as-of %s" % str(time))
arglist.append(extra_args)
arglist.append(self.src_prefix + args[0])
if len(args) > 1:
arglist.append(self.dest_prefix + args[1])
assert len(args) == 2
cmdstr = self.rb_schema + " ".join(arglist)
print "Restoring via cmdline: " + cmdstr
assert not os.system(cmdstr)
def delete_tmpdirs(self): def delete_tmpdirs(self):
"""Remove any temp directories created by previous tests""" """Remove any temp directories created by previous tests"""
assert not os.system(MiscDir + '/myrm testfiles/output* ' assert not os.system(MiscDir + '/myrm testfiles/output* '
...@@ -510,6 +524,49 @@ testfiles/increment2/changed_dir""") ...@@ -510,6 +524,49 @@ testfiles/increment2/changed_dir""")
self.assertRaises(OSError, os.lstat, self.assertRaises(OSError, os.lstat,
'testfiles/restoretarget1/executable2') 'testfiles/restoretarget1/executable2')
def testSelRestoreLocal(self):
"""Test selection options when restoring locally"""
self.set_connections(None, None, None, None)
self.run_sel_restore_test()
def testSelRestoreRemote(self):
"""Test selection options when both sides are remote"""
self.set_connections("test1/", "../", "test2/tmp/", "../../")
self.run_sel_restore_test("../../")
def run_sel_restore_test(self, prefix = ""):
"""Test selection options with restore"""
self.make_restore_sel_dir()
existing_file = self.make_restore_existing_target()
file1_target = Local.rpout1.append("file1")
file2_target = Local.rpout1.append("file2")
excludes = ("--exclude %s --exclude %s --force" %
(prefix + file1_target.path, prefix + existing_file.path))
self.exec_rb_restore_extra_args("now", excludes,
Local.rpout.path, Local.rpout1.path)
for rp in (file1_target, file2_target, existing_file):
rp.setdata()
assert not file1_target.lstat(), file1_target.lstat()
assert file2_target.lstat()
assert existing_file.lstat() # excluded file shouldn't be deleted
def make_restore_sel_dir(self):
"""Create rdiff-backup repository at Local.rpout"""
self.delete_tmpdirs()
Local.vft_in.mkdir()
rp1 = Local.vft_in.append("file1")
rp2 = Local.vft_in.append("file2")
rp1.touch()
rp2.touch()
self.exec_rb(None, Local.vft_in.path, Local.rpout.path)
Myrm(Local.vft_in.path)
def make_restore_existing_target(self):
"""Create an existing file in the restore target directory"""
Local.rpout1.mkdir()
existing_file = Local.rpout1.append("existing_file")
existing_file.touch()
return existing_file
class FinalCorrupt(PathSetter): class FinalCorrupt(PathSetter):
"""Test messing with things a bit and making sure they still work""" """Test messing with things a bit and making sure they still work"""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment