diff options
author | Ian Campbell <ijc@hellion.org.uk> | 2014-11-16 11:24:11 +0000 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2014-12-06 19:02:39 +0200 |
commit | da263719bc13e65b8f5948e657a4b2555523d509 (patch) | |
tree | 39f2ac63d12cb15ca9a7b18110a31f98d15649c5 /obnamlib/plugins/show_plugin.py | |
parent | ce0e45fece3dcec7faace976b30d99314f2d4c15 (diff) | |
download | obnam-da263719bc13e65b8f5948e657a4b2555523d509.tar.gz |
Implement support for listing a generation in kdirstat.cache format
This implements an ls variant whose output format is compatible with the
kdirstat cache format. The best reference I can find for this is from an old fork:
https://github.com/thomas-joiner/k4dirstat/blob/master/kdirstat/cache-file-format.txt
Recent versions of k4dirstat appear to be able to read the files produced just
fine, although this document is not present in their source AFAICT.
I find k4dirstat to be an excellent way to visualise what is included in a
backup, e.g. for the purposes of excluding file types or marking directories
with a CACHEDIR.TAG. My previous solution involved post-processing the output
of ls which require a ssh to the machine with the files (for stat() purposes).
This approach pulls all of the data out of the obnam repo, so is much faster,
less hacky and gets the status of the actual generation and not the current
files etc.
Diffstat (limited to 'obnamlib/plugins/show_plugin.py')
-rw-r--r-- | obnamlib/plugins/show_plugin.py | 69 |
1 files changed, 57 insertions, 12 deletions
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. |