public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* master - build: create contrib/ top level section
@ 2008-08-20 19:26 Andrew Price
  0 siblings, 0 replies; only message in thread
From: Andrew Price @ 2008-08-20 19:26 UTC (permalink / raw)
  To: cluster-cvs-relay

Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=139fb344064623f0edd93ffbf1897915cbb13fcb
Commit:        139fb344064623f0edd93ffbf1897915cbb13fcb
Parent:        d5d8e39eef7802a7d314cafe112deee0133a8227
Author:        Fabio M. Di Nitto <fdinitto@redhat.com>
AuthorDate:    Wed Aug 20 10:04:30 2008 +0200
Committer:     Fabio M. Di Nitto <fdinitto@redhat.com>
CommitterDate: Wed Aug 20 12:28:31 2008 +0200

build: create contrib/ top level section

now that the project is more open to the community, create
a top level contrib section where to add community contribution.

change build system to cope with it and make it disabled by default.

add option --enable_contrib to top level configure

move askant into contrib/

Signed-off-by: Fabio M. Di Nitto <fdinitto@redhat.com>
---
 Makefile                                   |   10 +-
 askant/INSTALL                             |   42 ---
 askant/PLUGINAPI                           |   65 -----
 askant/README                              |   74 -----
 askant/askant/about.py                     |    5 -
 askant/askant/askant.py                    |   24 --
 askant/askant/blktrace.py                  |   93 -------
 askant/askant/commands.py                  |  333 -----------------------
 askant/askant/sysfs.py                     |   86 ------
 askant/fsplugins/gfs2/gfs2.c               |  405 ----------------------------
 askant/fsplugins/gfs2/gfs2.h               |    3 -
 askant/fsplugins/gfs2/gfs2module.c         |  104 -------
 askant/scripts/askant                      |    6 -
 askant/setup.py                            |   18 --
 configure                                  |    7 +
 contrib/askant/INSTALL                     |   42 +++
 contrib/askant/PLUGINAPI                   |   65 +++++
 contrib/askant/README                      |   74 +++++
 contrib/askant/askant/about.py             |    5 +
 contrib/askant/askant/askant.py            |   24 ++
 contrib/askant/askant/blktrace.py          |   93 +++++++
 contrib/askant/askant/commands.py          |  333 +++++++++++++++++++++++
 contrib/askant/askant/sysfs.py             |   86 ++++++
 contrib/askant/fsplugins/gfs2/gfs2.c       |  405 ++++++++++++++++++++++++++++
 contrib/askant/fsplugins/gfs2/gfs2.h       |    3 +
 contrib/askant/fsplugins/gfs2/gfs2module.c |  104 +++++++
 contrib/askant/scripts/askant              |    6 +
 contrib/askant/setup.py                    |   18 ++
 make/defines.mk.input                      |    1 +
 29 files changed, 1274 insertions(+), 1260 deletions(-)

diff --git a/Makefile b/Makefile
index 30e3838..de16e70 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,8 @@ include make/defines.mk
 
 REALSUBDIRS = gnbd-kernel/src gfs-kernel/src/gfs \
 	      cman/lib config cman dlm fence/libfenced group \
-	      fence gfs gfs2 gnbd rgmanager bindings doc
+	      fence gfs gfs2 gnbd rgmanager bindings doc \
+	      contrib
 
 SUBDIRS = $(filter-out \
 	  $(if ${without_gnbd-kernel/src},gnbd-kernel/src) \
@@ -43,6 +44,7 @@ gfs2: group
 gnbd: cman
 rgmanager: cman dlm
 bindings: cman
+contrib: gfs2
 
 oldconfig:
 	@if [ -f $(OBJDIR)/.configure.sh ]; then \
@@ -58,7 +60,11 @@ uninstall:
 	set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done
 
 clean:
-	set -e && for i in ${REALSUBDIRS}; do legacy_code=1 ${MAKE} -C $$i $@; done
+	set -e && for i in ${REALSUBDIRS}; do \
+		contrib_code=1 \
+		legacy_code=1 \
+		${MAKE} -C $$i $@;\
+	done
 
 distclean: clean
 	rm -f make/defines.mk
diff --git a/askant/INSTALL b/askant/INSTALL
deleted file mode 100644
index ce26b82..0000000
--- a/askant/INSTALL
+++ /dev/null
@@ -1,42 +0,0 @@
-
-  How To Install Askant
-  ---------------------
-
-o Build Dependencies
-  - Python
-  - libgfs2 (part of the Red Hat cluster suite as a static lib)
-
-o Dependencies
-  - Python
-  - blktrace and blkparse with appropriate kernel config options enabled and
-    debugfs mounted
-
-o Intro
-  Askant uses Python's distutils for its standard installation procedure so the
-  setup.py script provides installation routines. Note that it doesn't provide
-  uninstallation routines so the best way to install it is to obtain a package
-  for your particular distribution and install that.
-
-o Installation
-  To install askant to the default location on your system, run as root:
-
-    ./setup.py install
-
-  If you wish to install it to a different root directory (e.g. /var/mychroot)
-  or prefix directory (e.g. /usr/local instead of /usr) see:
-  
-    ./setup.py install --help
-
-  To find out everything else setup.py can do:
-
-    ./setup.py --help-commands
-
-o Use Without Installing
-  To use askant without installing it (e.g. if you want to test patches
-  quickly) it is necessary to carry out the build stage to compile the required
-  plugins. Do:
-
-    ./setup.py build
-    cd build/lib*
-    python askant/askant.py
-
diff --git a/askant/PLUGINAPI b/askant/PLUGINAPI
deleted file mode 100644
index 4c2eceb..0000000
--- a/askant/PLUGINAPI
+++ /dev/null
@@ -1,65 +0,0 @@
-
-  Writing File System Plugins for Askant
-  --------------------------------------
-
-0. Contents
-
-  1. Intro
-  2. API
-  3. Reference
-
-
-1. Intro
-
-In order to gather file system data with which to enhance the data provided by
-blktrace, there must be specific code written for each file system type we wish
-to support. This is where file system plugins come along. The plugins must
-basically traverse an on-disk file system structure on a block device and report
-back their findings through a callback handed to them by askant. They must also
-expose a function which stops their traversal loop in case askant wishes to stop
-their execution prematurely.
-
-
-2. API
-
-A file system plugin must be written as a Python module. This means that C, C++
-and Python are suitable languages to write them in. They must expose a number of
-Python functions to askant:
-
-parsefs(dev)
-    
-    This function is called when askant wants the plugin to parse the file
-    system on the device specified by the device, dev.
-
-get_block_size()
-
-    This function is called to ascertain the file system block size which is
-    required in order to map the disk sector numbers provided by blktrace to
-    file system block numbers. It takes no arguments and should return a Python
-    integer value.
-
-set_report_hook(func)
-
-    This function is called to pass in a report function which the plugin should
-    call to report the details of a file system block. func is a function which
-    accepts four arguments in this order:
-    
-    Block number: a Python long integer
-    Block type: a Python string
-    Block number of parent: a Python long integer
-    Block file name: a Python string (should be "" for non-inode blocks)
-
-handle_sigint()
-
-    This function should make the plugin stop its parsing loop. This allows it
-    to be interrupted if askant catches a signal, for example. It takes no
-    arguments and its return value is not used.
-
-
-3. Reference
-
-o See fsplugins/gfs2/gfs2module.c for an example of writing a Python fs plugin
-  module in C.
-
-o http://www.python.org/doc/ext/intro.html shows how to extend Python with C or
-  C++.
diff --git a/askant/README b/askant/README
deleted file mode 100644
index 74e3219..0000000
--- a/askant/README
+++ /dev/null
@@ -1,74 +0,0 @@
-
- Askant - A Block I/O Analysis Tool
- ----------------------------------
-
-0. Contents
-
-  1. Intro
-  2. Commands and their options
-  3. Examples
-
-
-1. Intro
-
-Askant allows you to gather file system block I/O data for performance analysis.
-It uses blktrace to trace block operations and adds file system specific data by
-parsing fs block information straight from the storage device. To do this it
-invokes functionality from file system parser plugins which traverse the on-disk
-structures and report details of the blocks they encounter through callbacks.
-
-Usage: askant <command> [options...]
-
-o See 'askant --commands' for a full list of commands.
-
-
-2. Commands and their options
-
-  dumpfs
-
-    Dumps the block data provided by an fs plugin from the given file system.
-
-    -d  The block device which contains the file system under scrutiny
-    -t  The type of the file system, i.e. the name of the fs plugin to load
-    -o  Output file (optional). Stdout is used if '-' or omitted.
-        (Note that the output file should be on a different device to the one
-	specified with -d.)
-  
-  unlinks
-
-    Does a dumpfs and runs blktrace until interrupted. It then blkparses the
-    blktrace data and adds the block data. This is suitable for tracing I/O
-    operations during an unlink test run.
-
-    -d  The block device which contains the file system under scrutiny
-    -t  The type of the file system, i.e. the name of the fs plugin to load
-    -g  The path to the debugfs mount point, defaults to /sys/kernel/debug
-    -D  The directory in which to store the block dumps and blktrace files.
-
-  creates
-
-    Runs blktrace until interrupted. It then does a dumpfs and blkparses the
-    blktrace data and adds the block data. This is suitable for tracing I/O
-    operations during a file creation test run.
-
-    -d  The block device which contains the file system under scrutiny
-    -t  The type of the file system, i.e. the name of the fs plugin to load
-    -g  The path to the debugfs mount point, defaults to /sys/kernel/debug
-    -D  The directory in which to store the block dumps and blktrace files.
-
-3. Examples
-
-o Parse the file system and dump information about each block to stdout e.g.:
-
-    # askant dumpfs -t gfs2 -d /dev/sda3
-
-o Trace 'unlinks' by dumping block information before the blktrace and matching
-  block data to provide a more detailed blkparse output e.g.:
-
-    # askant unlinks -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
-
-o Trace 'creates' by dumping block information after the blktrace and matching
-  block data to provide a more detailed blkparse output e.g.:
-
-    # askant creates -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
-
diff --git a/askant/askant/__init__.py b/askant/askant/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/askant/askant/about.py b/askant/askant/about.py
deleted file mode 100644
index 3d40a74..0000000
--- a/askant/askant/about.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-Version information for askant.
-"""
-
-version = "0.0.1"
diff --git a/askant/askant/askant.py b/askant/askant/askant.py
deleted file mode 100644
index a4376b6..0000000
--- a/askant/askant/askant.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-The main entry point for askant.
-"""
-
-import sys
-import commands
-
-def main():
-
-	"""Run askant"""
-
-	cmds = commands.Commands()
-	cmds.register_command(commands.DumpFSCommand())
-	cmds.register_command(commands.UnlinksCommand())
-	cmds.register_command(commands.CreatesCommand())
-
-	try:
-		cmds.process_argv(sys.argv)
-	except commands.NoFSPluginException, p:
-		print >>sys.stderr, "Plugin not found: %s" %p
-
-
-if __name__ == '__main__':
-	main()
diff --git a/askant/askant/blktrace.py b/askant/askant/blktrace.py
deleted file mode 100644
index fdf0219..0000000
--- a/askant/askant/blktrace.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-Wrapper classes for running blktrace and blkparse and using their output
-"""
-
-import os
-import sys
-import time
-import signal
-from subprocess import Popen
-from subprocess import PIPE
-from sysfs import Sysfs
-
-class BlktraceException(Exception):
-
-	def __init__(self, val, msg):
-		Exception.__init__(self)
-		self.val = val
-		self.msg = msg
-	
-	def __str__(self):
-		return self.msg
-
-class Blktrace:
-
-	def __init__(self, dev, debugfs='/sys/kernel/debug'):
-		self.dev = dev
-		self.debugfs = debugfs
-		self.sysfs = Sysfs(dev)
-		self.btpid = -1
-
-	def handle_sigint(self, sig, frame):
-		if self.btpid >= 0:
-			os.kill(self.btpid, signal.SIGTERM)
-
-	def trace(self, tracefile):
-
-		if not self.dev:
-			raise Exception("No device specified.")
-
-		btargs = ['blktrace',
-			'-d', self.dev,
-			'-r', self.debugfs,
-			'-o', tracefile]
-
-		blktrace = Popen(btargs, bufsize=1, stdout=PIPE,
-						stderr=open('/dev/null','w'))
-		self.btpid = blktrace.pid
-		btres = None
-		while btres is None:
-			time.sleep(1)
-			btres = blktrace.poll()
-
-		self.btpid = -1
-		if btres:
-			raise BlktraceException(btres,
-				'blktrace exited with code ' + str(btres))
-
-
-
-	def parse(self, tracefile, getblk):
-
-		if not self.dev:
-			raise Exception("No device specified.")
-
-		offset = self.sysfs.get_partition_start_sector()
-
-		bpargs = ['blkparse', '-i', tracefile]
-		blkparse = Popen(bpargs, bufsize=1, stdout=PIPE)
-
-		bpres = None
-		while bpres is None:
-			output = blkparse.stdout.readline().strip()
-			if output:
-				chunks = output.split()
-				try:
-					# chunks[7] is the sector number
-					blk = list(getblk(int(chunks[7])))
-					print "%s %s %s" %\
-						(' '.join(blk[0:3]),\
-						 output.strip(),
-						 blk[3].strip())
-				except KeyError:
-					pass
-				except ValueError:
-					pass
-
-			bpres = blkparse.poll()
-
-		if bpres:
-			raise BlktraceException(bpres,
-				'blkparse exited with code ' + str(bpres))
-
-
diff --git a/askant/askant/commands.py b/askant/askant/commands.py
deleted file mode 100644
index ad4639f..0000000
--- a/askant/askant/commands.py
+++ /dev/null
@@ -1,333 +0,0 @@
-"""Command line parsing and processing for askant"""
-
-import fs
-import os
-import sys
-import time
-import about
-import signal
-import tempfile
-from sysfs import Sysfs, SysfsException
-from blktrace import Blktrace, BlktraceException
-from optparse import OptionParser, make_option
-
-class NoFSPluginException(Exception):
-
-	"""A generic Exception class for when file system plugins are missing"""
-
-	pass # Nothing special about this exception
-
-class Commands:
-
-	"""Provides a convenient interface to the askant commands"""
-
-	def __init__(self):
-
-		self.commands = {}
-		self.parser = OptionParser(usage="%prog COMMAND [options]",
-				version=about.version)
-
-
-	def register_command(self, command):
-		self.commands[str(command)] = command
-	
-	def __lookup_command(self, command):
-		try:
-			return self.commands[command]
-		except KeyError, k:
-			return DefaultCommand()
-	
-	def __parse_command(self, command, argv):
-		self.parser.prog = "%s %s" %\
-			(self.parser.get_prog_name(), str(command))
-		self.parser.set_usage(command.get_usage())
-		optlst = command.get_options()
-		for o in optlst:
-			self.parser.add_option(o)
-		self.parser.set_description(command.get_help())
-
-		if str(command) != "":
-			argv = argv[1:]
-
-		return self.parser.parse_args(argv)
-
-	def process_argv(self, argv):
-		try:
-			command = self.__lookup_command(argv[1])
-		except IndexError, i:
-			# This exits
-			self.parser.error("No command specified. Try --help")
-
-		options, args = self.__parse_command(command, argv[1:])
-
-		command.do_command(options, args, self)
-
-class Command:
-	def __init__(self):
-		self.options = []
-
-class DefaultCommand(Command):
-	def __str__(self):
-		return ""
-
-	def get_options(self):
-		self.options.append(make_option("-c","--commands",
-			action="store_true",
-			help="Lists available commands"))
-		return self.options
-
-	def get_usage(self):
-		return "%prog COMMAND [options] [args]"
-
-	def get_help(self):
-		return "Askant is a tool for tracing I/O activity in Linux "\
-			"and adding extra file system context by "\
-			"parsing file system data directly."
-	
-	def do_command(self, options, args, base):
-		if options.commands:
-			base.parser.print_usage()
-			print "Available commands:"
-			keys = base.commands.keys()
-			keys.sort()
-			for k in keys:
-				print " %10s  %s" %\
-					(k, base.commands[k].get_help())
-			print ""
-			print "For command-specific help, use "\
-				"%sCOMMAND --help" % base.parser.get_prog_name()
-		else:
-			base.parser.error("Command not found. Try --commands")
-
-
-class FSCommandTemplate(Command):
-	def __init__(self):
-		self.fsmod = None
-		self.block_table = {}
-		self.block_func = self.__report_hook
-		self.sector_size = None
-		self.offset = None
-		self.options = [
-			make_option("-d","--device", metavar="DEVICE",
-				help="The device to analyse."),
-		]
-
-	def load_fs_plugin(self, fsname):
-		try:
-			self.fsmod = __import__("fs." + fsname,
-					globals(), locals(), ["fs"])
-		except ImportError, i:
-			raise NoFSPluginException(fsname)
-
-	def parse_fs(self, dev, hook=None):
-		if hook is None:
-			hook = self.block_func
-
-		sfs = Sysfs(dev)
-		offset = sfs.get_partition_start_sector()
-		sector_size = sfs.get_dev_sector_size()
-
-		self.fsmod.set_report_hook(hook)
-
-		try:
-	                sfs = Sysfs(dev)
-        	        self.offset = sfs.get_partition_start_sector()
-                	self.sector_size = sfs.get_dev_sector_size()
-			signal.signal(signal.SIGINT, self.fsmod.handle_sigint)
-			self.blk_size = self.fsmod.get_block_size(dev)
-			self.fsmod.parsefs(dev)
-			signal.signal(signal.SIGINT, signal.SIG_DFL)
-		except IOError, i:
-			print >>sys.stderr, str(i)
-			sys.exit(1)
-
-	def set_outfile(self, outfile):
-		self.outfile = outfile
-
-	def trace(self, options, dump_before):
-
-		sfs = Sysfs(options.device)
-		self.offset = sfs.get_partition_start_sector()
-		self.sector_size = sfs.get_dev_sector_size()
-
-		tmstamp = time.strftime('%Y%m%d%H%M%S')
-		self.block_func = self.__report_hook
-		try:
-			self.outfile = open(os.path.join(options.outdir, "blocks-%s" %
-				tmstamp), 'w')
-		except IOError, e:
-			print >>sys.stderr, str(e)
-			sys.exit(1)
-
-		if dump_before:
-			print >>sys.stderr, "Gathering block data..."
-			self.parse_fs(options.device)
-			self.outfile.close()
-
-		if options.debugfs:
-			bt = Blktrace(options.device, options.debugfs)
-		else:
-			bt = Blktrace(options.device)
-
-		os.chdir(options.outdir) # Blktrace doesn't use absolute paths
-		print >>sys.stderr, "Tracing. Hit Ctrl-C to end..."
-		try:
-			signal.signal(signal.SIGINT, bt.handle_sigint)
-			bt.trace(tmstamp)
-			signal.signal(signal.SIGINT, signal.SIG_DFL)
-		except BlktraceException, b:
-			signal.signal(signal.SIGINT, signal.SIG_DFL)
-			print >>sys.stderr, str(b)
-			sys.exit(1)
-
-		if not dump_before:
-			print >>sys.stderr, "Gathering block data..."
-			self.parse_fs(options.device)
-			self.outfile.close()
-
-		print >>sys.stderr, "Matching blocks..."
-		blockdb = open(self.outfile.name, 'r')
-		blocks = {}
-		for l in blockdb.readlines():
-			s = l.split('\t')
-			blocks[int(s[0])] = tuple(s[1:])
-
-		bt.parse(os.path.join(options.outdir, tmstamp), blocks.__getitem__)
-		blockdb.close()
-
-	def __report_hook(self, blk, type, parent, fn):
-		if not fn:
-			fn = ""
-		try:
-			self.outfile.write("%d\t%d\t%s\t%d\t\"%s\"\n" % (
-			((blk * self.blk_size)/self.sector_size) + self.offset,
-			blk, type, parent, fn))
-		except Exception, e:
-			print str(e)
-
-	def __dict_hook(self, blk, type, parent, fn):
-		self.block_table[
-		    ((blk * self.blk_size)/self.sector_size)+self.offset] =\
-		    (blk, type, parent, fn)
-
-
-class DumpFSCommand(FSCommandTemplate):
-	def __str__(self):
-		return "dumpfs"
-
-	def get_options(self):
-		self.options.append(make_option("-t","--type", metavar="FSTYPE",
-			help="The type of file system on the device."))
-		self.options.append(make_option("-o","--output", metavar="FILE",
-			default="-",
-			help="File to write output to or '-' for stdout "
-			"(default)"))
-		return self.options
-	
-	def get_usage(self):
-		return "%prog [options]"
-	
-	def get_help(self):
-		return "Dump fs block information in TSV format."
-	
-	def do_command(self, options, args, base):
-
-		if not options.device:
-			base.parser.error("No device specified. Use -d.")
-
-		if not options.type:
-			base.parser.error("No file system type specified. "
-								"Use -t.")
-		try:
-			if options.output != "-":
-				self.set_outfile(open(options.output, 'w'))
-			else:
-				self.set_outfile(sys.stdout)
-		except IOError, e:
-			print >>sys.stderr,\
-				"Unable to open file for writing: %s" %\
-				options.output
-			return
-		except KeyError:
-			pass
-
-		self.load_fs_plugin(options.type)
-		try:
-			self.parse_fs(options.device)
-		except SysfsException, s:
-			base.parser.error(s)
-		
-class UnlinksCommand(FSCommandTemplate):
-	def __str__(self):
-		return "unlinks"
-
-	def get_options(self):
-		self.options.append(make_option("-t","--type", metavar="FSTYPE",
-			help="The type of file system on the device."))
-		self.options.append(make_option("-g","--debugfs", metavar="PATH",
-			default="/sys/kernel/debug",
-			help="The path to the debugfs mountpoint. Default "
-			"/sys/kernel/debug"))
-		self.options.append(make_option("-D","--outdir", metavar="DIR",
-			help="Directory to write output to. Required."))
-		return self.options
-	
-	def get_usage(self):
-		return "%prog [options]"
-	
-	def get_help(self):
-		return "Trace block I/O activity during unlink tests."
-	
-	def do_command(self, options, args, base):
-		if not options.device:
-			base.parser.error("No device specified. Use -d.")
-
-		self.device = options.device
-
-		if not options.type:
-			base.parser.error("No file system type specified. "
-								"Use -t.")
-		if not options.outdir:
-			base.parser.error("No output directory specified. "
-								"Use -D.")
-
-		self.load_fs_plugin(options.type)
-		self.trace(options, dump_before=True)
-
-class CreatesCommand(FSCommandTemplate):
-	def __str__(self):
-		return "creates"
-
-	def get_options(self):
-		self.options.append(make_option("-t","--type", metavar="FSTYPE",
-			help="The type of file system on the device."))
-		self.options.append(make_option("-g","--debugfs", metavar="PATH",
-			default="/sys/kernel/debug",
-			help="The path to the debugfs mountpoint. Default "
-			"/sys/kernel/debug"))
-		self.options.append(make_option("-D","--outdir", metavar="DIR",
-			help="Directory to write output to. Required."))
-		return self.options
-	
-	def get_usage(self):
-		return "%prog [options]"
-	
-	def get_help(self):
-		return "Trace block I/O activity during create tests."
-	
-	def do_command(self, options, args, base):
-		if not options.device:
-			base.parser.error("No device specified. Use -d.")
-
-		self.device = options.device
-
-		if not options.type:
-			base.parser.error("No file system type specified. "
-								"Use -t.")
-		if not options.outdir:
-			base.parser.error("No output directory specified. "
-								"Use -D.")
-
-		self.load_fs_plugin(options.type)
-		self.trace(options, dump_before=False)
-
diff --git a/askant/askant/fs/__init__.py b/askant/askant/fs/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/askant/askant/sysfs.py b/askant/askant/sysfs.py
deleted file mode 100644
index 12b7a3b..0000000
--- a/askant/askant/sysfs.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-Provides access to sysfs data pertaining to storage partitions.
-"""
-
-import os
-import os.path
-
-class SysfsException(Exception):
-	"""
-	An exception which is raised when things go wrong with Sysfs.
-	"""
-	pass # No functionality to add over Exception class
-
-class Sysfs:
-	"""
-	Provides access to sysfs data pertaining to storage partitions.
-	"""
-	def __init__(self, partition, sysfs_path='/sys'):
-		"""
-		Instantiate a Sysfs object. partition should be a path to a
-		disc partition device e.g. /dev/sda3.
-		"""
-		self.partition = partition
-		self.device = self.__partition2parent()
-		self.sysfs_path = sysfs_path
-		line = self.__firstline(os.path.join(
-				sysfs_path,'block',
-				os.path.split(self.device)[1],
-				os.path.split(self.partition)[1],
-				'start'))
-		self.partition_start = int(line)
-		line = self.__firstline(os.path.join(
-				sysfs_path,'block',
-				os.path.split(self.device)[1],
-				'queue',
-				'hw_sector_size'))
-		self.dev_sector_size = int(line)
-		line = self.__firstline(os.path.join(
-				sysfs_path,'block',
-				os.path.split(self.device)[1],
-				os.path.split(self.partition)[1],
-				'size'))
-		self.partition_size = int(line)
-
-	def __firstline(self, fname):
-		"""
-		Read the first line of a file
-		"""
-		try:
-			fobj = open(fname, 'r')
-		except IOError:
-			raise SysfsException(
-				'Could not open file "%s" for reading. Please '
-				'check your kernel is 2.6.25 or later and '
-				'sysfs is mounted.' % fname)
-		line = fobj.readline()
-		fobj.close()
-		return line
-
-	def __partition2parent(self):
-		"""
-		Return the name of a partition device's parent device
-		e.g. /dev/sda3 -> /dev/sda
-		"""
-		# This may need thinking about a bit more -
-		# nothing in life can be this simple.
-		return self.partition.rstrip('0123456789')
-
-
-	def get_partition_start_sector(self):
-		"""
-		Look up the start sector of the partition.
-		"""
-		return self.partition_start
-	
-	def get_dev_sector_size(self):
-		"""
-		Look up the size of the device's sectors.
-		"""
-		return self.dev_sector_size
-	
-	def get_partition_size(self):
-		"""
-		Look up the size of the partition.
-		"""
-		return self.partition_size
diff --git a/askant/fsplugins/__init__.py b/askant/fsplugins/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/askant/fsplugins/gfs2/gfs2.c b/askant/fsplugins/gfs2/gfs2.c
deleted file mode 100644
index bac9c35..0000000
--- a/askant/fsplugins/gfs2/gfs2.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/**
- * main.c - Functions for parsing the on-disk structure of a GFS2 fs.
- *          Written by Andrew Price
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <fcntl.h>
-#include <libgfs2.h>
-#include <errno.h>
-
-void (*report_func)(long int blk, char *type, long int parent, char *fn);
-
-/* Define how the block information is reported */
-#define report(b,p,t,n) \
-	((*report_func)((long int)b, t, (long int)p,n))
-/*	(printf("%lu\t%s\t%lu\t%s\n", (long int)b, t, (long int)p, f)) */
-#define report_data(b,p) report(b,p,"D","")
-#define report_leaf(b,p) report(b,p,"L","")
-#define report_indir(b,p) report(b,p,"i","")
-#define report_inode(b,p,n) report(b,p,"I",n)
-
-struct blk_extended {
-	uint64_t parent_blk;
-	char *fname;
-	char *blk;
-};
-
-/* FIXME: A static length block stack is most likely a stupid idea */
-struct blkstack {
-	int top;
-	struct blk_extended blocks[1024];
-};
-
-struct blkstack blk_stack;
-struct gfs2_sb sb;
-uint32_t blk_size;
-off_t max_seek;
-int fd;
-int flag_stop;
-
-/* prog_name and print_it() are needed to satisfy externs in libgfs2 */
-char *prog_name = "askant";
-
-void print_it(const char *label, const char *fmt, const char *fmt2, ...)
-{
-	va_list args;
-
-	va_start(args, fmt2);
-	printf("%s = ", label);
-	vprintf(fmt, args);
-	printf("\n");
-	va_end(args);
-}
-
-/**
- * Push a block onto the stack
- */
-static void push_blk(char *blk, uint64_t parent, char *fn)
-{
-	struct blk_extended eblk;
-
-	eblk.blk = blk;
-	eblk.parent_blk = parent;
-	eblk.fname = fn;
-
-	blk_stack.top++;
-	blk_stack.blocks[blk_stack.top] = eblk;
-}
-
-/**
- * Initialise the block stack
- */
-static int blk_stack_init(void)
-{
-	blk_stack.top = -1;
-	return 1;
-}
-
-/**
- * Pop a block from the stack
- */
-static struct blk_extended pop_blk(void)
-{
-	return blk_stack.blocks[blk_stack.top--];
-}
-
-/**
- * Read the GFS2 superblock into the global sb variable.
- * Returns 1 on error, 0 on success.
- */
-static int read_gfs2_sb(void)
-{
-	off_t offsetsb;
-	off_t offsetres;
-	unsigned char buffer[GFS2_BASIC_BLOCK];
-	ssize_t readsz;
-
-	offsetsb = GFS2_SB_ADDR * GFS2_BASIC_BLOCK;
-	offsetres = lseek(fd, offsetsb, SEEK_SET);
-
-	if (offsetres != offsetsb) {
-		fprintf(stderr, "Could not seek to sb location on device.\n");
-		return 0;
-	}
-
-	readsz = read(fd, buffer, GFS2_BASIC_BLOCK);
-	if (readsz != GFS2_BASIC_BLOCK) {
-		fprintf(stderr, "Could not read superblock.\n");
-		return 0;
-	}
-
-	gfs2_sb_in(&sb, (char *)buffer);
-	if (check_sb(&sb)) {
-		fprintf(stderr, "Not a GFS2 filesystem.\n");
-		return 0;
-	}
-
-	return 1;
-}
-
-/**
- * Read a block.
- * blk_offset must be a block number.
- * The returned pointer must be free'd.
- */
-static char *read_gfs2_blk(off_t blk_offset)
-{
-	off_t offset;
-	off_t offsetres;
-	ssize_t readsz;
-	char *buffer;
-
-	buffer = (char *)malloc(blk_size);
-	if (!buffer) {
-		fprintf(stderr, "Could not allocate memory for block.\n");
-		return NULL;
-	}
-
-	offset = blk_offset * blk_size;
-
-	offsetres = lseek(fd, offset, SEEK_SET);
-	if (offsetres != offset) {
-		fprintf(stderr,
-			"Could not seek to block location: %lu error: %s\n",
-			(long int)blk_offset, strerror(errno));
-		return NULL;
-	}
-
-	readsz = read(fd, buffer, blk_size);
-	if (readsz != blk_size) {
-		fprintf(stderr, "Could not read block: %lu\n",
-					(long int)blk_offset);
-		return NULL;
-	}
-
-	return buffer;
-}
-
-/**
- * Look at indirect pointers from a starting point in a block.
- */
-static void do_indirect(char *start, uint16_t height, uint64_t parent)
-{
-	uint64_t ptr;
-	unsigned int i;
-	char *blk;
-
-	for (i = 0; i < blk_size; i += sizeof(uint64_t)) {
-		ptr = be64_to_cpu(*(uint64_t *)(start + i));
-		if (ptr > 0 && ptr < (max_seek / blk_size)) {
-			if (height == 1) {
-				report_data(ptr, parent);
-			} else if (height > 1) {
-				blk = read_gfs2_blk(ptr);
-				if (blk) {
-					report_indir(ptr, parent);
-					do_indirect(blk, height - 1, ptr);
-					free(blk);
-				}
-			}
-		} else {
-			break;
-		}
-	}
-}
-
-/**
- * Parse count number of dirents from a starting point in a block.
- */
-static void do_dirents(char *dirents, char *end, uint64_t parent, uint64_t gparent)
-{
-	struct gfs2_dirent dirent;
-	char *di_blk;
-	char *fname;
-
-	while (dirents < end) {
-		gfs2_dirent_in(&dirent, dirents);
-		if (dirent.de_inum.no_addr &&
-			dirent.de_inum.no_addr != parent &&
-			dirent.de_inum.no_addr != gparent &&
-			dirent.de_name_len > 0 &&
-			dirent.de_name_len <= GFS2_FNAMESIZE) {
-
-			fname = (char *)malloc(dirent.de_name_len + 1);
-			if (!fname) {
-				break;
-			}
-
-			memcpy(fname, dirents + sizeof(struct gfs2_dirent),
-				dirent.de_name_len);
-			fname[dirent.de_name_len] = '\0';
-
-			di_blk = read_gfs2_blk(dirent.de_inum.no_addr);
-			if (di_blk) {
-				push_blk(di_blk, parent, fname);
-			}
-		}
-		dirents += dirent.de_rec_len;
-	}
-}
-
-/**
- * Examine the dirents in a leaf block.
- * If the leaf is chained, do the chained leaves too.
- */
-static void do_leaf(char *blk, uint64_t parent, uint64_t gparent)
-{
-	struct gfs2_leaf leaf;
-
-	while (blk) {
-		gfs2_leaf_in(&leaf, blk);
-		do_dirents(blk + sizeof(struct gfs2_leaf), blk + blk_size,
-							parent, gparent);
-		free(blk);
-		if (!leaf.lf_next) {
-			break;
-		}
-
-		blk = read_gfs2_blk(leaf.lf_next);
-	}
-}
-
-/**
- * Parse leaf pointer data and examine the
- * dirents in the destination leaf blocks.
- */
-static void do_leaves(char *start, uint64_t parent, uint64_t gparent)
-{
-	uint64_t ptr;
-	uint64_t prev;
-	unsigned int i;
-	char *blk;
-
-	prev = 0;
-	for (i = 0; i < blk_size - sizeof(struct gfs2_dinode);
-					i += sizeof(uint64_t)) {
-		ptr = be64_to_cpu(*(uint64_t *)(start + i));
-
-		if (ptr >= (max_seek / blk_size)) {
-			break;
-		}
-
-		if (ptr && ptr != prev) {
-			blk = read_gfs2_blk(ptr);
-			if (blk) {
-				report_leaf(ptr, parent);
-				do_leaf(blk, parent, gparent);
-			}
-			prev = ptr;
-		}
-	}
-}
-
-/**
- * Parse inode data from a block
- */
-static void do_inode_blk(char *blk, uint64_t parent, char *fname)
-{
-	struct gfs2_dinode di;
-	char *data;
-
-	gfs2_dinode_in(&di, blk);
-	report_inode(di.di_num.no_addr, parent, fname);
-
-	data = (char *)((struct gfs2_dinode *)blk + 1);
-
-	if (di.di_height > 0) {
-		/* Indirect pointers */
-		do_indirect(data, di.di_height, di.di_num.no_addr);
-	} else if (S_ISDIR(di.di_mode) && !(di.di_flags & GFS2_DIF_EXHASH)) {
-		/* Stuffed directory */
-		do_dirents(data, blk + blk_size, di.di_num.no_addr, parent);
-	} else if (S_ISDIR(di.di_mode) && 
-				(di.di_flags & GFS2_DIF_EXHASH) && 
-				!(di.di_height)) {
-		/* Directory, has hashtable, height == 0 */
-		do_leaves(data, di.di_num.no_addr, parent);
-	}
-
-	/* free previously stacked block */
-	free(fname);
-	free(blk);
-}
-
-/**
- * Get the root dir block and parse the fs
- * using a stack to keep track of the unvisited
- * inode blocks.
- */
-static void parse_fs(void)
-{
-	struct gfs2_inum *root_dir_inum;
-	struct gfs2_inum *master_dir_inum;
-	struct blk_extended blk;
-	char *root_blk;
-	char *master_blk;
-
-	flag_stop = 0;
-
-	root_dir_inum = &(sb.sb_root_dir);
-	master_dir_inum = &(sb.sb_master_dir);
-
-	root_blk = read_gfs2_blk(root_dir_inum->no_addr);
-	master_blk = read_gfs2_blk(master_dir_inum->no_addr);
-	if (!root_blk || !master_blk) {
-		return;
-	}
-
-	push_blk(root_blk, root_dir_inum->no_addr, NULL);
-	while (blk_stack.top >= 0 && !flag_stop) {
-		blk = pop_blk();
-		do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
-	}
-
-	push_blk(master_blk, master_dir_inum->no_addr, NULL);
-	while (blk_stack.top >= 0 && !flag_stop) {
-		blk = pop_blk();
-		/* TODO: Examine each block's magic number instead of assuming
-		 * they're inodes. Omitted for now due to time constraints and
-		 * the number of GFS2_METATYPE_*s which need catering for.
-		 */
-		do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
-	}
-}
-
-/**
- * Raise a flag to stop the parse loop cleanly
- */
-void gfs2_stop(void)
-{
-	flag_stop = 1;
-}
-
-/**
- * Parse a gfs2 file system on a given device
- */
-int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f))
-{
-	report_func = func;
-
-	if (!blk_stack_init()) {
-		return 0;
-	}
-
-	if ((fd = open(dev, O_RDONLY)) < 0) {
-		return 0;
-	}
-
-	if (!read_gfs2_sb()) {
-		close(fd);
-		return 0;
-	}
-
-	blk_size = sb.sb_bsize;
-	max_seek = lseek(fd, 0, SEEK_END);
-
-	parse_fs();
-
-	close(fd);
-
-	return 1;
-}
-
-/**
- * Return the block size of the gfs2 file system on a given device.
- */
-uint32_t gfs2_block_size(char *dev)
-{
-	if ((fd = open(dev, O_RDONLY)) < 0) {
-		return 0;
-	}
-
-	if (!read_gfs2_sb()) {
-		close(fd);
-		return 0;
-	}
-	close(fd);
-	return sb.sb_bsize;
-}
diff --git a/askant/fsplugins/gfs2/gfs2.h b/askant/fsplugins/gfs2/gfs2.h
deleted file mode 100644
index 6c72eb0..0000000
--- a/askant/fsplugins/gfs2/gfs2.h
+++ /dev/null
@@ -1,3 +0,0 @@
-uint32_t gfs2_block_size(char *dev);
-int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f));
-void gfs2_stop(void);
diff --git a/askant/fsplugins/gfs2/gfs2module.c b/askant/fsplugins/gfs2/gfs2module.c
deleted file mode 100644
index c6a0584..0000000
--- a/askant/fsplugins/gfs2/gfs2module.c
+++ /dev/null
@@ -1,104 +0,0 @@
-#include <Python.h>
-
-#include "gfs2.h"
-
-static PyObject *report_func = NULL;
-
-void report_func_wrapper(long int blk, char *type, long int parent, char *fn)
-{
-	PyObject *arglist;
-	PyObject *result;
-
-	arglist = Py_BuildValue("lsls", blk, type, parent, fn);
-	if (!arglist) {
-		return;
-	}
-	result = PyEval_CallObject(report_func, arglist);
-	Py_DECREF(arglist);
-	if (!result) {
-		return;
-	}
-	Py_DECREF(result);
-}
-
-static PyObject *gfs2_set_report_hook(PyObject *self, PyObject *args)
-{
-	PyObject *result = NULL;
-	PyObject *temp;
-
-	if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
-		if (!PyCallable_Check(temp)) {
-			PyErr_SetString(PyExc_TypeError, "parameter must be callable");
-			return NULL;
-		}
-		Py_XINCREF(temp);
-		Py_XDECREF(report_func);
-		report_func = temp;
-
-		Py_INCREF(Py_None);
-		result = Py_None;
-	}
-	return result;
-}
-
-static PyObject *gfs2_parsefs(PyObject *self, PyObject *args)
-{
-	char *dev;
-
-	if (!PyArg_ParseTuple(args, "s:parsefs", &dev)) {
-		return NULL;
-	}
-
-	if (!gfs2_parse(dev, &report_func_wrapper)) {
-		return PyErr_SetFromErrno(PyExc_IOError);
-	}
-	Py_INCREF(Py_None);
-
-	return Py_None;
-}
-
-static PyObject *gfs2_get_block_size(PyObject *self, PyObject *args)
-{
-	char *dev;
-	uint32_t blksize;
-
-	if (!PyArg_ParseTuple(args, "s:get_block_size", &dev)) {
-		return NULL;
-	}
-
-	blksize = gfs2_block_size(dev);
-	if (!blksize) {
-		return PyErr_SetFromErrno(PyExc_IOError);
-	}
-
-	PyObject *size = Py_BuildValue("I", blksize);
-	if (!size) {
-		return NULL;
-	}
-	Py_INCREF(size);
-	return size;
-}
-
-static PyObject *gfs2_handle_sigint(PyObject *signum, PyObject *frame)
-{
-	gfs2_stop();
-	Py_INCREF(Py_None);
-	return Py_None;
-}
-
-static PyMethodDef GFS2Methods[] = {
-	{"set_report_hook", gfs2_set_report_hook, METH_VARARGS,
-		"Specify a hook function through which to report blocks."},
-	{"parsefs", gfs2_parsefs, METH_VARARGS,
-		"Parses the given block device as a GFS2 file system."},
-	{"get_block_size", gfs2_get_block_size, METH_VARARGS,
-		"Returns the file system block size."},
-	{"handle_sigint", gfs2_handle_sigint, METH_VARARGS,
-		"Handles signal SIGINT."},
-	{NULL, NULL, 0, NULL}
-};
-
-PyMODINIT_FUNC initgfs2(void)
-{
-    (void)Py_InitModule("gfs2", GFS2Methods);
-}
diff --git a/askant/scripts/askant b/askant/scripts/askant
deleted file mode 100755
index 3375b0c..0000000
--- a/askant/scripts/askant
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env python
-
-from askant import askant
-
-if __name__ == '__main__':
-	askant.main()
diff --git a/askant/setup.py b/askant/setup.py
deleted file mode 100755
index 78daff2..0000000
--- a/askant/setup.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python
-
-from distutils.core import setup, Extension
-from askant import about
-
-setup(name="askant",
-	  version=about.version,
-	  description="File system performance analysis tool",
-	  author="Andrew Price",
-	  author_email="andy@andrewprice.me.uk",
-	  url="http://andrewprice.me.uk/projects/askant",
-	  packages = ['askant','askant.fs'],
-	  ext_modules = [Extension("askant.fs.gfs2",
-		  sources = ["fsplugins/gfs2/gfs2module.c","fsplugins/gfs2/gfs2.c"],
-		  libraries = ["gfs2"],
-		  include_dirs=['../gfs2/libgfs2', '../gfs2/include', '../make'],
-		  library_dirs=['../gfs2/libgfs2'])],
-	  scripts = ['scripts/askant']) 
diff --git a/configure b/configure
index 867762a..858076a 100755
--- a/configure
+++ b/configure
@@ -86,6 +86,7 @@ my %options = (
 	fenceagentslibdir => \$fenceagentslibdir,
 	enable_crack_of_the_day => \$enable_crack_of_the_day,
 	enable_legacy_code => \$enable_legacy_code,
+	enable_contrib => \$enable_contrib,
 	enable_xen => \$enable_xen,
 	somajor => \$somajor,
 	sominor => \$sominor,
@@ -175,6 +176,7 @@ my $err = &GetOptions (\%options,
 		    'fenceagentslibdir=s',
 		    'enable_crack_of_the_day',
 		    'enable_legacy_code',
+		    'enable_contrib',
 		    'enable_xen',
 		    'without_ccs',
 		    'without_cman',
@@ -266,6 +268,7 @@ if ($help || !$err) {
   print "\t\tUse --fence_agents=help for a list\n";
   print "--fenceagentslibdir=\tspecify directory where to install common fence python lib.  (Default: /usr/share/fence)\n";
   print "--enable_crack_of_the_day\tEnable build of highly experimental features that rely on code that is not yet available for general use. (Default: no)\n";
+  print "--enable_contrib\tEnable build of community contributed code/tools.  (Default: no)\n";
   print "--enable_legacy_code\tEnable build of old/obsolete/unsupported code/tools.  (Default: no)\n";
   print "--enable_xen\tEnable building of Xen-specific pieces\n";
   print "--without_ccs\tDisable ccs building  (Default: enabled)\n";
@@ -595,6 +598,9 @@ if (!$enable_crack_of_the_day) {
   print "\n********************************************************************************************\n";
   $cflags="${cflags} -DEXPERIMENTAL_BUILD";
 }
+if (!$enable_contrib) {
+  $enable_contrib="";
+}
 if (!$enable_legacy_code) {
   $enable_legacy_code="";
 } else {
@@ -748,6 +754,7 @@ while (<IFILE>) {
   $_ =~ s/\@FENCEAGENTSLIBDIR\@/$fenceagentslibdir/;
   $_ =~ s/\@ENABLE_CRACK_OF_THE_DAY\@/$enable_crack_of_the_day/;
   $_ =~ s/\@ENABLE_LEGACY_CODE\@/$enable_legacy_code/;
+  $_ =~ s/\@ENABLE_CONTRIB\@/$enable_contrib/;
   $_ =~ s/\@ENABLE_XEN\@/$enable_xen/;
   $_ =~ s/\@DISABLE_CCS\@/$without_ccs/;
   $_ =~ s/\@DISABLE_CMAN\@/$without_cman/;
diff --git a/contrib/askant/INSTALL b/contrib/askant/INSTALL
new file mode 100644
index 0000000..ce26b82
--- /dev/null
+++ b/contrib/askant/INSTALL
@@ -0,0 +1,42 @@
+
+  How To Install Askant
+  ---------------------
+
+o Build Dependencies
+  - Python
+  - libgfs2 (part of the Red Hat cluster suite as a static lib)
+
+o Dependencies
+  - Python
+  - blktrace and blkparse with appropriate kernel config options enabled and
+    debugfs mounted
+
+o Intro
+  Askant uses Python's distutils for its standard installation procedure so the
+  setup.py script provides installation routines. Note that it doesn't provide
+  uninstallation routines so the best way to install it is to obtain a package
+  for your particular distribution and install that.
+
+o Installation
+  To install askant to the default location on your system, run as root:
+
+    ./setup.py install
+
+  If you wish to install it to a different root directory (e.g. /var/mychroot)
+  or prefix directory (e.g. /usr/local instead of /usr) see:
+  
+    ./setup.py install --help
+
+  To find out everything else setup.py can do:
+
+    ./setup.py --help-commands
+
+o Use Without Installing
+  To use askant without installing it (e.g. if you want to test patches
+  quickly) it is necessary to carry out the build stage to compile the required
+  plugins. Do:
+
+    ./setup.py build
+    cd build/lib*
+    python askant/askant.py
+
diff --git a/contrib/askant/PLUGINAPI b/contrib/askant/PLUGINAPI
new file mode 100644
index 0000000..4c2eceb
--- /dev/null
+++ b/contrib/askant/PLUGINAPI
@@ -0,0 +1,65 @@
+
+  Writing File System Plugins for Askant
+  --------------------------------------
+
+0. Contents
+
+  1. Intro
+  2. API
+  3. Reference
+
+
+1. Intro
+
+In order to gather file system data with which to enhance the data provided by
+blktrace, there must be specific code written for each file system type we wish
+to support. This is where file system plugins come along. The plugins must
+basically traverse an on-disk file system structure on a block device and report
+back their findings through a callback handed to them by askant. They must also
+expose a function which stops their traversal loop in case askant wishes to stop
+their execution prematurely.
+
+
+2. API
+
+A file system plugin must be written as a Python module. This means that C, C++
+and Python are suitable languages to write them in. They must expose a number of
+Python functions to askant:
+
+parsefs(dev)
+    
+    This function is called when askant wants the plugin to parse the file
+    system on the device specified by the device, dev.
+
+get_block_size()
+
+    This function is called to ascertain the file system block size which is
+    required in order to map the disk sector numbers provided by blktrace to
+    file system block numbers. It takes no arguments and should return a Python
+    integer value.
+
+set_report_hook(func)
+
+    This function is called to pass in a report function which the plugin should
+    call to report the details of a file system block. func is a function which
+    accepts four arguments in this order:
+    
+    Block number: a Python long integer
+    Block type: a Python string
+    Block number of parent: a Python long integer
+    Block file name: a Python string (should be "" for non-inode blocks)
+
+handle_sigint()
+
+    This function should make the plugin stop its parsing loop. This allows it
+    to be interrupted if askant catches a signal, for example. It takes no
+    arguments and its return value is not used.
+
+
+3. Reference
+
+o See fsplugins/gfs2/gfs2module.c for an example of writing a Python fs plugin
+  module in C.
+
+o http://www.python.org/doc/ext/intro.html shows how to extend Python with C or
+  C++.
diff --git a/contrib/askant/README b/contrib/askant/README
new file mode 100644
index 0000000..74e3219
--- /dev/null
+++ b/contrib/askant/README
@@ -0,0 +1,74 @@
+
+ Askant - A Block I/O Analysis Tool
+ ----------------------------------
+
+0. Contents
+
+  1. Intro
+  2. Commands and their options
+  3. Examples
+
+
+1. Intro
+
+Askant allows you to gather file system block I/O data for performance analysis.
+It uses blktrace to trace block operations and adds file system specific data by
+parsing fs block information straight from the storage device. To do this it
+invokes functionality from file system parser plugins which traverse the on-disk
+structures and report details of the blocks they encounter through callbacks.
+
+Usage: askant <command> [options...]
+
+o See 'askant --commands' for a full list of commands.
+
+
+2. Commands and their options
+
+  dumpfs
+
+    Dumps the block data provided by an fs plugin from the given file system.
+
+    -d  The block device which contains the file system under scrutiny
+    -t  The type of the file system, i.e. the name of the fs plugin to load
+    -o  Output file (optional). Stdout is used if '-' or omitted.
+        (Note that the output file should be on a different device to the one
+	specified with -d.)
+  
+  unlinks
+
+    Does a dumpfs and runs blktrace until interrupted. It then blkparses the
+    blktrace data and adds the block data. This is suitable for tracing I/O
+    operations during an unlink test run.
+
+    -d  The block device which contains the file system under scrutiny
+    -t  The type of the file system, i.e. the name of the fs plugin to load
+    -g  The path to the debugfs mount point, defaults to /sys/kernel/debug
+    -D  The directory in which to store the block dumps and blktrace files.
+
+  creates
+
+    Runs blktrace until interrupted. It then does a dumpfs and blkparses the
+    blktrace data and adds the block data. This is suitable for tracing I/O
+    operations during a file creation test run.
+
+    -d  The block device which contains the file system under scrutiny
+    -t  The type of the file system, i.e. the name of the fs plugin to load
+    -g  The path to the debugfs mount point, defaults to /sys/kernel/debug
+    -D  The directory in which to store the block dumps and blktrace files.
+
+3. Examples
+
+o Parse the file system and dump information about each block to stdout e.g.:
+
+    # askant dumpfs -t gfs2 -d /dev/sda3
+
+o Trace 'unlinks' by dumping block information before the blktrace and matching
+  block data to provide a more detailed blkparse output e.g.:
+
+    # askant unlinks -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
+
+o Trace 'creates' by dumping block information after the blktrace and matching
+  block data to provide a more detailed blkparse output e.g.:
+
+    # askant creates -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
+
diff --git a/contrib/askant/askant/__init__.py b/contrib/askant/askant/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/contrib/askant/askant/about.py b/contrib/askant/askant/about.py
new file mode 100644
index 0000000..3d40a74
--- /dev/null
+++ b/contrib/askant/askant/about.py
@@ -0,0 +1,5 @@
+"""
+Version information for askant.
+"""
+
+version = "0.0.1"
diff --git a/contrib/askant/askant/askant.py b/contrib/askant/askant/askant.py
new file mode 100644
index 0000000..a4376b6
--- /dev/null
+++ b/contrib/askant/askant/askant.py
@@ -0,0 +1,24 @@
+"""
+The main entry point for askant.
+"""
+
+import sys
+import commands
+
+def main():
+
+	"""Run askant"""
+
+	cmds = commands.Commands()
+	cmds.register_command(commands.DumpFSCommand())
+	cmds.register_command(commands.UnlinksCommand())
+	cmds.register_command(commands.CreatesCommand())
+
+	try:
+		cmds.process_argv(sys.argv)
+	except commands.NoFSPluginException, p:
+		print >>sys.stderr, "Plugin not found: %s" %p
+
+
+if __name__ == '__main__':
+	main()
diff --git a/contrib/askant/askant/blktrace.py b/contrib/askant/askant/blktrace.py
new file mode 100644
index 0000000..fdf0219
--- /dev/null
+++ b/contrib/askant/askant/blktrace.py
@@ -0,0 +1,93 @@
+"""
+Wrapper classes for running blktrace and blkparse and using their output
+"""
+
+import os
+import sys
+import time
+import signal
+from subprocess import Popen
+from subprocess import PIPE
+from sysfs import Sysfs
+
+class BlktraceException(Exception):
+
+	def __init__(self, val, msg):
+		Exception.__init__(self)
+		self.val = val
+		self.msg = msg
+	
+	def __str__(self):
+		return self.msg
+
+class Blktrace:
+
+	def __init__(self, dev, debugfs='/sys/kernel/debug'):
+		self.dev = dev
+		self.debugfs = debugfs
+		self.sysfs = Sysfs(dev)
+		self.btpid = -1
+
+	def handle_sigint(self, sig, frame):
+		if self.btpid >= 0:
+			os.kill(self.btpid, signal.SIGTERM)
+
+	def trace(self, tracefile):
+
+		if not self.dev:
+			raise Exception("No device specified.")
+
+		btargs = ['blktrace',
+			'-d', self.dev,
+			'-r', self.debugfs,
+			'-o', tracefile]
+
+		blktrace = Popen(btargs, bufsize=1, stdout=PIPE,
+						stderr=open('/dev/null','w'))
+		self.btpid = blktrace.pid
+		btres = None
+		while btres is None:
+			time.sleep(1)
+			btres = blktrace.poll()
+
+		self.btpid = -1
+		if btres:
+			raise BlktraceException(btres,
+				'blktrace exited with code ' + str(btres))
+
+
+
+	def parse(self, tracefile, getblk):
+
+		if not self.dev:
+			raise Exception("No device specified.")
+
+		offset = self.sysfs.get_partition_start_sector()
+
+		bpargs = ['blkparse', '-i', tracefile]
+		blkparse = Popen(bpargs, bufsize=1, stdout=PIPE)
+
+		bpres = None
+		while bpres is None:
+			output = blkparse.stdout.readline().strip()
+			if output:
+				chunks = output.split()
+				try:
+					# chunks[7] is the sector number
+					blk = list(getblk(int(chunks[7])))
+					print "%s %s %s" %\
+						(' '.join(blk[0:3]),\
+						 output.strip(),
+						 blk[3].strip())
+				except KeyError:
+					pass
+				except ValueError:
+					pass
+
+			bpres = blkparse.poll()
+
+		if bpres:
+			raise BlktraceException(bpres,
+				'blkparse exited with code ' + str(bpres))
+
+
diff --git a/contrib/askant/askant/commands.py b/contrib/askant/askant/commands.py
new file mode 100644
index 0000000..ad4639f
--- /dev/null
+++ b/contrib/askant/askant/commands.py
@@ -0,0 +1,333 @@
+"""Command line parsing and processing for askant"""
+
+import fs
+import os
+import sys
+import time
+import about
+import signal
+import tempfile
+from sysfs import Sysfs, SysfsException
+from blktrace import Blktrace, BlktraceException
+from optparse import OptionParser, make_option
+
+class NoFSPluginException(Exception):
+
+	"""A generic Exception class for when file system plugins are missing"""
+
+	pass # Nothing special about this exception
+
+class Commands:
+
+	"""Provides a convenient interface to the askant commands"""
+
+	def __init__(self):
+
+		self.commands = {}
+		self.parser = OptionParser(usage="%prog COMMAND [options]",
+				version=about.version)
+
+
+	def register_command(self, command):
+		self.commands[str(command)] = command
+	
+	def __lookup_command(self, command):
+		try:
+			return self.commands[command]
+		except KeyError, k:
+			return DefaultCommand()
+	
+	def __parse_command(self, command, argv):
+		self.parser.prog = "%s %s" %\
+			(self.parser.get_prog_name(), str(command))
+		self.parser.set_usage(command.get_usage())
+		optlst = command.get_options()
+		for o in optlst:
+			self.parser.add_option(o)
+		self.parser.set_description(command.get_help())
+
+		if str(command) != "":
+			argv = argv[1:]
+
+		return self.parser.parse_args(argv)
+
+	def process_argv(self, argv):
+		try:
+			command = self.__lookup_command(argv[1])
+		except IndexError, i:
+			# This exits
+			self.parser.error("No command specified. Try --help")
+
+		options, args = self.__parse_command(command, argv[1:])
+
+		command.do_command(options, args, self)
+
+class Command:
+	def __init__(self):
+		self.options = []
+
+class DefaultCommand(Command):
+	def __str__(self):
+		return ""
+
+	def get_options(self):
+		self.options.append(make_option("-c","--commands",
+			action="store_true",
+			help="Lists available commands"))
+		return self.options
+
+	def get_usage(self):
+		return "%prog COMMAND [options] [args]"
+
+	def get_help(self):
+		return "Askant is a tool for tracing I/O activity in Linux "\
+			"and adding extra file system context by "\
+			"parsing file system data directly."
+	
+	def do_command(self, options, args, base):
+		if options.commands:
+			base.parser.print_usage()
+			print "Available commands:"
+			keys = base.commands.keys()
+			keys.sort()
+			for k in keys:
+				print " %10s  %s" %\
+					(k, base.commands[k].get_help())
+			print ""
+			print "For command-specific help, use "\
+				"%sCOMMAND --help" % base.parser.get_prog_name()
+		else:
+			base.parser.error("Command not found. Try --commands")
+
+
+class FSCommandTemplate(Command):
+	def __init__(self):
+		self.fsmod = None
+		self.block_table = {}
+		self.block_func = self.__report_hook
+		self.sector_size = None
+		self.offset = None
+		self.options = [
+			make_option("-d","--device", metavar="DEVICE",
+				help="The device to analyse."),
+		]
+
+	def load_fs_plugin(self, fsname):
+		try:
+			self.fsmod = __import__("fs." + fsname,
+					globals(), locals(), ["fs"])
+		except ImportError, i:
+			raise NoFSPluginException(fsname)
+
+	def parse_fs(self, dev, hook=None):
+		if hook is None:
+			hook = self.block_func
+
+		sfs = Sysfs(dev)
+		offset = sfs.get_partition_start_sector()
+		sector_size = sfs.get_dev_sector_size()
+
+		self.fsmod.set_report_hook(hook)
+
+		try:
+	                sfs = Sysfs(dev)
+        	        self.offset = sfs.get_partition_start_sector()
+                	self.sector_size = sfs.get_dev_sector_size()
+			signal.signal(signal.SIGINT, self.fsmod.handle_sigint)
+			self.blk_size = self.fsmod.get_block_size(dev)
+			self.fsmod.parsefs(dev)
+			signal.signal(signal.SIGINT, signal.SIG_DFL)
+		except IOError, i:
+			print >>sys.stderr, str(i)
+			sys.exit(1)
+
+	def set_outfile(self, outfile):
+		self.outfile = outfile
+
+	def trace(self, options, dump_before):
+
+		sfs = Sysfs(options.device)
+		self.offset = sfs.get_partition_start_sector()
+		self.sector_size = sfs.get_dev_sector_size()
+
+		tmstamp = time.strftime('%Y%m%d%H%M%S')
+		self.block_func = self.__report_hook
+		try:
+			self.outfile = open(os.path.join(options.outdir, "blocks-%s" %
+				tmstamp), 'w')
+		except IOError, e:
+			print >>sys.stderr, str(e)
+			sys.exit(1)
+
+		if dump_before:
+			print >>sys.stderr, "Gathering block data..."
+			self.parse_fs(options.device)
+			self.outfile.close()
+
+		if options.debugfs:
+			bt = Blktrace(options.device, options.debugfs)
+		else:
+			bt = Blktrace(options.device)
+
+		os.chdir(options.outdir) # Blktrace doesn't use absolute paths
+		print >>sys.stderr, "Tracing. Hit Ctrl-C to end..."
+		try:
+			signal.signal(signal.SIGINT, bt.handle_sigint)
+			bt.trace(tmstamp)
+			signal.signal(signal.SIGINT, signal.SIG_DFL)
+		except BlktraceException, b:
+			signal.signal(signal.SIGINT, signal.SIG_DFL)
+			print >>sys.stderr, str(b)
+			sys.exit(1)
+
+		if not dump_before:
+			print >>sys.stderr, "Gathering block data..."
+			self.parse_fs(options.device)
+			self.outfile.close()
+
+		print >>sys.stderr, "Matching blocks..."
+		blockdb = open(self.outfile.name, 'r')
+		blocks = {}
+		for l in blockdb.readlines():
+			s = l.split('\t')
+			blocks[int(s[0])] = tuple(s[1:])
+
+		bt.parse(os.path.join(options.outdir, tmstamp), blocks.__getitem__)
+		blockdb.close()
+
+	def __report_hook(self, blk, type, parent, fn):
+		if not fn:
+			fn = ""
+		try:
+			self.outfile.write("%d\t%d\t%s\t%d\t\"%s\"\n" % (
+			((blk * self.blk_size)/self.sector_size) + self.offset,
+			blk, type, parent, fn))
+		except Exception, e:
+			print str(e)
+
+	def __dict_hook(self, blk, type, parent, fn):
+		self.block_table[
+		    ((blk * self.blk_size)/self.sector_size)+self.offset] =\
+		    (blk, type, parent, fn)
+
+
+class DumpFSCommand(FSCommandTemplate):
+	def __str__(self):
+		return "dumpfs"
+
+	def get_options(self):
+		self.options.append(make_option("-t","--type", metavar="FSTYPE",
+			help="The type of file system on the device."))
+		self.options.append(make_option("-o","--output", metavar="FILE",
+			default="-",
+			help="File to write output to or '-' for stdout "
+			"(default)"))
+		return self.options
+	
+	def get_usage(self):
+		return "%prog [options]"
+	
+	def get_help(self):
+		return "Dump fs block information in TSV format."
+	
+	def do_command(self, options, args, base):
+
+		if not options.device:
+			base.parser.error("No device specified. Use -d.")
+
+		if not options.type:
+			base.parser.error("No file system type specified. "
+								"Use -t.")
+		try:
+			if options.output != "-":
+				self.set_outfile(open(options.output, 'w'))
+			else:
+				self.set_outfile(sys.stdout)
+		except IOError, e:
+			print >>sys.stderr,\
+				"Unable to open file for writing: %s" %\
+				options.output
+			return
+		except KeyError:
+			pass
+
+		self.load_fs_plugin(options.type)
+		try:
+			self.parse_fs(options.device)
+		except SysfsException, s:
+			base.parser.error(s)
+		
+class UnlinksCommand(FSCommandTemplate):
+	def __str__(self):
+		return "unlinks"
+
+	def get_options(self):
+		self.options.append(make_option("-t","--type", metavar="FSTYPE",
+			help="The type of file system on the device."))
+		self.options.append(make_option("-g","--debugfs", metavar="PATH",
+			default="/sys/kernel/debug",
+			help="The path to the debugfs mountpoint. Default "
+			"/sys/kernel/debug"))
+		self.options.append(make_option("-D","--outdir", metavar="DIR",
+			help="Directory to write output to. Required."))
+		return self.options
+	
+	def get_usage(self):
+		return "%prog [options]"
+	
+	def get_help(self):
+		return "Trace block I/O activity during unlink tests."
+	
+	def do_command(self, options, args, base):
+		if not options.device:
+			base.parser.error("No device specified. Use -d.")
+
+		self.device = options.device
+
+		if not options.type:
+			base.parser.error("No file system type specified. "
+								"Use -t.")
+		if not options.outdir:
+			base.parser.error("No output directory specified. "
+								"Use -D.")
+
+		self.load_fs_plugin(options.type)
+		self.trace(options, dump_before=True)
+
+class CreatesCommand(FSCommandTemplate):
+	def __str__(self):
+		return "creates"
+
+	def get_options(self):
+		self.options.append(make_option("-t","--type", metavar="FSTYPE",
+			help="The type of file system on the device."))
+		self.options.append(make_option("-g","--debugfs", metavar="PATH",
+			default="/sys/kernel/debug",
+			help="The path to the debugfs mountpoint. Default "
+			"/sys/kernel/debug"))
+		self.options.append(make_option("-D","--outdir", metavar="DIR",
+			help="Directory to write output to. Required."))
+		return self.options
+	
+	def get_usage(self):
+		return "%prog [options]"
+	
+	def get_help(self):
+		return "Trace block I/O activity during create tests."
+	
+	def do_command(self, options, args, base):
+		if not options.device:
+			base.parser.error("No device specified. Use -d.")
+
+		self.device = options.device
+
+		if not options.type:
+			base.parser.error("No file system type specified. "
+								"Use -t.")
+		if not options.outdir:
+			base.parser.error("No output directory specified. "
+								"Use -D.")
+
+		self.load_fs_plugin(options.type)
+		self.trace(options, dump_before=False)
+
diff --git a/contrib/askant/askant/fs/__init__.py b/contrib/askant/askant/fs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/contrib/askant/askant/sysfs.py b/contrib/askant/askant/sysfs.py
new file mode 100644
index 0000000..12b7a3b
--- /dev/null
+++ b/contrib/askant/askant/sysfs.py
@@ -0,0 +1,86 @@
+"""
+Provides access to sysfs data pertaining to storage partitions.
+"""
+
+import os
+import os.path
+
+class SysfsException(Exception):
+	"""
+	An exception which is raised when things go wrong with Sysfs.
+	"""
+	pass # No functionality to add over Exception class
+
+class Sysfs:
+	"""
+	Provides access to sysfs data pertaining to storage partitions.
+	"""
+	def __init__(self, partition, sysfs_path='/sys'):
+		"""
+		Instantiate a Sysfs object. partition should be a path to a
+		disc partition device e.g. /dev/sda3.
+		"""
+		self.partition = partition
+		self.device = self.__partition2parent()
+		self.sysfs_path = sysfs_path
+		line = self.__firstline(os.path.join(
+				sysfs_path,'block',
+				os.path.split(self.device)[1],
+				os.path.split(self.partition)[1],
+				'start'))
+		self.partition_start = int(line)
+		line = self.__firstline(os.path.join(
+				sysfs_path,'block',
+				os.path.split(self.device)[1],
+				'queue',
+				'hw_sector_size'))
+		self.dev_sector_size = int(line)
+		line = self.__firstline(os.path.join(
+				sysfs_path,'block',
+				os.path.split(self.device)[1],
+				os.path.split(self.partition)[1],
+				'size'))
+		self.partition_size = int(line)
+
+	def __firstline(self, fname):
+		"""
+		Read the first line of a file
+		"""
+		try:
+			fobj = open(fname, 'r')
+		except IOError:
+			raise SysfsException(
+				'Could not open file "%s" for reading. Please '
+				'check your kernel is 2.6.25 or later and '
+				'sysfs is mounted.' % fname)
+		line = fobj.readline()
+		fobj.close()
+		return line
+
+	def __partition2parent(self):
+		"""
+		Return the name of a partition device's parent device
+		e.g. /dev/sda3 -> /dev/sda
+		"""
+		# This may need thinking about a bit more -
+		# nothing in life can be this simple.
+		return self.partition.rstrip('0123456789')
+
+
+	def get_partition_start_sector(self):
+		"""
+		Look up the start sector of the partition.
+		"""
+		return self.partition_start
+	
+	def get_dev_sector_size(self):
+		"""
+		Look up the size of the device's sectors.
+		"""
+		return self.dev_sector_size
+	
+	def get_partition_size(self):
+		"""
+		Look up the size of the partition.
+		"""
+		return self.partition_size
diff --git a/contrib/askant/fsplugins/__init__.py b/contrib/askant/fsplugins/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/contrib/askant/fsplugins/gfs2/gfs2.c b/contrib/askant/fsplugins/gfs2/gfs2.c
new file mode 100644
index 0000000..bac9c35
--- /dev/null
+++ b/contrib/askant/fsplugins/gfs2/gfs2.c
@@ -0,0 +1,405 @@
+/**
+ * main.c - Functions for parsing the on-disk structure of a GFS2 fs.
+ *          Written by Andrew Price
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgfs2.h>
+#include <errno.h>
+
+void (*report_func)(long int blk, char *type, long int parent, char *fn);
+
+/* Define how the block information is reported */
+#define report(b,p,t,n) \
+	((*report_func)((long int)b, t, (long int)p,n))
+/*	(printf("%lu\t%s\t%lu\t%s\n", (long int)b, t, (long int)p, f)) */
+#define report_data(b,p) report(b,p,"D","")
+#define report_leaf(b,p) report(b,p,"L","")
+#define report_indir(b,p) report(b,p,"i","")
+#define report_inode(b,p,n) report(b,p,"I",n)
+
+struct blk_extended {
+	uint64_t parent_blk;
+	char *fname;
+	char *blk;
+};
+
+/* FIXME: A static length block stack is most likely a stupid idea */
+struct blkstack {
+	int top;
+	struct blk_extended blocks[1024];
+};
+
+struct blkstack blk_stack;
+struct gfs2_sb sb;
+uint32_t blk_size;
+off_t max_seek;
+int fd;
+int flag_stop;
+
+/* prog_name and print_it() are needed to satisfy externs in libgfs2 */
+char *prog_name = "askant";
+
+void print_it(const char *label, const char *fmt, const char *fmt2, ...)
+{
+	va_list args;
+
+	va_start(args, fmt2);
+	printf("%s = ", label);
+	vprintf(fmt, args);
+	printf("\n");
+	va_end(args);
+}
+
+/**
+ * Push a block onto the stack
+ */
+static void push_blk(char *blk, uint64_t parent, char *fn)
+{
+	struct blk_extended eblk;
+
+	eblk.blk = blk;
+	eblk.parent_blk = parent;
+	eblk.fname = fn;
+
+	blk_stack.top++;
+	blk_stack.blocks[blk_stack.top] = eblk;
+}
+
+/**
+ * Initialise the block stack
+ */
+static int blk_stack_init(void)
+{
+	blk_stack.top = -1;
+	return 1;
+}
+
+/**
+ * Pop a block from the stack
+ */
+static struct blk_extended pop_blk(void)
+{
+	return blk_stack.blocks[blk_stack.top--];
+}
+
+/**
+ * Read the GFS2 superblock into the global sb variable.
+ * Returns 1 on error, 0 on success.
+ */
+static int read_gfs2_sb(void)
+{
+	off_t offsetsb;
+	off_t offsetres;
+	unsigned char buffer[GFS2_BASIC_BLOCK];
+	ssize_t readsz;
+
+	offsetsb = GFS2_SB_ADDR * GFS2_BASIC_BLOCK;
+	offsetres = lseek(fd, offsetsb, SEEK_SET);
+
+	if (offsetres != offsetsb) {
+		fprintf(stderr, "Could not seek to sb location on device.\n");
+		return 0;
+	}
+
+	readsz = read(fd, buffer, GFS2_BASIC_BLOCK);
+	if (readsz != GFS2_BASIC_BLOCK) {
+		fprintf(stderr, "Could not read superblock.\n");
+		return 0;
+	}
+
+	gfs2_sb_in(&sb, (char *)buffer);
+	if (check_sb(&sb)) {
+		fprintf(stderr, "Not a GFS2 filesystem.\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * Read a block.
+ * blk_offset must be a block number.
+ * The returned pointer must be free'd.
+ */
+static char *read_gfs2_blk(off_t blk_offset)
+{
+	off_t offset;
+	off_t offsetres;
+	ssize_t readsz;
+	char *buffer;
+
+	buffer = (char *)malloc(blk_size);
+	if (!buffer) {
+		fprintf(stderr, "Could not allocate memory for block.\n");
+		return NULL;
+	}
+
+	offset = blk_offset * blk_size;
+
+	offsetres = lseek(fd, offset, SEEK_SET);
+	if (offsetres != offset) {
+		fprintf(stderr,
+			"Could not seek to block location: %lu error: %s\n",
+			(long int)blk_offset, strerror(errno));
+		return NULL;
+	}
+
+	readsz = read(fd, buffer, blk_size);
+	if (readsz != blk_size) {
+		fprintf(stderr, "Could not read block: %lu\n",
+					(long int)blk_offset);
+		return NULL;
+	}
+
+	return buffer;
+}
+
+/**
+ * Look at indirect pointers from a starting point in a block.
+ */
+static void do_indirect(char *start, uint16_t height, uint64_t parent)
+{
+	uint64_t ptr;
+	unsigned int i;
+	char *blk;
+
+	for (i = 0; i < blk_size; i += sizeof(uint64_t)) {
+		ptr = be64_to_cpu(*(uint64_t *)(start + i));
+		if (ptr > 0 && ptr < (max_seek / blk_size)) {
+			if (height == 1) {
+				report_data(ptr, parent);
+			} else if (height > 1) {
+				blk = read_gfs2_blk(ptr);
+				if (blk) {
+					report_indir(ptr, parent);
+					do_indirect(blk, height - 1, ptr);
+					free(blk);
+				}
+			}
+		} else {
+			break;
+		}
+	}
+}
+
+/**
+ * Parse count number of dirents from a starting point in a block.
+ */
+static void do_dirents(char *dirents, char *end, uint64_t parent, uint64_t gparent)
+{
+	struct gfs2_dirent dirent;
+	char *di_blk;
+	char *fname;
+
+	while (dirents < end) {
+		gfs2_dirent_in(&dirent, dirents);
+		if (dirent.de_inum.no_addr &&
+			dirent.de_inum.no_addr != parent &&
+			dirent.de_inum.no_addr != gparent &&
+			dirent.de_name_len > 0 &&
+			dirent.de_name_len <= GFS2_FNAMESIZE) {
+
+			fname = (char *)malloc(dirent.de_name_len + 1);
+			if (!fname) {
+				break;
+			}
+
+			memcpy(fname, dirents + sizeof(struct gfs2_dirent),
+				dirent.de_name_len);
+			fname[dirent.de_name_len] = '\0';
+
+			di_blk = read_gfs2_blk(dirent.de_inum.no_addr);
+			if (di_blk) {
+				push_blk(di_blk, parent, fname);
+			}
+		}
+		dirents += dirent.de_rec_len;
+	}
+}
+
+/**
+ * Examine the dirents in a leaf block.
+ * If the leaf is chained, do the chained leaves too.
+ */
+static void do_leaf(char *blk, uint64_t parent, uint64_t gparent)
+{
+	struct gfs2_leaf leaf;
+
+	while (blk) {
+		gfs2_leaf_in(&leaf, blk);
+		do_dirents(blk + sizeof(struct gfs2_leaf), blk + blk_size,
+							parent, gparent);
+		free(blk);
+		if (!leaf.lf_next) {
+			break;
+		}
+
+		blk = read_gfs2_blk(leaf.lf_next);
+	}
+}
+
+/**
+ * Parse leaf pointer data and examine the
+ * dirents in the destination leaf blocks.
+ */
+static void do_leaves(char *start, uint64_t parent, uint64_t gparent)
+{
+	uint64_t ptr;
+	uint64_t prev;
+	unsigned int i;
+	char *blk;
+
+	prev = 0;
+	for (i = 0; i < blk_size - sizeof(struct gfs2_dinode);
+					i += sizeof(uint64_t)) {
+		ptr = be64_to_cpu(*(uint64_t *)(start + i));
+
+		if (ptr >= (max_seek / blk_size)) {
+			break;
+		}
+
+		if (ptr && ptr != prev) {
+			blk = read_gfs2_blk(ptr);
+			if (blk) {
+				report_leaf(ptr, parent);
+				do_leaf(blk, parent, gparent);
+			}
+			prev = ptr;
+		}
+	}
+}
+
+/**
+ * Parse inode data from a block
+ */
+static void do_inode_blk(char *blk, uint64_t parent, char *fname)
+{
+	struct gfs2_dinode di;
+	char *data;
+
+	gfs2_dinode_in(&di, blk);
+	report_inode(di.di_num.no_addr, parent, fname);
+
+	data = (char *)((struct gfs2_dinode *)blk + 1);
+
+	if (di.di_height > 0) {
+		/* Indirect pointers */
+		do_indirect(data, di.di_height, di.di_num.no_addr);
+	} else if (S_ISDIR(di.di_mode) && !(di.di_flags & GFS2_DIF_EXHASH)) {
+		/* Stuffed directory */
+		do_dirents(data, blk + blk_size, di.di_num.no_addr, parent);
+	} else if (S_ISDIR(di.di_mode) && 
+				(di.di_flags & GFS2_DIF_EXHASH) && 
+				!(di.di_height)) {
+		/* Directory, has hashtable, height == 0 */
+		do_leaves(data, di.di_num.no_addr, parent);
+	}
+
+	/* free previously stacked block */
+	free(fname);
+	free(blk);
+}
+
+/**
+ * Get the root dir block and parse the fs
+ * using a stack to keep track of the unvisited
+ * inode blocks.
+ */
+static void parse_fs(void)
+{
+	struct gfs2_inum *root_dir_inum;
+	struct gfs2_inum *master_dir_inum;
+	struct blk_extended blk;
+	char *root_blk;
+	char *master_blk;
+
+	flag_stop = 0;
+
+	root_dir_inum = &(sb.sb_root_dir);
+	master_dir_inum = &(sb.sb_master_dir);
+
+	root_blk = read_gfs2_blk(root_dir_inum->no_addr);
+	master_blk = read_gfs2_blk(master_dir_inum->no_addr);
+	if (!root_blk || !master_blk) {
+		return;
+	}
+
+	push_blk(root_blk, root_dir_inum->no_addr, NULL);
+	while (blk_stack.top >= 0 && !flag_stop) {
+		blk = pop_blk();
+		do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
+	}
+
+	push_blk(master_blk, master_dir_inum->no_addr, NULL);
+	while (blk_stack.top >= 0 && !flag_stop) {
+		blk = pop_blk();
+		/* TODO: Examine each block's magic number instead of assuming
+		 * they're inodes. Omitted for now due to time constraints and
+		 * the number of GFS2_METATYPE_*s which need catering for.
+		 */
+		do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
+	}
+}
+
+/**
+ * Raise a flag to stop the parse loop cleanly
+ */
+void gfs2_stop(void)
+{
+	flag_stop = 1;
+}
+
+/**
+ * Parse a gfs2 file system on a given device
+ */
+int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f))
+{
+	report_func = func;
+
+	if (!blk_stack_init()) {
+		return 0;
+	}
+
+	if ((fd = open(dev, O_RDONLY)) < 0) {
+		return 0;
+	}
+
+	if (!read_gfs2_sb()) {
+		close(fd);
+		return 0;
+	}
+
+	blk_size = sb.sb_bsize;
+	max_seek = lseek(fd, 0, SEEK_END);
+
+	parse_fs();
+
+	close(fd);
+
+	return 1;
+}
+
+/**
+ * Return the block size of the gfs2 file system on a given device.
+ */
+uint32_t gfs2_block_size(char *dev)
+{
+	if ((fd = open(dev, O_RDONLY)) < 0) {
+		return 0;
+	}
+
+	if (!read_gfs2_sb()) {
+		close(fd);
+		return 0;
+	}
+	close(fd);
+	return sb.sb_bsize;
+}
diff --git a/contrib/askant/fsplugins/gfs2/gfs2.h b/contrib/askant/fsplugins/gfs2/gfs2.h
new file mode 100644
index 0000000..6c72eb0
--- /dev/null
+++ b/contrib/askant/fsplugins/gfs2/gfs2.h
@@ -0,0 +1,3 @@
+uint32_t gfs2_block_size(char *dev);
+int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f));
+void gfs2_stop(void);
diff --git a/contrib/askant/fsplugins/gfs2/gfs2module.c b/contrib/askant/fsplugins/gfs2/gfs2module.c
new file mode 100644
index 0000000..c6a0584
--- /dev/null
+++ b/contrib/askant/fsplugins/gfs2/gfs2module.c
@@ -0,0 +1,104 @@
+#include <Python.h>
+
+#include "gfs2.h"
+
+static PyObject *report_func = NULL;
+
+void report_func_wrapper(long int blk, char *type, long int parent, char *fn)
+{
+	PyObject *arglist;
+	PyObject *result;
+
+	arglist = Py_BuildValue("lsls", blk, type, parent, fn);
+	if (!arglist) {
+		return;
+	}
+	result = PyEval_CallObject(report_func, arglist);
+	Py_DECREF(arglist);
+	if (!result) {
+		return;
+	}
+	Py_DECREF(result);
+}
+
+static PyObject *gfs2_set_report_hook(PyObject *self, PyObject *args)
+{
+	PyObject *result = NULL;
+	PyObject *temp;
+
+	if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
+		if (!PyCallable_Check(temp)) {
+			PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+			return NULL;
+		}
+		Py_XINCREF(temp);
+		Py_XDECREF(report_func);
+		report_func = temp;
+
+		Py_INCREF(Py_None);
+		result = Py_None;
+	}
+	return result;
+}
+
+static PyObject *gfs2_parsefs(PyObject *self, PyObject *args)
+{
+	char *dev;
+
+	if (!PyArg_ParseTuple(args, "s:parsefs", &dev)) {
+		return NULL;
+	}
+
+	if (!gfs2_parse(dev, &report_func_wrapper)) {
+		return PyErr_SetFromErrno(PyExc_IOError);
+	}
+	Py_INCREF(Py_None);
+
+	return Py_None;
+}
+
+static PyObject *gfs2_get_block_size(PyObject *self, PyObject *args)
+{
+	char *dev;
+	uint32_t blksize;
+
+	if (!PyArg_ParseTuple(args, "s:get_block_size", &dev)) {
+		return NULL;
+	}
+
+	blksize = gfs2_block_size(dev);
+	if (!blksize) {
+		return PyErr_SetFromErrno(PyExc_IOError);
+	}
+
+	PyObject *size = Py_BuildValue("I", blksize);
+	if (!size) {
+		return NULL;
+	}
+	Py_INCREF(size);
+	return size;
+}
+
+static PyObject *gfs2_handle_sigint(PyObject *signum, PyObject *frame)
+{
+	gfs2_stop();
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyMethodDef GFS2Methods[] = {
+	{"set_report_hook", gfs2_set_report_hook, METH_VARARGS,
+		"Specify a hook function through which to report blocks."},
+	{"parsefs", gfs2_parsefs, METH_VARARGS,
+		"Parses the given block device as a GFS2 file system."},
+	{"get_block_size", gfs2_get_block_size, METH_VARARGS,
+		"Returns the file system block size."},
+	{"handle_sigint", gfs2_handle_sigint, METH_VARARGS,
+		"Handles signal SIGINT."},
+	{NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC initgfs2(void)
+{
+    (void)Py_InitModule("gfs2", GFS2Methods);
+}
diff --git a/contrib/askant/scripts/askant b/contrib/askant/scripts/askant
new file mode 100755
index 0000000..3375b0c
--- /dev/null
+++ b/contrib/askant/scripts/askant
@@ -0,0 +1,6 @@
+#! /usr/bin/env python
+
+from askant import askant
+
+if __name__ == '__main__':
+	askant.main()
diff --git a/contrib/askant/setup.py b/contrib/askant/setup.py
new file mode 100755
index 0000000..78daff2
--- /dev/null
+++ b/contrib/askant/setup.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+from askant import about
+
+setup(name="askant",
+	  version=about.version,
+	  description="File system performance analysis tool",
+	  author="Andrew Price",
+	  author_email="andy@andrewprice.me.uk",
+	  url="http://andrewprice.me.uk/projects/askant",
+	  packages = ['askant','askant.fs'],
+	  ext_modules = [Extension("askant.fs.gfs2",
+		  sources = ["fsplugins/gfs2/gfs2module.c","fsplugins/gfs2/gfs2.c"],
+		  libraries = ["gfs2"],
+		  include_dirs=['../gfs2/libgfs2', '../gfs2/include', '../make'],
+		  library_dirs=['../gfs2/libgfs2'])],
+	  scripts = ['scripts/askant']) 
diff --git a/make/defines.mk.input b/make/defines.mk.input
index 0f62d7d..d09adf6 100644
--- a/make/defines.mk.input
+++ b/make/defines.mk.input
@@ -69,6 +69,7 @@ fence_agents ?= @FENCE_AGENTS@
 fenceagentslibdir ?= @FENCEAGENTSLIBDIR@
 experimental_build ?= @ENABLE_CRACK_OF_THE_DAY@
 legacy_code ?= @ENABLE_LEGACY_CODE@
+contrib_code ?= @ENABLE_CONTRIB@
 enable_xen ?= @ENABLE_XEN@
 without_gnbd-kernel/src ?= @DISABLE_GNBDKERNEL@
 without_gfs-kernel/src/gfs ?= @DISABLE_GFSKERNEL@


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2008-08-20 13:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-20 19:26 master - build: create contrib/ top level section Andrew Price

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).