Commit 76141dac authored by bescoto's avatar bescoto

Added --list-increment-sizes option


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@336 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent 98201e67
New in v0.13.0 (2003/07/02)
---------------------------
To prevent the buildup of confusing and error-prone options, the
capabilities of the source and destination file systems are now
autodetected. Detected features include allowed characters, extended
attributes, access control lists, hard links, ownership, and directory
fsyncing. Options such as --windows-mode, --chars-to-quote,
--quoting-char, and --windows-restore-mode have been removed.
Now rdiff-backup supports extended attributes. To take advantage of
this you will need the python module pyxattr and a file system that
supports EAs. Thanks to Greg Freemyer for discussion.
Added --list-increment-sizes switch, which tells you how much space
the various backup files take up. (Suggested by Andrew Bressen)
New in v0.12.0 (2003/06/26) New in v0.12.0 (2003/06/26)
--------------------------- ---------------------------
......
...@@ -11,7 +11,8 @@ rdiff-backup \- local/remote mirror and incremental backup ...@@ -11,7 +11,8 @@ rdiff-backup \- local/remote mirror and incremental backup
.B {{ -l | --list-increments } .B {{ -l | --list-increments }
.BI "| --remove-older-than " time_interval .BI "| --remove-older-than " time_interval
.BI "| --list-at-time " time .BI "| --list-at-time " time
.BI "| --list-changed-since " time } .BI "| --list-changed-since " time
.B "| --list-increment-sizes "}
.BI [[[ user@ ] host2.foo ]:: destination_directory ] .BI [[[ user@ ] host2.foo ]:: destination_directory ]
.B rdiff-backup --calculate-average .B rdiff-backup --calculate-average
...@@ -186,6 +187,13 @@ List the number and date of partial incremental backups contained in ...@@ -186,6 +187,13 @@ List the number and date of partial incremental backups contained in
the specified destination directory. No backup or restore will take the specified destination directory. No backup or restore will take
place if this option is given. place if this option is given.
.TP .TP
.B --list-increment-sizes
List the total size of all the increment and mirror files by time.
This may be helpful in deciding how many increments to keep, and when
to --remove-older-than. Specifying a subdirectory is allowable; then
only the sizes of the mirror and increments pertaining to that
subdirectory will be listed.
.TP
.B "-m, --mirror-only" .B "-m, --mirror-only"
Do not create an rdiff-backup-data directory or make any increments. Do not create an rdiff-backup-data directory or make any increments.
In this mode rdiff-backup is similar to rsync (but usually In this mode rdiff-backup is similar to rsync (but usually
......
...@@ -55,9 +55,9 @@ def parse_cmdlineoptions(arglist): ...@@ -55,9 +55,9 @@ def parse_cmdlineoptions(arglist):
"include-filelist=", "include-filelist-stdin", "include-filelist=", "include-filelist-stdin",
"include-globbing-filelist=", "include-regexp=", "include-globbing-filelist=", "include-regexp=",
"list-at-time=", "list-changed-since=", "list-increments", "list-at-time=", "list-changed-since=", "list-increments",
"no-compare-inode", "no-compression", "list-increment-sizes", "no-compare-inode",
"no-compression-regexp=", "no-file-statistics", "no-compression", "no-compression-regexp=",
"no-hard-links", "null-separator", "no-file-statistics", "no-hard-links", "null-separator",
"override-chars-to-quote=", "parsable-output", "override-chars-to-quote=", "parsable-output",
"print-statistics", "remote-cmd=", "remote-schema=", "print-statistics", "remote-cmd=", "remote-schema=",
"remove-older-than=", "restore-as-of=", "restrict=", "remove-older-than=", "restore-as-of=", "restrict=",
...@@ -105,6 +105,7 @@ def parse_cmdlineoptions(arglist): ...@@ -105,6 +105,7 @@ def parse_cmdlineoptions(arglist):
restore_timestr, action = arg, "list-changed-since" restore_timestr, action = arg, "list-changed-since"
elif opt == "-l" or opt == "--list-increments": elif opt == "-l" or opt == "--list-increments":
action = "list-increments" action = "list-increments"
elif opt == '--list-increment-sizes': action = 'list-increment-sizes'
elif opt == "--no-compare-inode": Globals.set("compare_inode", 0) elif opt == "--no-compare-inode": Globals.set("compare_inode", 0)
elif opt == "--no-compression": Globals.set("compression", None) elif opt == "--no-compression": Globals.set("compression", None)
elif opt == "--no-compression-regexp": elif opt == "--no-compression-regexp":
...@@ -147,9 +148,9 @@ def check_action(): ...@@ -147,9 +148,9 @@ def check_action():
"""Check to make sure action is compatible with args""" """Check to make sure action is compatible with args"""
global action global action
arg_action_dict = {0: ['server'], arg_action_dict = {0: ['server'],
1: ['list-increments', 'remove-older-than', 1: ['list-increments', 'list-increment-sizes',
'list-at-time', 'list-changed-since', 'remove-older-than', 'list-at-time',
'check-destination-dir'], 'list-changed-since', 'check-destination-dir'],
2: ['backup', 'restore', 'restore-as-of']} 2: ['backup', 'restore', 'restore-as-of']}
l = len(args) l = len(args)
if not action: assert l == 2, args # cannot tell backup or restore yet if not action: assert l == 2, args # cannot tell backup or restore yet
...@@ -163,7 +164,7 @@ def final_set_action(rps): ...@@ -163,7 +164,7 @@ def final_set_action(rps):
global action global action
if action: return if action: return
assert len(rps) == 2, rps assert len(rps) == 2, rps
if restore_get_root(rps[0]): action = "restore" if restore_set_root(rps[0]): action = "restore"
else: action = "backup" else: action = "backup"
def commandline_error(message): def commandline_error(message):
...@@ -194,6 +195,7 @@ def take_action(rps): ...@@ -194,6 +195,7 @@ def take_action(rps):
elif action == "list-at-time": ListAtTime(rps[0]) elif action == "list-at-time": ListAtTime(rps[0])
elif action == "list-changed-since": ListChangedSince(rps[0]) elif action == "list-changed-since": ListChangedSince(rps[0])
elif action == "list-increments": ListIncrements(rps[0]) elif action == "list-increments": ListIncrements(rps[0])
elif action == 'list-increment-sizes': ListIncrementSizes(rps[0])
elif action == "remove-older-than": RemoveOlderThan(rps[0]) elif action == "remove-older-than": RemoveOlderThan(rps[0])
elif action == "calculate-average": CalculateAverage(rps) elif action == "calculate-average": CalculateAverage(rps)
elif action == "check-destination-dir": CheckDest(rps[0]) elif action == "check-destination-dir": CheckDest(rps[0])
...@@ -373,7 +375,7 @@ def Restore(src_rp, dest_rp, restore_as_of = None): ...@@ -373,7 +375,7 @@ def Restore(src_rp, dest_rp, restore_as_of = None):
mirror file), dest_rp should be the target rp to be written. mirror file), dest_rp should be the target rp to be written.
""" """
if not restore_root_set: assert restore_get_root(src_rp) if not restore_root_set: assert restore_set_root(src_rp)
restore_check_paths(src_rp, dest_rp, restore_as_of) restore_check_paths(src_rp, dest_rp, restore_as_of)
restore_set_fs_globals(Globals.rbdir) restore_set_fs_globals(Globals.rbdir)
src_rp = restore_init_quoting(src_rp) src_rp = restore_init_quoting(src_rp)
...@@ -449,7 +451,7 @@ def restore_check_paths(rpin, rpout, restoreasof = None): ...@@ -449,7 +451,7 @@ def restore_check_paths(rpin, rpout, restoreasof = None):
Log.FatalError("Restore target %s already exists, " Log.FatalError("Restore target %s already exists, "
"specify --force to overwrite." % rpout.path) "specify --force to overwrite." % rpout.path)
def restore_check_backup_dir(mirror_root, src_rp, restore_as_of): def restore_check_backup_dir(mirror_root, src_rp = None, restore_as_of = 1):
"""Make sure backup dir root rpin is in consistent state""" """Make sure backup dir root rpin is in consistent state"""
if not restore_as_of and not src_rp.isincfile(): if not restore_as_of and not src_rp.isincfile():
Log.FatalError("""File %s does not look like an increment file. Log.FatalError("""File %s does not look like an increment file.
...@@ -466,7 +468,7 @@ Try restoring from an increment file (the filenames look like ...@@ -466,7 +468,7 @@ Try restoring from an increment file (the filenames look like
"rdiff-with --check-destination-dir option to revert directory " "rdiff-with --check-destination-dir option to revert directory "
"to state before unsuccessful session." % (mirror_root.path,)) "to state before unsuccessful session." % (mirror_root.path,))
def restore_get_root(rpin): def restore_set_root(rpin):
"""Set data dir, restore_root and index, or return None if fail """Set data dir, restore_root and index, or return None if fail
The idea here is to keep backing up on the path until we find The idea here is to keep backing up on the path until we find
...@@ -480,7 +482,7 @@ def restore_get_root(rpin): ...@@ -480,7 +482,7 @@ def restore_get_root(rpin):
funny way, using symlinks or somesuch. funny way, using symlinks or somesuch.
""" """
global restore_root, restore_index global restore_root, restore_index, restore_root_set
if rpin.isincfile(): relpath = rpin.getincbase().path if rpin.isincfile(): relpath = rpin.getincbase().path
else: relpath = rpin.path else: relpath = rpin.path
pathcomps = os.path.join(rpin.conn.os.getcwd(), relpath).split("/") pathcomps = os.path.join(rpin.conn.os.getcwd(), relpath).split("/")
...@@ -510,15 +512,16 @@ def restore_get_root(rpin): ...@@ -510,15 +512,16 @@ def restore_get_root(rpin):
(len(from_datadir) == 2 and (len(from_datadir) == 2 and
from_datadir[1].startswith('increments'))), from_datadir from_datadir[1].startswith('increments'))), from_datadir
restore_index = from_datadir[2:] restore_index = from_datadir[2:]
restore_root_set = 1
return 1 return 1
def ListIncrements(rp): def ListIncrements(rp):
"""Print out a summary of the increments and their times""" """Print out a summary of the increments and their times"""
mirror_root, index = restore_get_root(rp) assert restore_set_root(rp)
restore_check_backup_dir(mirror_root) restore_check_backup_dir(restore_root)
mirror_rp = mirror_root.new_index(index) mirror_rp = restore_root.new_index(restore_index)
inc_rpath = Globals.rbdir.append_path('increments', index) inc_rpath = Globals.rbdir.append_path('increments', restore_index)
incs = restore.get_inclist(inc_rpath) incs = restore.get_inclist(inc_rpath)
mirror_time = restore.MirrorStruct.get_mirror_time() mirror_time = restore.MirrorStruct.get_mirror_time()
if Globals.parsable_output: if Globals.parsable_output:
...@@ -526,6 +529,13 @@ def ListIncrements(rp): ...@@ -526,6 +529,13 @@ def ListIncrements(rp):
else: print manage.describe_incs_human(incs, mirror_time, mirror_rp) else: print manage.describe_incs_human(incs, mirror_time, mirror_rp)
def ListIncrementSizes(rp):
"""Print out a summary of the increments """
assert restore_set_root(rp)
restore_check_backup_dir(restore_root)
print manage.ListIncrementSizes(restore_root, restore_index)
def CalculateAverage(rps): def CalculateAverage(rps):
"""Print out the average of the given statistics files""" """Print out the average of the given statistics files"""
statobjs = map(lambda rp: StatsObj().read_stats_from_rp(rp), rps) statobjs = map(lambda rp: StatsObj().read_stats_from_rp(rp), rps)
...@@ -575,10 +585,10 @@ def ListChangedSince(rp): ...@@ -575,10 +585,10 @@ def ListChangedSince(rp):
"""List all the files under rp that have changed since restoretime""" """List all the files under rp that have changed since restoretime"""
try: rest_time = Time.genstrtotime(restore_timestr) try: rest_time = Time.genstrtotime(restore_timestr)
except Time.TimeException, exc: Log.FatalError(str(exc)) except Time.TimeException, exc: Log.FatalError(str(exc))
mirror_root, index = restore_get_root(rp) assert restore_set_root(rp)
restore_check_backup_dir(mirror_root) restore_check_backup_dir(restore_root)
mirror_rp = mirror_root.new_index(index) mirror_rp = restore_root.new_index(restore_index)
inc_rp = mirror_rp.append_path("increments", index) inc_rp = mirror_rp.append_path("increments", restore_index)
restore.ListChangedSince(mirror_rp, inc_rp, rest_time) restore.ListChangedSince(mirror_rp, inc_rp, rest_time)
...@@ -586,10 +596,10 @@ def ListAtTime(rp): ...@@ -586,10 +596,10 @@ def ListAtTime(rp):
"""List files in archive under rp that are present at restoretime""" """List files in archive under rp that are present at restoretime"""
try: rest_time = Time.genstrtotime(restore_timestr) try: rest_time = Time.genstrtotime(restore_timestr)
except Time.TimeException, exc: Log.FatalError(str(exc)) except Time.TimeException, exc: Log.FatalError(str(exc))
mirror_root, index = restore_get_root(rp) assert restore_set_root(rp)
restore_check_backup_dir(mirror_root) restore_check_backup_dir(restore_root)
mirror_rp = mirror_root.new_index(index) mirror_rp = restore_root.new_index(restore_index)
inc_rp = mirror_rp.append_path("increments", index) inc_rp = mirror_rp.append_path("increments", restore_index)
restore.ListAtTime(mirror_rp, inc_rp, rest_time) restore.ListAtTime(mirror_rp, inc_rp, rest_time)
......
...@@ -76,7 +76,7 @@ def set_security_level(action, cmdpairs): ...@@ -76,7 +76,7 @@ def set_security_level(action, cmdpairs):
rdir = tempfile.gettempdir() rdir = tempfile.gettempdir()
elif islocal(cp1): elif islocal(cp1):
sec_level = "read-only" sec_level = "read-only"
Main.restore_get_root(rpath.RPath(Globals.local_connection, Main.restore_set_root(rpath.RPath(Globals.local_connection,
getpath(cp1))) getpath(cp1)))
rdir = Main.restore_root.path rdir = Main.restore_root.path
else: else:
...@@ -94,9 +94,9 @@ def set_security_level(action, cmdpairs): ...@@ -94,9 +94,9 @@ def set_security_level(action, cmdpairs):
assert islocal(cp2) assert islocal(cp2)
sec_level = "all" sec_level = "all"
rdir = getpath(cp2) rdir = getpath(cp2)
elif (action == "test-server" or action == "list-increments" or elif action in ["test-server", "list-increments", 'list-increment-sizes',
action == "list-at-time" or action == "list-changed-since" "list-at-time", "list-changed-since",
or action == "calculate-average" or action == "remove-older-than"): "calculate-average", "remove-older-than"]:
sec_level = "minimal" sec_level = "minimal"
rdir = tempfile.gettempdir() rdir = tempfile.gettempdir()
else: assert 0, "Unknown action %s" % action else: assert 0, "Unknown action %s" % action
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
from __future__ import generators from __future__ import generators
from log import Log from log import Log
import Globals, Time, static, manage import Globals, Time, static, manage, statistics, restore, selection
class ManageException(Exception): pass class ManageException(Exception): pass
...@@ -128,3 +128,77 @@ class IncObj: ...@@ -128,3 +128,77 @@ class IncObj:
s = ["Increment file %s" % self.incrp.path, s = ["Increment file %s" % self.incrp.path,
"Date: %s" % self.pretty_time()] "Date: %s" % self.pretty_time()]
return "\n".join(s) return "\n".join(s)
def ListIncrementSizes(mirror_root, index):
"""Return string summarizing the size of all the increments"""
stat_obj = statistics.StatsObj() # used for byte summary string
def get_total(rp_iter):
"""Return the total size of everything in rp_iter"""
total = 0
for rp in rp_iter: total += rp.getsize()
return total
def get_time_dict(inc_iter):
"""Return dictionary pairing times to total size of incs"""
time_dict = {}
for inc in inc_iter:
if not inc.isincfile(): continue
t = inc.getinctime()
if not time_dict.has_key(t): time_dict[t] = 0
time_dict[t] += inc.getsize()
return time_dict
def get_mirror_select():
"""Return iterator of mirror rpaths"""
mirror_base = mirror_root.new_index(index)
mirror_select = selection.Select(mirror_base)
if not index: # must exclude rdiff-backup-directory
mirror_select.parse_rbdir_exclude()
return mirror_select.set_iter()
def get_inc_select():
"""Return iterator of increment rpaths"""
inc_base = Globals.rbdir.append_path('increments', index)
for base_inc in restore.get_inclist(inc_base): yield base_inc
if inc_base.isdir():
inc_select = selection.Select(inc_base).set_iter()
for inc in inc_select: yield inc
def get_summary_triples(mirror_total, time_dict):
"""Return list of triples (time, size, cumulative size)"""
triples = []
cur_mir_base = Globals.rbdir.append('current_mirror')
mirror_time = restore.get_inclist(cur_mir_base)[0].getinctime()
triples.append((mirror_time, mirror_total, mirror_total))
inc_times = time_dict.keys()
inc_times.sort()
inc_times.reverse()
cumulative_size = mirror_total
for inc_time in inc_times:
size = time_dict[inc_time]
cumulative_size += size
triples.append((inc_time, size, cumulative_size))
return triples
def triple_to_line(triple):
"""Convert triple to display string"""
time, size, cum_size = triple
return "%24s %13s %15s" % \
(Time.timetopretty(time),
stat_obj.get_byte_summary_string(size),
stat_obj.get_byte_summary_string(cum_size))
mirror_total = get_total(get_mirror_select())
time_dict = get_time_dict(get_inc_select())
triples = get_summary_triples(mirror_total, time_dict)
l = ['%12s %9s %15s %20s' % ('Time', '', 'Size', 'Cumulative size'),
'-' * 77,
triple_to_line(triples[0]) + ' (current mirror)']
for triple in triples[1:]: l.append(triple_to_line(triple))
return '\n'.join(l)
...@@ -296,6 +296,62 @@ class Final(PathSetter): ...@@ -296,6 +296,62 @@ class Final(PathSetter):
assert wc_output.split() == ["0", "0", "0"], wc_output assert wc_output.split() == ["0", "0", "0"], wc_output
class FinalMisc(PathSetter):
"""Test miscellaneous operations like list-increments, etc.
Many of these just run and make sure there were no errors; they
don't verify the output.
"""
def testListIncrementsLocal(self):
"""Test --list-increments switch. Assumes restoretest3 valid rd dir"""
self.set_connections(None, None, None, None)
self.exec_rb_extra_args(None, "--list-increments",
"testfiles/restoretest3")
def testListIncrementsRemote(self):
"""Test --list-increments mode remotely. Uses restoretest3"""
self.set_connections('test1', '../', None, None)
self.exec_rb_extra_args(None, "--list-increments",
"testfiles/restoretest3")
def testListChangeSinceLocal(self):
"""Test --list-changed-since mode locally. Uses restoretest3"""
self.set_connections(None, None, None, None)
self.exec_rb_extra_args(None, '--list-changed-since 10000',
'testfiles/restoretest3')
def testListChangeSinceRemote(self):
"""Test --list-changed-since mode remotely. Uses restoretest3"""
self.set_connections('test1', '../', None, None)
self.exec_rb_extra_args(None, '--list-changed-since 10000',
'testfiles/restoretest3')
def testListAtTimeLocal(self):
"""Test --list-at-time mode locally. Uses restoretest3"""
self.set_connections(None, None, None, None)
self.exec_rb_extra_args(None, '--list-at-time 20000',
'testfiles/restoretest3')
def testListAtTimeRemote(self):
"""Test --list-at-time mode locally. Uses restoretest3"""
self.set_connections('test1', '../', None, None)
self.exec_rb_extra_args(None, '--list-at-time 20000',
'testfiles/restoretest3')
def testListIncrementSizesLocal(self):
"""Test --list-increment-sizes switch. Uses restoretest3"""
self.set_connections(None, None, None, None)
self.exec_rb_extra_args(None, "--list-increment-sizes",
"testfiles/restoretest3")
def testListIncrementsRemote(self):
"""Test --list-increment-sizes mode remotely. Uses restoretest3"""
self.set_connections('test1', '../', None, None)
self.exec_rb_extra_args(None, "--list-increment-sizes",
"testfiles/restoretest3")
class FinalSelection(PathSetter): class FinalSelection(PathSetter):
"""Test selection options""" """Test selection options"""
def run(self, cmd): def run(self, cmd):
......
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