diff options
-rw-r--r-- | manual/en/120-misc.mdwn | 16 | ||||
-rw-r--r-- | obnam.1.in | 7 | ||||
-rw-r--r-- | obnamlib/plugins/show_plugin.py | 69 |
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 @@ -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. |