diff options
author | bescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109> | 2003-02-11 07:37:12 +0000 |
---|---|---|
committer | bescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109> | 2003-02-11 07:37:12 +0000 |
commit | 9613406fbab1949f66fe7858590cab990c7b4b25 (patch) | |
tree | 573a6106da5bb7ead2c4f17c45be664063d67e01 /rdiff-backup/rdiff_backup/Main.py | |
parent | e18d568c6fdfa1a73fea01362d5bdf31aaa0d5b5 (diff) | |
download | rdiff-backup-9613406fbab1949f66fe7858590cab990c7b4b25.tar.gz |
First pass at integrating regress code
At this point most of the tests work, but there are still problems
with the finaltest error tests on /proc, and with some selection
options. The regress code is totally unchecked, and regresstest.py is
unwritten.
git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@277 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
Diffstat (limited to 'rdiff-backup/rdiff_backup/Main.py')
-rw-r--r-- | rdiff-backup/rdiff_backup/Main.py | 141 |
1 files changed, 103 insertions, 38 deletions
diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index bfcc906..b9120fb 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -24,7 +24,7 @@ import getopt, sys, re, os from log import Log, LoggerError import Globals, Time, SetConnections, selection, robust, rpath, \ manage, backup, connection, restore, FilenameMapping, \ - Security, Hardlink + Security, Hardlink, regress, C action = None @@ -44,12 +44,12 @@ def parse_cmdlineoptions(arglist): try: optlist, args = getopt.getopt(arglist, "blr:sv:V", ["backup-mode", "calculate-average", "chars-to-quote=", - "current-time=", "exclude=", "exclude-device-files", - "exclude-filelist=", "exclude-filelist-stdin", - "exclude-globbing-filelist=", "exclude-mirror=", - "exclude-other-filesystems", "exclude-regexp=", - "exclude-special-files", "force", "include=", - "include-filelist=", "include-filelist-stdin", + "check-destination-dir", "current-time=", "exclude=", + "exclude-device-files", "exclude-filelist=", + "exclude-filelist-stdin", "exclude-globbing-filelist=", + "exclude-mirror=", "exclude-other-filesystems", + "exclude-regexp=", "exclude-special-files", "force", + "include=", "include-filelist=", "include-filelist-stdin", "include-globbing-filelist=", "include-regexp=", "list-changed-since=", "list-increments", "no-compression", "no-compression-regexp=", "no-hard-links", "null-separator", @@ -66,6 +66,7 @@ def parse_cmdlineoptions(arglist): for opt, arg in optlist: if opt == "-b" or opt == "--backup-mode": action = "backup" elif opt == "--calculate-average": action = "calculate-average" + elif opt == "--check-destination-dir": action = "check-destination-dir" elif opt == "--chars-to-quote": Globals.set('chars_to_quote', arg) Globals.set('quoting_enabled', 1) @@ -176,7 +177,8 @@ def set_action(): commandline_error("Two arguments are required (source, destination).") if l == 2 and (action == "list-increments" or action == "remove-older-than" or - action == "list-changed-since"): + action == "list-changed-since" or + action == "check-destination-dir"): commandline_error("Only use one argument, " "the root of the backup directory") if l > 2 and action != "calculate-average": @@ -211,6 +213,7 @@ def take_action(rps): elif action == "list-increments": ListIncrements(rps[0]) elif action == "remove-older-than": RemoveOlderThan(rps[0]) elif action == "calculate-average": CalculateAverage(rps) + elif action == "check-destination-dir": CheckDest(rps[0]) else: raise AssertionError("Unknown action " + action) def cleanup(): @@ -239,10 +242,13 @@ def Backup(rpin, rpout): backup_set_select(rpin) backup_init_dirs(rpin, rpout) if prevtime: + rpout.conn.Main.backup_touch_curmirror_local(rpin, rpout) Time.setprevtime(prevtime) backup.Mirror_and_increment(rpin, rpout, incdir) - else: backup.Mirror(rpin, rpout) - rpout.conn.Main.backup_touch_curmirror_local(rpin, rpout) + rpout.conn.Main.backup_remove_curmirror_local() + else: + backup.Mirror(rpin, rpout) + rpout.conn.Main.backup_touch_curmirror_local(rpin, rpout) def backup_set_select(rpin): """Create Select objects on source connection""" @@ -266,6 +272,7 @@ def backup_init_dirs(rpin, rpout): datadir = rpout.append_path("rdiff-backup-data") SetConnections.UpdateGlobal('rbdir', datadir) + checkdest_if_necessary(rpout) incdir = datadir.append_path("increments") prevtime = backup_get_mirrortime() @@ -305,39 +312,45 @@ def backup_warn_if_infinite_regress(rpin, rpout): source directory '%s'. This could cause an infinite regress. You may need to use the --exclude option.""" % (rpout.path, rpin.path), 2) -def backup_get_mirrorrps(): - """Return list of current_mirror rps""" - datadir = Globals.rbdir - if not datadir.isdir(): return [] - mirrorrps = [datadir.append(fn) for fn in datadir.listdir() - if fn.startswith("current_mirror.")] - return filter(lambda rp: rp.isincfile(), mirrorrps) - def backup_get_mirrortime(): """Return time in seconds of previous mirror, or None if cannot""" - mirrorrps = backup_get_mirrorrps() - if not mirrorrps: return None - if len(mirrorrps) > 1: - Log( -"""Warning: duplicate current_mirror files found. Perhaps something -went wrong during your last backup? Using """ + mirrorrps[-1].path, 2) - - return mirrorrps[-1].getinctime() + incbase = Globals.rbdir.append_path("current_mirror") + mirror_rps = restore.get_inclist(incbase) + assert len(mirror_rps) <= 1, \ + "Found %s current_mirror rps, expected <=1" % (len(mirror_rps),) + if mirror_rps: return mirror_rps[0].getinctime() + else: return None def backup_touch_curmirror_local(rpin, rpout): """Make a file like current_mirror.time.data to record time - Also updates rpout so mod times don't get messed up. This should - be run on the destination connection. + When doing an incremental backup, this should happen before any + other writes, and the file should be removed after all writes. + That way we can tell whether the previous session aborted if there + are two current_mirror files. + + When doing the initial full backup, the file can be created after + everything else is in place. """ - datadir = Globals.rbdir - map(rpath.RPath.delete, backup_get_mirrorrps()) - mirrorrp = datadir.append("current_mirror.%s.%s" % (Time.curtimestr, - "data")) + mirrorrp = Globals.rbdir.append("current_mirror.%s.%s" % (Time.curtimestr, + "data")) Log("Touching mirror marker %s" % mirrorrp.path, 6) mirrorrp.touch() - rpath.copy_attribs(rpin, rpout) + mirrorrp.fsync_with_dir() + +def backup_remove_curmirror_local(): + """Remove the older of the current_mirror files. Use at end of session""" + assert Globals.rbdir.conn is Globals.local_connection + curmir_incs = restore.get_inclist(Globals.rbdir.append("current_mirror")) + assert len(curmir_incs) == 2 + if curmir_incs[0].getinctime() < curmir_incs[1].getinctime(): + older_inc = curmir_incs[0] + else: older_inc = curmir_incs[1] + + C.sync() # Make sure everything is written before curmirror is removed + older_inc.sync_delete() + def Restore(src_rp, dest_rp = None): """Main restoring function @@ -366,6 +379,7 @@ def restore_common(rpin, target, time): if target.conn.os.getuid() == 0: SetConnections.UpdateGlobal('change_ownership', 1) mirror_root, index = restore_get_root(rpin) + restore_check_backup_dir(mirror_root) mirror = mirror_root.new_index(index) inc_rpath = datadir.append_path('increments', index) restore_init_select(mirror_root, target) @@ -404,6 +418,17 @@ Try restoring from an increment file (the filenames look like "specify --force to overwrite." % rpout.path) return rpin, rpout +def restore_check_backup_dir(rpin): + """Make sure backup dir root rpin is in consistent state""" + result = checkdest_need_check(rpin) + if result is None: + Log.FatalError("%s does not appear to be an rdiff-backup directory." + % (rpin.path,)) + elif result == 1: Log.FatalError( + "Previous backup to %s seems to have failed." + "Rerun rdiff-backup with --check-destination-dir option to revert" + "directory to state before unsuccessful session." % (rpin.path,)) + def restore_init_select(rpin, rpout): """Initialize Select @@ -465,6 +490,7 @@ def restore_get_root(rpin): def ListIncrements(rp): """Print out a summary of the increments and their times""" mirror_root, index = restore_get_root(rp) + restore_check_backup_dir(mirror_root) mirror_rp = mirror_root.new_index(index) inc_rpath = Globals.rbdir.append_path('increments', index) incs = restore.get_inclist(inc_rpath) @@ -484,11 +510,7 @@ def CalculateAverage(rps): def RemoveOlderThan(rootrp): """Remove all increment files older than a certain time""" - datadir = rootrp.append_path("rdiff-backup-data") - if not datadir.lstat() or not datadir.isdir(): - Log.FatalError("Unable to open rdiff-backup-data dir %s" % - (datadir.path,)) - + rom_check_dir(rootrp) try: time = Time.genstrtotime(remove_older_than_string) except Time.TimeException, exc: Log.FatalError(str(exc)) timep = Time.timetopretty(time) @@ -512,13 +534,56 @@ def RemoveOlderThan(rootrp): else: Log("Deleting increments at times:\n" + inc_pretty_time, 3) manage.delete_earlier_than(datadir, time) +def rom_check_dir(rootrp): + """Check destination dir before RemoveOlderThan""" + SetConnections.UpdateGlobal('rbdir', + rootrp.append_path("rdiff-backup-data")) + if not Globals.rbdir.isdir(): + Log.FatalError("Unable to open rdiff-backup-data dir %s" % + (datadir.path,)) + checkdest_if_necessary(rootrp) + def ListChangedSince(rp): """List all the files under rp that have changed since restoretime""" try: rest_time = Time.genstrtotime(restore_timestr) except Time.TimeException, exc: Log.FatalError(str(exc)) mirror_root, index = restore_get_root(rp) + restore_check_backup_dir(mirror_root) mirror_rp = mirror_root.new_index(index) inc_rp = mirror_rp.append_path("increments", index) restore.ListChangedSince(mirror_rp, inc_rp, rest_time) + +def CheckDest(dest_rp): + """Check the destination directory, """ + need_check = checkdest_need_check(dest_rp) + if need_check is None: + Log.FatalError("No destination dir found at %s" % (dest_rp.path,)) + elif need_check == 0: + Log.FatalError("Destination dir %s does not need checking" % + (dest_rp.path,)) + regress.Regress(dest_rp) + +def checkdest_need_check(dest_rp): + """Return None if no dest dir found, 1 if dest dir needs check, 0 o/w""" + assert dest_rp.conn is Globals.rbdir.conn + if not dest_rp.isdir() or not Globals.rbdir.isdir(): return None + curmirroot = Globals.rbdir.append("current_mirror") + curmir_incs = restore.get_inclist(curmirroot) + if not curmir_incs: return None + elif len(curmir_incs) == 1: return 0 + else: + assert len(curmir_incs) == 2, "Found too many current_mirror incs!" + return 1 + +def checkdest_if_necessary(dest_rp): + """Check the destination dir if necessary. + + This can/should be run before an incremental backup. + + """ + need_check = checkdest_need_check(dest_rp) + if need_check == 1: + Log("Previous backup seems to have failed, checking now.", 2) + regress.Regress(dest_rp) |