From 98a31c8656bb61659e1eea4f649d51a214ce806d Mon Sep 17 00:00:00 2001 From: bescoto Date: Sun, 31 Aug 2003 21:03:53 +0000 Subject: Added session number like '3B' to general time specification git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@415 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/CHANGELOG | 4 ++++ rdiff-backup/TODO | 2 -- rdiff-backup/rdiff-backup.1 | 4 ++++ rdiff-backup/rdiff_backup/Main.py | 8 ++++---- rdiff-backup/rdiff_backup/Time.py | 31 +++++++++++++++++++++++++--- rdiff-backup/rdiff_backup/restore.py | 14 ++++++++----- rdiff-backup/testing/finaltest.py | 39 ++++++++++++++++++++++++++++++++++++ rdiff-backup/testing/restoretest.py | 20 ++++++++++++++++++ 8 files changed, 108 insertions(+), 14 deletions(-) diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index 4a1bdde..a74cf10 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -20,6 +20,10 @@ forthcoming attr/facl utilities. Fixed problems with --restrict options that would cause proper sessions to fail. Thanks to Randall Nortman for error report. +Added new time specification by backup number. So now you can +'--remove-older-than 2B' or '--list-at-time 0B'. Original suggestion +by Alan Bailward. + New in v0.13.1 (2003/08/08) --------------------------- diff --git a/rdiff-backup/TODO b/rdiff-backup/TODO index 6741564..79b5894 100644 --- a/rdiff-backup/TODO +++ b/rdiff-backup/TODO @@ -1,7 +1,5 @@ See if regressing takes too much memory (large directories). -Alan Bailward's suggestion to add removing older than 4 sessions. - write test case for --calculate-statistics Use ctime to check whether files have been changed. See message: diff --git a/rdiff-backup/rdiff-backup.1 b/rdiff-backup/rdiff-backup.1 index ad17e7a..f3db272 100644 --- a/rdiff-backup/rdiff-backup.1 +++ b/rdiff-backup/rdiff-backup.1 @@ -536,6 +536,10 @@ A date format of the form YYYY/MM/DD, YYYY-MM-DD, MM/DD/YYYY, or MM/DD/YYYY, which indicates midnight on the day in question, relative to the current timezone settings. For instance, "2002/3/5", "03-05-2002", and "2002-3-05" all mean March 5th, 2002. +.IP 6. +A backup session specification which is a non-negative integer +followed by 'B'. For instance, '0B' specifies the time of the current +mirror, and '3B' specifies the time of the 3rd newest increment. .SH REMOTE OPERATION In order to access remote files, rdiff-backup opens up a pipe to a diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index ab26903..ef3537d 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -413,11 +413,11 @@ def Restore(src_rp, dest_rp, restore_as_of = None): restore_set_fs_globals(dest_rp) src_rp = restore_init_quoting(src_rp) restore_check_backup_dir(restore_root, src_rp, restore_as_of) + inc_rpath = Globals.rbdir.append_path('increments', restore_index) if restore_as_of: - try: time = Time.genstrtotime(restore_timestr) + try: time = Time.genstrtotime(restore_timestr, rp = inc_rpath) except Time.TimeException, exc: Log.FatalError(str(exc)) else: time = src_rp.getinctime() - inc_rpath = Globals.rbdir.append_path('increments', restore_index) restore_set_select(restore_root, dest_rp) restore_start_log(src_rp, dest_rp, time) restore.Restore(restore_root.new_index(restore_index), @@ -632,9 +632,9 @@ def rot_check_dir(rootrp): def ListChangedSince(rp): """List all the files under rp that have changed since restoretime""" + assert restore_set_root(rp) try: rest_time = Time.genstrtotime(restore_timestr) except Time.TimeException, exc: Log.FatalError(str(exc)) - assert restore_set_root(rp) restore_check_backup_dir(restore_root) mirror_rp = restore_root.new_index(restore_index) inc_rp = mirror_rp.append_path("increments", restore_index) @@ -643,9 +643,9 @@ def ListChangedSince(rp): def ListAtTime(rp): """List files in archive under rp that are present at restoretime""" + assert restore_set_root(rp) try: rest_time = Time.genstrtotime(restore_timestr) except Time.TimeException, exc: Log.FatalError(str(exc)) - assert restore_set_root(rp) restore_check_backup_dir(restore_root) mirror_rp = restore_root.new_index(restore_index) inc_rp = mirror_rp.append_path("increments", restore_index) diff --git a/rdiff-backup/rdiff_backup/Time.py b/rdiff-backup/rdiff_backup/Time.py index 289df0a..e3a6173 100644 --- a/rdiff-backup/rdiff_backup/Time.py +++ b/rdiff-backup/rdiff_backup/Time.py @@ -19,7 +19,7 @@ """Provide time related exceptions and functions""" -import time, types, re +import time, types, re, sys import Globals @@ -28,6 +28,7 @@ class TimeException(Exception): pass _interval_conv_dict = {"s": 1, "m": 60, "h": 3600, "D": 86400, "W": 7*86400, "M": 30*86400, "Y": 365*86400} _integer_regexp = re.compile("^[0-9]+$") +_session_regexp = re.compile("^[0-9]+B$") _interval_regexp = re.compile("^([0-9]+)([smhDWMY])") _genstr_date_regexp1 = re.compile("^(?P[0-9]{4})[-/]" "(?P[0-9]{1,2})[-/](?P[0-9]{1,2})$") @@ -174,8 +175,27 @@ def cmp(time1, time2): elif time1 == time2: return 0 else: return 1 -def genstrtotime(timestr, curtime = None): - """Convert a generic time string to a time in seconds""" +def time_from_session(session_num, rp = None): + """Return time in seconds of given backup + + The current mirror is session_num 0, the next oldest increment has + number 1, etc. Requires that the Globals.rbdir directory be set. + + """ + session_times = Globals.rbdir.conn.restore.MirrorStruct \ + .get_increment_times() + session_times.sort() + if len(session_times) < session_num: + return session_times[0] # Use oldest if two few backups + return session_times[-session_num-1] + +def genstrtotime(timestr, curtime = None, rp = None): + """Convert a generic time string to a time in seconds + + rp is used when the time is of the form "4B" or similar. Then the + times of the increments of that particular file are used. + + """ if curtime is None: curtime = globals()['curtime'] if timestr == "now": return curtime @@ -196,6 +216,10 @@ the day).""" % timestr) t = stringtotime(timestr) or stringtotime(timestr+gettzd()) if t: return t + # Test for time given as number of backups, like 3B + if _session_regexp.search(timestr): + return time_from_session(int(timestr[:-1]), rp) + try: # test for an interval, like "2 days ago" return curtime - intstringtoseconds(timestr) except TimeException: pass @@ -209,3 +233,4 @@ the day).""" % timestr) t = stringtotime(timestr) if t: return t else: error() + diff --git a/rdiff-backup/rdiff_backup/restore.py b/rdiff-backup/rdiff_backup/restore.py index f4add16..aa6c6fe 100644 --- a/rdiff-backup/rdiff_backup/restore.py +++ b/rdiff-backup/rdiff_backup/restore.py @@ -125,16 +125,20 @@ class MirrorStruct: older one here. """ - global _rest_time - base_incs = get_inclist(Globals.rbdir.append("increments")) - if not base_incs: return _mirror_time - inctimes = [inc.getinctime() for inc in base_incs] - inctimes.append(_mirror_time) + inctimes = cls.get_increment_times() older_times = filter(lambda time: time <= restore_to_time, inctimes) if older_times: return max(older_times) else: # restore time older than oldest increment, just return that return min(inctimes) + def get_increment_times(cls, rp = None): + """Return list of times of backups, including current mirror""" + if not _mirror_time: return_list = [cls.get_mirror_time()] + else: return_list = [_mirror_time] + if not rp or not rp.index: rp = Globals.rbdir.append("increments") + for inc in get_inclist(rp): return_list.append(inc.getinctime()) + return return_list + def initialize_rf_cache(cls, mirror_base, inc_base): """Set cls.rf_cache to CachedRF object""" inc_list = get_inclist(inc_base) diff --git a/rdiff-backup/testing/finaltest.py b/rdiff-backup/testing/finaltest.py index 85f3378..5608220 100644 --- a/rdiff-backup/testing/finaltest.py +++ b/rdiff-backup/testing/finaltest.py @@ -1,3 +1,4 @@ +from __future__ import generators import unittest, os, re, sys, time from commontest import * from rdiff_backup import Globals, log, rpath, robust, FilenameMapping @@ -333,6 +334,8 @@ class FinalMisc(PathSetter): self.set_connections(None, None, None, None) self.exec_rb_extra_args(None, '--list-changed-since 10000', 'testfiles/restoretest3') + self.exec_rb_extra_args(None, '--list-changed-since 2B', + 'testfiles/restoretest3') def testListChangeSinceRemote(self): """Test --list-changed-since mode remotely. Uses restoretest3""" @@ -364,6 +367,42 @@ class FinalMisc(PathSetter): self.exec_rb_extra_args(None, "--list-increment-sizes", "testfiles/restoretest3") + def get_all_increments(self, rp): + """Iterate all increments at or below given directory""" + assert rp.isdir() + dirlist = rp.listdir() + dirlist.sort() + for filename in dirlist: + subrp = rp.append(filename) + if subrp.isincfile(): yield subrp + elif subrp.isdir(): + for subsubrp in self.get_all_increments(subrp): + yield subsubrp + + def testRemoveOlderThan(self): + """Test --remove-older-than. Uses restoretest3""" + Myrm("testfiles/output") + assert not os.system("cp -a testfiles/restoretest3 testfiles/output") + self.set_connections(None, None, None, None) + self.exec_rb_extra_args(None, "--remove-older-than 20000", + "testfiles/output") + rbdir = rpath.RPath(Globals.local_connection, + "testfiles/output/rdiff-backup-data") + for inc in self.get_all_increments(rbdir): + assert inc.getinctime() >= 20000 + + def testRemoveOlderThan2(self): + """Test --remove-older-than, but '1B'. Uses restoretest3""" + Myrm("testfiles/output") + assert not os.system("cp -a testfiles/restoretest3 testfiles/output") + self.set_connections(None, None, None, None) + self.exec_rb_extra_args(None, "--remove-older-than 1B --force", + "testfiles/output") + rbdir = rpath.RPath(Globals.local_connection, + "testfiles/output/rdiff-backup-data") + for inc in self.get_all_increments(rbdir): + assert inc.getinctime() >= 30000 + class FinalSelection(PathSetter): """Test selection options""" diff --git a/rdiff-backup/testing/restoretest.py b/rdiff-backup/testing/restoretest.py index b401643..5f939f9 100644 --- a/rdiff-backup/testing/restoretest.py +++ b/rdiff-backup/testing/restoretest.py @@ -51,6 +51,26 @@ class RestoreFileComparer: for t in self.time_rp_dict.keys(): self.compare_at_time(t) +class RestoreTimeTest(unittest.TestCase): + def test_time_from_session(self): + """Test getting time from session number (as in Time.time_from_session) + + Test here instead of in timetest because it depends on an + rdiff-backup-data directory already being laid out. + + """ + restore._mirror_time = None # Reset + Globals.rbdir = rpath.RPath(lc, + "testfiles/restoretest3/rdiff-backup-data") + assert Time.genstrtotime("0B") == Time.time_from_session(0) + assert Time.genstrtotime("2B") == Time.time_from_session(2) + assert Time.genstrtotime("23B") == Time.time_from_session(23) + + assert Time.time_from_session(0) == 40000, Time.time_from_session(0) + assert Time.time_from_session(2) == 20000, Time.time_from_session(2) + assert Time.time_from_session(5) == 10000, Time.time_from_session(5) + + class RestoreTest(unittest.TestCase): """Test Restore class""" def get_rfcs(self): -- cgit v1.2.1