rdiff-backup-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Fwd: [rdiff-backup-users] Resuming initial backups]


From: Josh Nisly
Subject: [Fwd: [rdiff-backup-users] Resuming initial backups]
Date: Fri, 03 Oct 2008 11:36:50 -0500
User-agent: Thunderbird 2.0.0.17 (X11/20080925)

After over a month, I finally have a patch to implement this. It detects a failed initial backup by the following checks:
* No current_mirror file
* 0 or 1 error_log files
* 0 or 1 mirror_metadata files
* No files in the increments directory

If all of the above are true, then it removes all contents of the rdiff-backup directory, allowing backup resumption. I believe that this guarantees that there will be no information lost, since there are no historical increments being lost.

Andrew, what do you think of the patch? (Especially the delete_dir_no_files function.)

Thanks,
JoshN

-------- Original Message --------
Subject:        [rdiff-backup-users] Resuming initial backups
Date:   Fri, 22 Aug 2008 09:24:23 -0500
From:   Josh Nisly <address@hidden>
To:     rdiff-backup <address@hidden>



I have clients that back up large data sets over the internet. The initial backup is always difficult, since rdiff-backup does not handle it well when the initial backup fails. If it fails, subsequent backups display an error saying that the initial backup probably failed, and it should be removed.

My question is this: couldn't rdiff-backup detect this and handle it automatically? I've added code to my private branch that checks if there is no current mirror and one or fewer error_log and mirror_metadata files. If so, it removes them and runs normally. Does this seem like a reasonable way to handle it? If so, I'd be happy to submit a patch implementing it.

Thanks,
JoshN


--- rdiff_backup/Security.py    20 Aug 2008 17:43:25 -0000      1.36
+++ rdiff_backup/Security.py    3 Oct 2008 16:14:15 -0000
@@ -45,7 +45,8 @@
                                 'os.chown':0, 'os.remove':0, 'os.removedirs':0,
                                 'os.rename':0, 'os.renames':0, 'os.rmdir':0, 
'os.unlink':0,
                                 'os.utime':0, 'os.lchown':0, 'os.link':1, 
'os.symlink':1,
-                                'os.mkdir':0, 'os.makedirs':0}
+                                'os.mkdir':0, 'os.makedirs':0,
+                                'rpath.delete_dir_no_files':0}
                                 
 def initialize(action, cmdpairs):
        """Initialize allowable request list and chroot"""
@@ -180,6 +181,7 @@
        if sec_level == "all":
                l.extend(["os.mkdir", "os.chown", "os.lchown", "os.rename",
                                  "os.unlink", "os.remove", "os.chmod", 
"os.makedirs",
+                                 "rpath.delete_dir_no_files",
                                  "backup.DestinationStruct.patch",
                                  "restore.TargetStruct.get_initial_iter",
                                  "restore.TargetStruct.patch",
--- rdiff_backup/rpath.py       22 Jul 2008 17:46:59 -0000      1.126
+++ rdiff_backup/rpath.py       3 Oct 2008 16:14:07 -0000
@@ -358,6 +358,14 @@
        else: basestr = ".".join(dotsplit[:-2])
        return (compressed, timestring, ext, basestr)
 
+def delete_dir_no_files(rp):
+       """Deletes the directory at self.path if empty. Raises if the
+       directory contains files."""
+       log.Log("Determining if directory contains files: %s" % rp.path, 7)
+       if rp.contains_files():
+               raise RPathException("Directory contains files.")
+       rp.delete()
+
 
 class RORPath:
        """Read Only RPath - carry information about a path
@@ -1047,6 +1055,18 @@
                else: self.conn.os.unlink(self.path)
                self.setdata()
 
+       def contains_files(self):
+               log.Log("Determining if directory contains files: %s" % 
self.path, 7)
+               dir_entries = self.listdir()
+               for entry in dir_entries:
+                       child_rp = self.append_path(entry)
+                       if not child_rp.isdir():
+                               return False
+                       else:
+                               if not child_rp.contains_files():
+                                       return False
+               return True
+
        def quote(self):
                """Return quoted self.path for use with os.system()"""
                return '"%s"' % self.regex_chars_to_quote.sub(
--- rdiff_backup/Main.py        20 Aug 2008 17:43:25 -0000      1.119
+++ rdiff_backup/Main.py        3 Oct 2008 14:34:11 -0000
@@ -378,6 +378,46 @@
                Log.FatalError("Source %s is not a directory" % rpin.path)
        Globals.rbdir = rpout.append_path("rdiff-backup-data")
 
+def fix_failed_initial_backup(rpout):
+       if Globals.rbdir.lstat():
+               rbdir_files = Globals.rbdir.listdir()
+               mirror_markers = filter(lambda x: 
x.startswith("current_mirror"),
+                                                               rbdir_files)
+               error_logs = filter(lambda x: x.startswith("error_log"),
+                                                       rbdir_files)
+               metadata_mirrors = filter(lambda x: 
x.startswith("mirror_metadata"),
+                                                               rbdir_files)
+               # If we have no current_mirror marker, and the increments 
directory
+               # is empty, we most likely have a failed backup. At any rate, 
if the
+               # increments directory is empty, we can't lose any historical 
diffs,
+               # because there aren't any.
+               if not mirror_markers and len(error_logs) <= 1 and \
+                               len(metadata_mirrors) <= 1:
+                       Log("Found interrupted initial backup. Removing...",  2)
+                       # Try to delete the increments dir first
+                       if 'increments' in rbdir_files:
+                               rbdir_files.remove('increments') # Only try to 
delete once
+                               rp = Globals.rbdir.append('increments')
+                               try:
+                                       rp.conn.rpath.delete_dir_no_files(rp)
+                               except rpath.RPathException:
+                                       Log("Increments dir contains files.", 4)
+                                       return
+                               except Security.Violation:
+                                       Log("Server doesn't support resuming.", 
4)
+                                       return
+                       for file_name in rbdir_files:
+                               rp = Globals.rbdir.append_path(file_name)
+                               if rp.isdir():
+                                       try:
+                                               
rp.conn.rpath.delete_dir_no_files(rp)
+                                       except rpath.RPathException:
+                                               Log("Directory contains 
files.", 4)
+                                               return
+                               else:
+                                       rp.delete()
+
+
 def backup_set_rbdir(rpin, rpout):
        """Initialize data dir and logging"""
        global incdir
@@ -389,6 +429,8 @@
 rdiff-backup like this could mess up what is currently in it.  If you
 want to update or overwrite it, run rdiff-backup with the --force
 option.""" % rpout.path)
+       else: # Check for interrupted initial backup
+               fix_failed_initial_backup(rpout)
 
        if not Globals.rbdir.lstat(): Globals.rbdir.mkdir()
        SetConnections.UpdateGlobal('rbdir', Globals.rbdir)
 

reply via email to

[Prev in Thread] Current Thread [Next in Thread]