summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--manual/en/120-misc.mdwn16
-rw-r--r--obnam.1.in7
-rw-r--r--obnamlib/plugins/show_plugin.py69
3 files changed, 80 insertions, 12 deletions
diff --git a/manual/en/120-misc.mdwn b/manual/en/120-misc.mdwn
index 0ed6c7fd..d80e8231 100644
--- a/manual/en/120-misc.mdwn
+++ b/manual/en/120-misc.mdwn
@@ -3,3 +3,19 @@ Other stuff
This chapter discusses topics that do not warrant a chapter of their
own, such as compressing backups and running Obnam from cron.
+
+k4dirstat cache files
+---------------------
+
+[k4dirstat] is a utility for visualising the disk space used by a
+directory tree. Obnam's `kdirstat` command can be used to produce a
+listing of the contents of a generation in a format which can be read
+by k4dirstat using `File`, `Read Cache File` from the k4dirstat
+menu. e.g.
+
+ $ obnam kdirstat --client CLIENT --generation GENID > CLIENT.kdirstat.cache
+ $ gzip -v9 CLIENT.kdirstat.cache # OPTIONAL
+
+`CLIENT.kdirstat.cache[.gz]` can now be read by `k4dirstat`.
+
+[k4dirstat]: https://bitbucket.org/jeromerobert/k4dirstat/wiki/Home
diff --git a/obnam.1.in b/obnam.1.in
index 50b0092a..c0c7185e 100644
--- a/obnam.1.in
+++ b/obnam.1.in
@@ -68,6 +68,13 @@ This can be useful for scripting.
lists the contents of a given generation, similar to
.BR "ls \-lAR" .
.IP \(bu
+.B kdirstat
+lists the contents of a given generation, in a format which is
+compatible with the
+.BR "kdirstat"
+cache file format, which can then be used to visualise the contents
+of a backup.
+.IP \(bu
.B verify
compares data in the backup with actual user data,
and makes sure they are identical.
diff --git a/obnamlib/plugins/show_plugin.py b/obnamlib/plugins/show_plugin.py
index 7c933cfb..949b2143 100644
--- a/obnamlib/plugins/show_plugin.py
+++ b/obnamlib/plugins/show_plugin.py
@@ -55,6 +55,7 @@ class ShowPlugin(obnamlib.ObnamPlugin):
self.app.add_subcommand('generations', self.generations)
self.app.add_subcommand('genids', self.genids)
self.app.add_subcommand('ls', self.ls, arg_synopsis='[FILE]...')
+ self.app.add_subcommand('kdirstat', self.kdirstat, arg_synopsis='[FILE]...')
self.app.add_subcommand('diff', self.diff,
arg_synopsis='[GENERATION1] GENERATION2')
self.app.add_subcommand('nagios-last-backup-age',
@@ -179,8 +180,8 @@ class ShowPlugin(obnamlib.ObnamPlugin):
sys.stdout.write('%s\n' % self.repo.make_generation_spec(gen_id))
self.repo.close()
- def ls(self, args):
- '''List contents of a generation.'''
+ def traverse(self, hdr, cb, args):
+ '''Traverse a generation calling callback.'''
self.open_repository()
@@ -196,12 +197,11 @@ class ShowPlugin(obnamlib.ObnamPlugin):
gen_id, obnamlib.REPO_GENERATION_ENDED)
started = self.format_time(started)
ended = self.format_time(ended)
- self.app.output.write(
- 'Generation %s (%s - %s)\n' %
+ hdr('Generation %s (%s - %s)\n' %
(self.repo.make_generation_spec(gen_id), started, ended))
- for ls_file in args:
- ls_file = self.remove_trailing_slashes(ls_file)
- self.show_objects(gen_id, ls_file)
+ for file in args:
+ file = self.remove_trailing_slashes(file)
+ self.show_objects(cb, gen_id, file)
self.repo.close()
@@ -218,19 +218,26 @@ class ShowPlugin(obnamlib.ObnamPlugin):
gen_id, filename, obnamlib.REPO_FILE_MODE)
return stat.S_ISDIR(mode)
- def show_objects(self, gen_id, dirname):
- self.show_item(gen_id, dirname)
+ def show_objects(self, cb, gen_id, dirname):
+ cb(gen_id, dirname)
subdirs = []
for filename in sorted(self.repo.get_file_children(gen_id, dirname)):
if self.isdir(gen_id, filename):
subdirs.append(filename)
else:
- self.show_item(gen_id, filename)
+ cb(gen_id, filename)
for subdir in subdirs:
- self.show_objects(gen_id, subdir)
+ self.show_objects(cb, gen_id, subdir)
+
+ def ls(self, args):
+ '''List contents of a generation.'''
+ self.traverse(self.show_hdr_ls, self.show_item_ls, args)
- def show_item(self, gen_id, filename):
+ def show_hdr_ls(self, comment):
+ self.app.output.write(comment)
+
+ def show_item_ls(self, gen_id, filename):
fields = self.fields(gen_id, filename)
widths = [
1, # mode
@@ -251,6 +258,44 @@ class ShowPlugin(obnamlib.ObnamPlugin):
result.append(fmt % (abs(widths[i]), fields[i]))
self.app.output.write('%s\n' % ' '.join(result))
+ def kdirstat(self, args):
+ '''List contents of a generation in kdirstat cache format.'''
+ self.traverse(self.show_hdr_kdirstat, self.show_item_kdirstat, args)
+
+ def show_hdr_kdirstat(self, comment):
+ self.app.output.write('''
+[kdirstat 4.0 cache file]
+# Generated by obnam %s
+# Do not edit!
+#
+# Type path size mtime <optional fields>
+
+''' % comment)
+
+ def show_item_kdirstat(self, gen_id, filename):
+ mode = self.repo.get_file_key(
+ gen_id, filename, obnamlib.REPO_FILE_MODE)
+ size = self.repo.get_file_key(
+ gen_id, filename, obnamlib.REPO_FILE_SIZE)
+ mtime_sec = self.repo.get_file_key(
+ gen_id, filename, obnamlib.REPO_FILE_MTIME_SEC)
+
+ if stat.S_ISREG(mode): mode_str = "F\t"
+ elif stat.S_ISDIR(mode): mode_str = "D "
+ elif stat.S_ISLNK(mode): mode_str = "L\t"
+ elif stat.S_ISBLK(mode): mode_str = "BlockDev\t"
+ elif stat.S_ISCHR(mode): mode_str = "CharDev\t"
+ elif stat.S_ISSOCK(mode): mode_str = "Socket\t"
+
+ enc_filename = filename.replace("%", "%25")
+ enc_filename = enc_filename.replace(" ", "%20")
+ enc_filename = enc_filename.replace("\t", "%09")
+
+ if (filename == "/"): return
+
+ self.app.output.write("%s%s\t%d\t%#x\n" %
+ (mode_str, enc_filename, size, mtime_sec))
+
def show_diff_for_file(self, gen_id, fullname, change_char):
'''Show what has changed for a single file.