[Ocfs2-tools-commits] manish commits r799 - in trunk/ocfs2console: . ocfs2interface

svn-commits at oss.oracle.com svn-commits at oss.oracle.com
Tue Apr 12 21:02:33 CDT 2005


Author: manish
Date: 2005-04-12 21:02:31 -0500 (Tue, 12 Apr 2005)
New Revision: 799

Added:
   trunk/ocfs2console/ocfs2interface/bosa.py
   trunk/ocfs2console/ocfs2interface/classlabel.py
   trunk/ocfs2console/ocfs2interface/fstab.py
   trunk/ocfs2console/ocfs2interface/gidlemodule.c
   trunk/ocfs2console/ocfs2interface/ls.py
   trunk/ocfs2console/ocfs2interface/nodeconfig.py
   trunk/ocfs2console/ocfs2interface/o2cbmodule.c
   trunk/ocfs2console/ocfs2interface/plistmodule.c
   trunk/ocfs2console/ocfs2interface/pushconfig.py
Removed:
   trunk/ocfs2console/ocfs2interface/browser.py
   trunk/ocfs2console/ocfs2interface/clconfig.py
Modified:
   trunk/ocfs2console/ocfs2console
   trunk/ocfs2console/ocfs2interface/
   trunk/ocfs2console/ocfs2interface/Makefile
   trunk/ocfs2console/ocfs2interface/about.py
   trunk/ocfs2console/ocfs2interface/console.py
   trunk/ocfs2console/ocfs2interface/format.py
   trunk/ocfs2console/ocfs2interface/general.py
   trunk/ocfs2console/ocfs2interface/guiutil.py
   trunk/ocfs2console/ocfs2interface/menu.py
   trunk/ocfs2console/ocfs2interface/mount.py
   trunk/ocfs2console/ocfs2interface/ocfs2module.c
   trunk/ocfs2console/ocfs2interface/partitionview.py
   trunk/ocfs2console/ocfs2interface/tune.py
Log:
Massive ocfs2console refactoring


Modified: trunk/ocfs2console/ocfs2console
===================================================================
--- trunk/ocfs2console/ocfs2console	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2console	2005-04-13 02:02:31 UTC (rev 799)
@@ -4,7 +4,7 @@
 #!/usr/bin/python -W ignore::DeprecationWarning
 
 from ocfs2interface.about import process_args
-clusterconf = process_args()
+nodeconf = process_args()
 
 try:
     import gtk
@@ -14,9 +14,9 @@
 Make sure a proper setup for your display environment exists.\n'''
     sys.exit(1)
 
-if clusterconf:
-    from ocfs2interface.clconfig import cluster_configurator
-    cluster_configurator()
+if nodeconf:
+    from ocfs2interface.nodeconfig import node_config
+    node_config()
 else:
     from ocfs2interface.console import main
     main()


Property changes on: trunk/ocfs2console/ocfs2interface
___________________________________________________________________
Name: svn:ignore
   - *.d
*.pyc
*.pyo
ocfs2module.so

   + *.d
*.pyc
*.pyo
plistmodule.so
gidlemodule.so
ocfs2module.so
o2cbmodule.so


Modified: trunk/ocfs2console/ocfs2interface/Makefile
===================================================================
--- trunk/ocfs2console/ocfs2interface/Makefile	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/Makefile	2005-04-13 02:02:31 UTC (rev 799)
@@ -11,6 +11,8 @@
 
 CFLAGS = $(OPTS) $(WARNINGS) -fPIC
 
+PYMOD_CFLAGS = $(CFLAGS) -fno-strict-aliasing $(PYTHON_INCLUDES)
+
 LIBOCFS2_CFLAGS = -I$(TOPDIR)/libocfs2/include
 LIBOCFS2_LIBS = -L$(TOPDIR)/libocfs2 -locfs2
 LIBOCFS2_DEPS = $(TOPDIR)/libocfs2/libocfs2.a
@@ -32,55 +34,85 @@
 BLKID_LIBS = -L$(TOPDIR)/ocfs2console/blkid -lblkid-internal $(UUID_LIBS)
 endif
 
-DEFINES = -DOCFS2_FLAT_INCLUDES -DO2DLM_FLAT_INCLUDES -DO2CB_FLAT_INCLUDES -DG_DISABLE_DEPRECATED
-INCLUDES = $(LIBOCFS2_CFLAGS) $(LIBO2DLM_CFLAGS) $(LIBO2CB_CFLAGS) $(BLKID_CFLAGS) $(PYTHON_INCLUDES) $(GLIB_CFLAGS)
+O2CB_CPPFLAGS = -DO2CB_FLAT_INCLUDES $(LIBO2CB_CFLAGS)
+OCFS2_CPPFLAGS = -DOCFS2_FLAT_INCLUDES -DO2DLM_FLAT_INCLUDES $(LIBOCFS2_CFLAGS) $(LIBO2DLM_CFLAGS) $(O2CB_CPPFLAGS)
 
+GLIB_CPPFLAGS = $(GLIB_CFLAGS) -DG_DISABLE_DEPRECATED
+
 OPTIMIZE = -O2
 OPTIMIZE = -O0
 
 CFLAGS += $(OPTIMIZE)
 
-CBITS = \
-	ocfsplist.c
+PLIST_CBITS = ocfsplist.c
+PLIST_CFILES = $(PLIST_CBITS) plistmodule.c
+PLIST_HFILES = $(subst .c,.h,$(PLIST_CBITS))
 
-HBITS = $(subst .c,.h,$(CBITS))
+ocfsplist_CPPFLAGS = $(OCFS2_CPPFLAGS) $(GLIB_CPPFLAGS) $(BLKID_CFLAGS)
+plistmodule_CPPFLAGS = $(GLIB_CPPFLAGS)
+plistmodule_CFLAGS = $(PYMOD_CFLAGS)
 
-CFILES = $(CBITS) ocfs2module.c
-HFILES = $(HBITS)
+GIDLE_CFILES = gidlemodule.c
+gidlemodule_CPPFLAGS = $(GLIB_CPPFLAGS)
+gidlemodule_CFLAGS = $(PYMOD_CFLAGS)
 
-OBJS = $(subst .c,.o,$(CFILES))
+O2CB_CFILES = o2cbmodule.c
+o2cbmodule_CPPFLAGS = $(O2CB_CPPFLAGS)
+o2cbmodule_CFLAGS = $(PYMOD_CFLAGS)
 
-LIBRARIES = ocfs2module.so
+OCFS2_CFILES = ocfs2module.c
+ocfs2module_CPPFLAGS = $(OCFS2_CPPFLAGS)
+ocfs2module_CFLAGS = $(PYMOD_CFLAGS)
 
+PLIST_OBJS = $(subst .c,.o,$(PLIST_CFILES))
+GIDLE_OBJS = $(subst .c,.o,$(GIDLE_CFILES))
+OCFS2_OBJS = $(subst .c,.o,$(OCFS2_CFILES))
+O2CB_OBJS = $(subst .c,.o,$(O2CB_CFILES))
+
+LIBRARIES = plistmodule.so gidlemodule.so ocfs2module.so o2cbmodule.so
+
 PYSRC = \
-	__init__.py \
-	about.py \
-	browser.py \
-	clconfig.py \
-	console.py \
-	format.py \
-	fsck.py \
-	fswidgets.py \
-	general.py \
-	guiutil.py \
-	ipwidget.py \
-	mount.py \
-	menu.py \
-	partitionview.py \
-	process.py \
-	terminal.py \
-	toolbar.py \
+	__init__.py		\
+	about.py		\
+	bosa.py			\
+	classlabel.py		\
+	console.py		\
+	format.py		\
+	fsck.py			\
+	fstab.py		\
+	fswidgets.py		\
+	general.py		\
+	guiutil.py		\
+	ipwidget.py		\
+	ls.py			\
+	mount.py		\
+	menu.py			\
+	nodeconfig.py		\
+	partitionview.py	\
+	process.py		\
+	pushconfig.py		\
+	terminal.py		\
+	toolbar.py		\
 	tune.py
 
 PYLIB = $(LIBRARIES) $(PYSRC)
 
 INSTALL_RULES = install-pylib
 
-DIST_FILES = $(CFILES) $(HFILES) $(PYSRC)
+DIST_FILES = $(PLIST_CFILES) $(PLIST_HFILES) $(GIDLE_CFILES) $(OCFS2_CFILES) $(O2CB_CFILES) $(PYSRC)
 
-ocfs2module.so: $(OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS)  $(LIBO2CB_DEPS) $(BLKID_DEPS)
+plistmodule.so: $(PLIST_OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS) $(BLKID_DEPS)
 	$(LINK) -shared $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(BLKID_LIBS) $(COM_ERR_LIBS) $(GLIB_LIBS)
 
+gidlemodule.so: $(GIDLE_OBJS)
+	$(LINK) -shared $(GLIB_LIBS)
+
+ocfs2module.so: $(OCFS2_OBJS) $(LIBOCFS2_DEPS) $(LIBO2DLM_DEPS) $(LIBO2CB_DEPS)
+	$(LINK) -shared $(LIBOCFS2_LIBS) $(LIBO2DLM_LIBS) $(LIBO2CB_LIBS) $(COM_ERR_LIBS) $(UUID_LIBS)
+
+o2cbmodule.so: $(O2CB_OBJS) $(LIBO2CB_DEPS)
+	$(LINK) -shared $(LIBO2CB_LIBS) $(COM_ERR_LIBS)
+
 install-pylib:
 	$(SHELL) $(TOPDIR)/mkinstalldirs $(DESTDIR)$(pyexecdir)/ocfs2interface
 	for f in $(PYLIB); do \

Modified: trunk/ocfs2console/ocfs2interface/about.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/about.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/about.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -25,12 +25,12 @@
 def print_usage(name):
     print '''Usage: %s [OPTION]...
 Options:
-  -C, --clusterconf  run cluster configurator only
+  -N, --node-config  run node configurator only
   -V, --version      print version information and exit
       --help         display this help and exit''' % name
 
 def process_args():
-    clusterconf = False
+    nodeconf = False
 
     for arg in sys.argv[1:]:
         if arg in ('--version', '-V'):
@@ -39,10 +39,10 @@
         elif arg in ('--help',):
             print_usage(sys.argv[0])
             sys.exit(0)
-        elif arg in ('--clusterconf', '-C'):
-            clusterconf = True
+        elif arg in ('--node-config', '-N'):
+            nodeconf = True
 
-    return clusterconf
+    return nodeconf
 
 def process_gui_args():
     if len(sys.argv) > 1 and sys.argv[1] not in ('--clusterconf', '-C'):

Added: trunk/ocfs2console/ocfs2interface/bosa.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/bosa.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/bosa.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,223 @@
+# OCFS2Console - GUI frontend for OCFS2 management and debugging
+# Copyright (C) 2002, 2005 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
+
+import gtk
+import gobject
+import pango
+
+import ocfs2
+
+from guiutil import set_props
+
+from ls import fields
+
+INFO_LABEL_FONT = pango.FontDescription('monospace')
+
+(
+    COLUMN_NAME,
+    COLUMN_INFO_OBJECT,
+    COLUMN_ICON,
+    COLUMN_ITALIC
+) = range(4)
+
+sample = ('-rw-r--r--', '1', 'manish', 'manish', '133194', '262144', 'Sep 29 12:46', 'closobo.c')
+
+STOCK_LOADING = gtk.STOCK_REFRESH
+STOCK_EMPTY = gtk.STOCK_STOP
+STOCK_ERROR = gtk.STOCK_DIALOG_ERROR
+
+try:
+    STOCK_FILE = gtk.STOCK_FILE
+except AttributeError:
+    STOCK_FILE = gtk.STOCK_NEW
+
+try:
+    STOCK_DIRECTORY = gtk.STOCK_DIRECTORY
+except AttributeError:
+    STOCK_DIRECTORY = gtk.STOCK_OPEN
+
+class InfoLabel(gtk.Label):
+    def __init__(self, field_type):
+        gtk.Label.__init__(self)
+
+        self.field_type = field_type
+
+        if field_type.right_justify:
+             set_props(self, xalign=1.0)
+        else:
+             set_props(self, xalign=0.0)
+
+        self.modify_font(INFO_LABEL_FONT)
+
+    def update(self, dentry, dinode):
+        field = self.field_type(dentry, dinode)
+        self.set_text(field.text)
+
+class Browser(gtk.VBox):
+    def __init__(self, device=None):
+        gtk.VBox.__init__(self, spacing=4)
+
+        label = gtk.Label('/')
+        set_props(label, xalign=0.0)
+        self.pack_start(label, expand=False)
+
+        self.path_label = label
+
+        self.scrl_win = gtk.ScrolledWindow()
+        self.scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.add(self.scrl_win)
+
+        self.store = gtk.TreeStore(str, gobject.TYPE_PYOBJECT,
+                                   str, gobject.TYPE_BOOLEAN)
+
+        self.make_file_view()
+
+        self.make_ls_fields()
+
+        self.fs = None
+
+        if device:
+            try:
+                self.fs = ocfs2.Filesystem(device)
+            except ocfs2.error:
+                self.make_error_node()
+        else:
+            self.make_empty_node()
+
+        self.connect('destroy', self.destroy)
+
+    def make_file_view(self):
+        tv = gtk.TreeView(self.store)
+        self.scrl_win.add(tv)
+
+        set_props(tv, headers_visible=False,
+                      rules_hint=True)
+
+        column = gtk.TreeViewColumn()
+
+        renderer = gtk.CellRendererPixbuf()
+        column.pack_start(renderer, expand=False)
+        column.set_attributes(renderer, stock_id=COLUMN_ICON)
+
+        renderer = gtk.CellRendererText()
+        renderer.set_property('style', pango.STYLE_ITALIC)
+        column.pack_start(renderer, expand=True)
+        column.set_attributes(renderer, text=COLUMN_NAME,
+                                        style_set=COLUMN_ITALIC)
+
+        tv.append_column(column)
+
+        #tv.connect('test_expand_row', self.test_expand_row)
+        #tv.connect('test_collapse_row', self.test_collapse_row)
+        #tv.connect('row_activated', self.row_activated)
+
+        #sel = tv.get_selection()
+        #sel.connect('changed', self.select)
+
+    def make_ls_fields(self):
+        table = gtk.Table(rows=2, columns=7)
+        set_props(table, row_spacing=4,
+                         column_spacing=4,
+                         border_width=4)
+        self.pack_end(table, expand=False, fill=False)
+
+        self.info_labels = []
+
+        column = 0
+
+        for field in fields:
+            label = gtk.Label(field.label)
+            set_props(label, xalign=0.0)
+            table.attach(label, column, column + 1, 0, 1)
+
+            label = InfoLabel(field)
+            table.attach(label, column, column + 1, 1, 2)
+
+            self.info_labels.append(label)
+
+            column += 1
+
+    def destroy(self, obj):
+        pass
+
+    def make_dentry_node(self, dentry, stock_id, parent=None):
+        self.store.append(parent, (dentry.name, dentry, stock_id, False))
+
+    def make_file_node(self, dentry, parent=None):
+        self.make_dentry_node(dentry, STOCK_FILE, parent)
+
+    def make_dir_node(self, dentry, parent=None):
+        iter = self.make_dentry_node(dentry, STOCK_DIRECTORY, parent)
+        self.store_append(iter, ('.', dentry, None, False))
+
+    def make_loading_node(self, parent=None):
+        self.store.append(parent, ('Loading...', None, STOCK_LOADING, True))
+
+    def make_empty_node(self, parent=None):
+        self.store.append(parent, ('Empty', None, STOCK_EMPTY, True))
+
+    def make_error_node(self, parent=None):
+        self.store.append(parent, ('Error', None, STOCK_ERROR, True))
+
+    def refresh(self):
+        pass
+        
+    def add_level(self, dentry=None, parent=None):
+        if parent:
+            iter = self.store.iter_children(parent)
+
+            name = self.store[iter][COLUMN_NAME]
+            if name != '.':
+                return
+
+            del self.store[iter]
+
+        try:
+            level = TreeLevel(self, dentry)
+        except ocfs2.error:
+            self.make_erro_node(parent)
+            return
+
+        self.make_loading_node(parent)
+
+        
+        #self.levels.append(TreeLevel(self, dentry))
+
+class TreeLevel:
+    def __init__(self, browser, dentry=None):
+        self.dentry = dentry
+        self.browser = browser
+        self.diriter = dentry.fs.iterdir(dentry)
+        
+def main():
+    import sys
+
+    def dummy(*args):
+        gtk.main_quit()
+
+    window = gtk.Window()
+    window.connect('delete_event', dummy)
+
+    browser = Browser(sys.argv[1])
+    window.add(browser)
+
+    window.show_all()
+
+    gtk.main()
+
+if __name__ == '__main__':
+    main()

Deleted: trunk/ocfs2console/ocfs2interface/browser.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/browser.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/browser.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -1,82 +0,0 @@
-# OCFS2Console - GUI frontend for OCFS2 management and debugging
-# Copyright (C) 2002, 2005 Oracle.  All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
-
-import gtk
-import pango
-
-import ocfs2
-
-from guiutil import set_props
-
-fields = ('Perms', '# Links', 'Owner', 'Group', 'Size', 'Alloc Size',
-          'Timestamp', 'Name')
-sample = ('-rw-r--r--', '1', 'manish', 'manish', '133194', '262144', 'Sep 29 12:46', 'closobo.c')
-
-class Browser:
-     def __init__(self, device=None):
-         self.widget = gtk.VBox(spacing=4)
-
-         scrl_win = gtk.ScrolledWindow()
-         scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-         self.widget.add(scrl_win)
-
-         tv = gtk.TreeView()
-         scrl_win.add(tv)
-
-         tv.insert_column_with_attributes(-1, 'File', gtk.CellRendererText(),
-                                          text=0)
-
-         table = gtk.Table(rows=2, columns=7)
-         set_props(table, row_spacing=4,
-                          column_spacing=4,
-                          border_width=4)
-         self.widget.pack_end(table, expand=False, fill=False)
-
-         font = pango.FontDescription('Monospace')
-
-         for i in range(0, len(fields)):
-             label = gtk.Label(fields[i])
-             set_props(label, xalign=0.0)
-             table.attach(label, i, i + 1, 0, 1)
-
-             label = gtk.Label(sample[i])
-             if i == 1 or i == 4 or i == 5:
-                 set_props(label, xalign=1.0)
-             else:
-                 set_props(label, xalign=0.0)
-             table.attach(label, i, i + 1, 1, 2)
-
-             label.modify_font(font)
-
-def main():
-    import sys
-
-    def dummy(*args):
-        gtk.main_quit()
-
-    window = gtk.Window()
-    window.connect('delete_event', dummy)
-
-    browser = Browser(sys.argv[1]).widget
-    window.add(browser)
-
-    window.show_all()
-
-    gtk.main()
-
-if __name__ == '__main__':
-    main()

Added: trunk/ocfs2console/ocfs2interface/classlabel.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/classlabel.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/classlabel.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,38 @@
+# OCFS2Console - GUI frontend for OCFS2 management and debugging
+# Copyright (C) 2005 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
+
+import re
+
+caps = re.compile('(?!\A)[A-Z][a-z]')
+
+def make_title(m):
+    return ' ' + m.group()
+
+class class_label(object):
+    def __get__(self, obj, cls):
+        return caps.sub(make_title, cls.__name__)
+
+class_label = class_label()
+
+def main():
+    import sys
+
+    cls = type(sys.argv[1], (), {'label': class_label})
+    print cls.label
+
+if __name__ == '__main__':
+    main()

Deleted: trunk/ocfs2console/ocfs2interface/clconfig.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/clconfig.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/clconfig.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -1,536 +0,0 @@
-# OCFS2Console - GUI frontend for OCFS2 management and debugging
-# Copyright (C) 2002, 2005 Oracle.  All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
-
-import os
-import types
-
-import gtk
-import gobject
-import pango
-
-from cStringIO import StringIO
- 
-import ocfs2
-
-from guiutil import Dialog, set_props, error_box
-from process import Process
-from ipwidget import IPEditor, IPMissing, IPError
-
-CLUSTER_NAME = 'ocfs2'
-CONFIG_FS_PATH = '/config/cluster'
-
-O2CB_INIT = '/etc/init.d/o2cb'
-O2CB_CTL = 'o2cb_ctl'
-
-PORT_DEFAULT = 7777
-PORT_MINIMUM = 1000
-PORT_MAXIMUM = 30000
-
-(
-    COLUMN_NEW_NODE,
-    COLUMN_NAME,
-    COLUMN_NODE,
-    COLUMN_IP_ADDR,
-    COLUMN_IP_PORT
-) = range(5)
-
-fields = (
-    (COLUMN_NEW_NODE, 'Active',     None,           bool),
-    (COLUMN_NAME,     'Name',       gtk.Entry,      str),
-    (COLUMN_NODE,     'Node',       None,           int),
-    (COLUMN_IP_ADDR,  'IP Address', IPEditor,       str),
-    (COLUMN_IP_PORT,  'IP Port',    gtk.SpinButton, str)
-)
-
-typemap = { bool: gobject.TYPE_BOOLEAN }
-
-class ConfigError(Exception):
-    pass
-
-class ClusterConfig(Dialog):
-    def __init__(self, parent=None):
-        self.new_nodes = 0
-
-        Dialog.__init__(self, parent=parent, title='Cluster Configurator',
-                        buttons=(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
-                                 gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
-
-        if parent is None:
-            self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
-
-        frame = gtk.Frame()
-        frame.set_shadow_type(gtk.SHADOW_NONE)
-        self.vbox.add(frame)
-
-        label = gtk.Label()
-        label.set_text_with_mnemonic('_Nodes:')
-        frame.set_label_widget(label)
-
-        hbox = gtk.HBox(spacing=4)
-        hbox.set_border_width(4)
-        frame.add(hbox)
-
-        scrl_win = gtk.ScrolledWindow()     
-        scrl_win.set_policy(hscrollbar_policy=gtk.POLICY_AUTOMATIC,
-                            vscrollbar_policy=gtk.POLICY_AUTOMATIC)
-        hbox.pack_start(scrl_win)
-
-        self.setup_treeview()
-        scrl_win.add(self.tv)
-
-        vbbox = gtk.VButtonBox()
-        set_props(vbbox, layout_style=gtk.BUTTONBOX_START,
-                         spacing=5,
-                         border_width=5)
-        hbox.pack_end(vbbox, expand=False, fill=False)
-
-        self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
-        self.add_button.connect('clicked', self.add_node)
-        vbbox.add(self.add_button)
-
-        try:
-            edit_construct_args = {'stock': gtk.STOCK_EDIT}
-        except AttributeError:
-            edit_construct_args = {'label': '_Edit'}
-
-        self.edit_button = gtk.Button(**edit_construct_args)
-        self.edit_button.connect('clicked', self.edit_node)
-        vbbox.add(self.edit_button)
-        
-        self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE)
-        self.remove_button.connect('clicked', self.remove_node)
-        vbbox.add(self.remove_button)
-
-        self.can_edit(False)
-        self.can_apply(False)
- 
-        self.sel = self.tv.get_selection()
-        self.sel.connect('changed', self.on_select)
-
-    def load_cluster_state(self):
-        query_args = '-I -t node -o'
-        o2cb_ctl = O2CBCtl(query_args, 'Querying nodes...', self)
-        success, output, k = o2cb_ctl.reap()
-
-        if not success:
-            raise ConfError, output
-
-        self.store = gtk.ListStore(*[typemap.get(f[3], f[3]) for f in fields])
-
-        def node_compare(store, a, b):
-            n1 = store[a][COLUMN_NODE]
-            n2 = store[b][COLUMN_NODE]
-
-            if n1 < 0 and n2 >= 0:
-                return 1
-            elif n1 >= 0 and n2 < 0:
-                return -1
-            else:
-                return cmp(abs(n1), abs(n2))
-              
-        self.store.set_sort_func(COLUMN_NODE, node_compare)
-        self.store.set_sort_column_id(COLUMN_NODE, gtk.SORT_ASCENDING)
-
-        buffer = StringIO(output)
-
-        for line in buffer:
-            if line.startswith('#'):
-                continue
-
-            data = list((None,) * len(fields))
-
-            try:
-                data[COLUMN_NEW_NODE] = False
-
-                (data[COLUMN_NAME],
-                 cluster,
-                 data[COLUMN_NODE],
-                 data[COLUMN_IP_ADDR],
-                 data[COLUMN_IP_PORT],
-                 state) = line.split(':')
-
-                for i in range(0, len(fields)):
-                    data[i] = fields[i][3](data[i])
-            except ValueError:
-                continue
-
-            if cluster == CLUSTER_NAME:
-                self.store.append(data)
-
-        self.new_nodes = 0
-
-        self.tv.set_model(self.store)
-        self.sel.select_iter(self.store.get_iter_first())
-
-    def setup_treeview(self):
-        self.tv = gtk.TreeView()
-        self.tv.set_size_request(350, 200)
-
-        for col, title, widget_type, field_type in fields:
-            if col == COLUMN_NEW_NODE:
-                self.tv.insert_column_with_data_func(-1, title,
-                                                     gtk.CellRendererPixbuf(),
-                                                     self.active_set_func)
-            elif col == COLUMN_NODE:
-                self.tv.insert_column_with_data_func(-1, title,
-                                                     gtk.CellRendererText(),
-                                                     self.node_set_func)
-            else:
-                cell_renderer = gtk.CellRendererText()
-                cell_renderer.set_property('style', pango.STYLE_ITALIC)
-
-                self.tv.insert_column_with_attributes(-1, title,
-                                                      cell_renderer,
-                                                      text=col,
-                                                      style_set=COLUMN_NEW_NODE)
-
-    def active_set_func(self, tree_column, cell, model, iter):
-        if model[iter][COLUMN_NEW_NODE]:
-            stock_id = None
-        else:
-            stock_id = gtk.STOCK_EXECUTE
-
-        cell.set_property('stock_id', stock_id)
-
-    def node_set_func(self, tree_column, cell, model, iter):
-        if model[iter][COLUMN_NEW_NODE]:
-            text = ''
-        else:
-            text = str(model[iter][COLUMN_NODE])
-
-        cell.set_property('text', text)
-
-    def on_select(self, sel):
-        store, iter = sel.get_selected()
-
-        if iter:
-            editable = store[iter][COLUMN_NEW_NODE]
-        else:
-            editable = False
-
-        self.can_edit(editable)
-
-    def can_edit(self, state):
-        self.edit_button.set_sensitive(state)
-        self.remove_button.set_sensitive(state)
-
-    def can_apply(self, state):
-        self.set_response_sensitive(gtk.RESPONSE_APPLY, state)
-            
-    def add_node(self, b):
-        if len(self.store) == ocfs2.MAX_NODES:
-            error_box(self, 'Cannot have more than %d nodes in a cluster' %
-                            ocfs2.MAX_NODES)
-            return
-
-        node_attrs = self.node_query(title='Add Node')
-
-        if node_attrs is None:
-            return
-
-        self.new_nodes += 1
-
-        name, ip_addr, ip_port = node_attrs
-
-        iter = self.store.append((True, name, -self.new_nodes,
-                                  ip_addr, ip_port))
-        self.sel.select_iter(iter)
-
-        self.can_apply(True)
-
-    def edit_node(self, b):
-        store, iter = self.sel.get_selected()
-        attrs = store[iter]
-
-        node_attrs = self.node_query(title='Edit Node', defaults=attrs)
-
-        if node_attrs is None:
-            return
-
-        (attrs[COLUMN_NAME],
-         attrs[COLUMN_IP_ADDR],
-         attrs[COLUMN_IP_PORT]) = node_attrs
-
-    def remove_node(self, b):
-        store, iter = self.sel.get_selected()
-
-        msg = ('Are you sure you want to delete node %s?' %
-               store[iter][COLUMN_NAME])
-
-        ask = gtk.MessageDialog(parent=self,
-                                flags=gtk.DIALOG_DESTROY_WITH_PARENT,
-                                type=gtk.MESSAGE_QUESTION,
-                                buttons=gtk.BUTTONS_YES_NO,
-                                message_format=msg)
-
-        response = ask.run()
-        ask.destroy()
-
-        if response == gtk.RESPONSE_YES:
-            del store[iter]
-            self.new_nodes -= 1
-
-            if self.new_nodes == 0:
-                self.can_apply(False)
-
-            self.sel.select_iter(self.store.get_iter_first())
-
-    def new_node_attrs(self):
-        attrs = []
-
-        for row in self.store:
-            if row[COLUMN_NEW_NODE]:
-                attrs.append((row[COLUMN_NAME],
-                              row[COLUMN_IP_ADDR],
-                              row[COLUMN_IP_PORT]))
-
-        return attrs
-
-    def apply_changes(self):
-        success = False
-
-        for name, ip_addr, ip_port in self.new_node_attrs():
-            add_node_args = ('-C', '-n', name, '-t', 'node',
-                             '-a', 'cluster=%s' % CLUSTER_NAME,
-                             '-a', 'ip_address=%s' % ip_addr,
-                             '-a', 'ip_port=%s' % ip_port,
-                             '-i')
-
-            o2cb_ctl = O2CBCtl(add_node_args, 'Adding node %s...' % name, self)
-            success, output, k = o2cb_ctl.reap()
-
-            if not success:
-                error_box(self, '%s\nCould not add node %s' % (output, name))
-                break
-
-        self.load_cluster_state()
-        return success
-
-    def node_query(self, title='Node Attributes', defaults=None):
-        existing_names = {}
-        existing_ip_addrs = {}
-
-        for row in self.store:
-            name = row[COLUMN_NAME]
-            ip_addr = row[COLUMN_IP_ADDR]
-
-            existing_names[name] = 1
-            existing_ip_addrs[ip_addr] = name
-
-        dialog = Dialog(parent=self, title=title,
-                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                                 gtk.STOCK_OK,     gtk.RESPONSE_OK))
-
-        dialog.set_alternative_button_order((gtk.RESPONSE_OK,
-                                             gtk.RESPONSE_CANCEL))
-
-        dialog.set_default_response(gtk.RESPONSE_OK)
-
-        table = gtk.Table(rows=4, columns=2)
-        set_props(table, row_spacing=4,
-                         column_spacing=4,
-                         border_width=4,
-                         parent=dialog.vbox)
-
-        widgets = []
-        row = 0
-
-        for col, title, widget_type, field_type in fields:
-            if widget_type is None:
-                widgets.append(None)
-                continue
-
-            label = gtk.Label(title + ':')
-            set_props(label, xalign=1.0)
-            table.attach(label, 0, 1, row, row + 1)
-
-            widget = widget_type()
-            table.attach(widget, 1, 2, row, row + 1)
-
-            if isinstance(widget, gtk.SpinButton):
-                widget.set_numeric(True)
-
-                adjustment = gtk.Adjustment(PORT_DEFAULT,
-                                            PORT_MINIMUM, PORT_MAXIMUM,
-                                            1, 100) 
-
-                widget.set_adjustment(adjustment)
-                widget.set_value(PORT_DEFAULT)
-
-            widgets.append(widget)
-
-            row = row + 1
-
-        if defaults:
-            for w, d in zip(widgets, defaults):
-                if w and d:
-                    w.set_text(d)
-
-        dialog.show_all()
-
-        while 1:
-            if dialog.run() != gtk.RESPONSE_OK:
-                dialog.destroy()
-                return None
-
-            ip_port = widgets[COLUMN_IP_PORT].get_text()
-
-            name = widgets[COLUMN_NAME].get_text()
-
-            if not name:
-                error_box(dialog, 'Node name not specified')
-                continue
-
-            try:
-                ip_addr = widgets[COLUMN_IP_ADDR].get_text()
-            except (IPMissing, IPError), msg:
-                error_box(dialog, msg[0])
-                continue
-
-            if name in existing_names:
-                error_box(dialog,
-                          'Node %s already exists in the configuration' % name)
-            elif ip_addr in existing_ip_addrs:
-                error_box(dialog,
-                          'IP %s is already assigned to node %s' %
-                          (ip_addr, existing_ip_addrs[ip_addr]))
-            else:
-                break
-
-        dialog.destroy()
-
-        return name, ip_addr, ip_port
-
-    def run(self):
-        self.show_all()
-
-        while 1:
-            if Dialog.run(self) == gtk.RESPONSE_APPLY:
-                self.apply_changes()
-            elif len(self.new_node_attrs()):
-                msg = ('New nodes have been created, but they have not been '
-                       'applied to the cluster configuration. Do you want to '
-                       'apply the changes now?')
-
-                ask = gtk.MessageDialog(parent=self,
-                                        flags=gtk.DIALOG_DESTROY_WITH_PARENT,
-                                        type=gtk.MESSAGE_QUESTION,
-                                        buttons=gtk.BUTTONS_YES_NO,
-                                        message_format=msg)
-
-                if ask.run() == gtk.RESPONSE_NO:
-                    break
-                elif self.apply_changes():
-                    break
-            else: 
-                break
-
-class O2CBProcess(Process):
-    def __init__(self, args, desc, parent=None):
-        if isinstance(args, types.StringTypes):
-            command = '%s %s' % (self.o2cb_program, args)
-        else:
-            command = (self.o2cb_program,) + tuple(args)
-
-        Process.__init__(self, command, self.o2cb_title, desc, parent)
-
-class O2CBCtl(O2CBProcess):
-    o2cb_program = O2CB_CTL
-    o2cb_title = 'Cluster Control'
-
-class O2CBInit(O2CBProcess):
-    o2cb_program = O2CB_INIT
-    o2cb_title = 'Cluster Stack'
-
-def cluster_configurator(parent=None):
-    if not os.access(CONFIG_FS_PATH, os.F_OK):
-        load_args = ('load',)
-        o2cb_init = O2CBInit(load_args, 'Starting cluster stack...', parent)
-        success, output, k = o2cb_init.reap()
-
-        if success:
-            msg_type = gtk.MESSAGE_INFO
-            msg = ('The cluster stack has been started. It needs to be '
-                   'running for any clustering functionality to happen. '
-                   'Please run "%s enable" to have it started upon bootup.'
-                   % o2cb_init.o2cb_program)
-        else:
-            msg_type = gtk.MESSAGE_WARNING
-            msg = ('Could not start cluster stack. This must be resolved '
-                   'before any OCFS2 filesystem can be mounted')
-
-        info = gtk.MessageDialog(parent=parent,
-                                 flags=gtk.DIALOG_DESTROY_WITH_PARENT,
-                                 type=msg_type,
-                                 buttons=gtk.BUTTONS_CLOSE,
-                                 message_format=msg)
-
-        info.run()
-        info.destroy()
-
-        if not success:
-            return
-
-    query_args = '-I -t cluster -n %s -o' % CLUSTER_NAME
-    o2cb_ctl = O2CBCtl(query_args, 'Querying cluster...', parent)
-    success, output, k = o2cb_ctl.reap()
-
-    if not success:
-        create_args = '-C -n %s -t cluster -i' % CLUSTER_NAME
-        o2cb_ctl = O2CBCtl(query_args, 'Creating cluster...', parent)
-        success, output, k = o2cb_ctl.reap()
-
-        if not success:
-            error_box(parent, '%s\nCould not create cluster' % output)
-            return
-
-    conf = ClusterConfig(parent)
-
-    try:
-        conf.load_cluster_state()
-    except ConfigError, e:
-        error_box(parent, '%s: Could not query cluster configuration' % str(e))
-        return
-
-    conf.run()
-    conf.destroy()
-
-    if not os.access(os.path.join(CONFIG_FS_PATH, CLUSTER_NAME), os.F_OK):
-        online_args = ('online', CLUSTER_NAME),
-        o2cb_init = O2CBInit(online_args, 'Starting OCFS2 cluster...', parent)
-        success, output, k = o2cb_init.reap()
-
-        if not success:
-            msg = ('Could not bring OCFS2 cluster online. This must be '
-                   'resolved before any OCFS2 filesystem can be mounted')
-
-            info = gtk.MessageDialog(parent=parent,
-                                     flags=gtk.DIALOG_DESTROY_WITH_PARENT,
-                                     type=gtk.MESSAGE_WARNING,
-                                     buttons=gtk.BUTTONS_CLOSE,
-                                     message_format=msg)
-
-            info.run()
-            info.destroy()
-
-def main():
-    from about import process_gui_args
-    process_gui_args()
-    cluster_configurator()
-
-if __name__ == '__main__':
-    main()

Modified: trunk/ocfs2console/ocfs2interface/console.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/console.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/console.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -17,7 +17,7 @@
 
 import gtk
 
-from guiutil import set_props, error_box, query_text
+from guiutil import set_props, error_box
 
 from partitionview import PartitionView
 from menu import Menu
@@ -25,11 +25,12 @@
 from about import about, process_gui_args
 from mount import mount, unmount
 from format import format_partition
+from fsck import fsck_volume
 from tune import tune_label, tune_nodes
 from general import General
-from browser import Browser
-from clconfig import cluster_configurator
-from fsck import fsck_volume
+from bosa import Browser
+from nodeconfig import node_config
+from pushconfig import push_config
 
 info_items = (
     ('General',          General),
@@ -136,9 +137,12 @@
     def repair(self):
         fsck_volume(self, self.pv.get_device(), check=False)
 
-    def clconfig(self):
-        cluster_configurator(self)
+    def node_config(self):
+        node_config(self)
 
+    def push_config(self):
+        push_config(self)
+
 def main():
     from about import process_gui_args
     process_gui_args()

Modified: trunk/ocfs2console/ocfs2interface/format.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/format.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/format.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -17,7 +17,7 @@
 
 import gtk
 
-import ocfs2
+from plist import partition_list
 
 from guiutil import Dialog, set_props, error_box, format_bytes
 from process import Process
@@ -48,7 +48,7 @@
     def add_partition(device, fstype):
         partitions.append((device, fstype))
 
-    ocfs2.partition_list(add_partition, unmounted=True)
+    partition_list(add_partition, unmounted=True)
 
     if not partitions:
         error_box(parent, 'No unmounted partitions')

Added: trunk/ocfs2console/ocfs2interface/fstab.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/fstab.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/fstab.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,100 @@
+# OCFS2Console - GUI frontend for OCFS2 management and debugging
+# Copyright (C) 2005 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
+
+PATH_FSTAB = '/etc/fstab'
+
+class FSTab:
+    def __init__(self):
+        self.refresh()
+
+    def refresh(self):
+        self.entries = []
+
+        try:
+            fstab_file = open(PATH_FSTAB)
+            lines = fstab_file.readlines()
+            fstab_file.close()
+        except IOError:
+            return
+
+        for line in lines:
+            line = line.strip()
+
+            if line.startswith('#'):
+                continue
+
+            try:
+                entry = FSTabEntry(*line.split())
+            except (ValueError, TypeError):
+                continue
+
+            self.entries.append(entry)
+
+    def get(self, device=None, label=None, uuid=None):
+        valid_specs = {}
+
+        if device:
+            valid_specs[device] = True
+
+        if label:
+            spec = 'LABEL=' + label
+            valid_specs[spec] = True
+
+        if uuid:
+            spec = 'UUID=' + uuid.lower()
+            valid_specs[spec] = True
+
+        for entry in self.entries:
+            if entry.spec in valid_specs:
+                return entry
+
+        return None
+    
+str_fields = ('spec', 'mountpoint', 'vfstype', 'options')
+int_fields = ('freq', 'passno')
+
+entry_fmt = ('\t'.join(['%%(%s)s' % f for f in str_fields]) + '\t' +
+             '\t'.join(['%%(%s)d' % f for f in int_fields]))
+
+class FSTabEntry:
+    def __init__(self, spec, mountpoint, vfstype, options, freq=0, passno=0):
+        symtab = locals()
+
+        for attr in str_fields:
+            setattr(self, attr, symtab[attr])
+
+        for attr in int_fields:
+            setattr(self, attr, int(symtab[attr]))
+
+        if self.spec.startswith('UUID='):
+            self.spec = 'UUID=' + self.spec[5:].lower()
+
+    def __str__(self):
+        return entry_fmt % self.__dict__
+
+    def __repr__(self):
+        return "<FSTabEntry: '%s'>" % str(self)
+
+def main():
+    import sys
+    spec = sys.argv[1]
+
+    fstab = FSTab()
+    print fstab.get(device=spec, label=spec, uuid=spec)
+
+if __name__ == '__main__':
+    main()

Modified: trunk/ocfs2console/ocfs2interface/general.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/general.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/general.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -19,80 +19,117 @@
 
 import ocfs2
 
+from classlabel import class_label
 from guiutil import set_props, format_bytes
 
-fields = (
-    ('Version', 'version'),
-    ('Label', 's_label'),
-    ('UUID', 's_uuid'),
-    ('Maximum Nodes', 's_max_nodes'),
-    ('Cluster Size', 's_clustersize_bits'),
-    ('Block Size', 's_blocksize_bits'),
-    ('Free Space', 'freebits'),
-    ('Total Space', 'numbits'),
-)
+EMPTY_TEXT = 'N/A'
 
-class General:
+class Field(object):
+    def __init__(self, fs, super, dinode):
+        self.fs = fs
+        self.super = super
+        self.dinode = dinode
+
+    def get_text(self):
+        if self.super:
+            return self.real_get_text()
+        else:
+            return EMPTY_TEXT
+
+    text = property(get_text)
+
+    label = class_label
+
+class Version(Field):
+    def real_get_text(self):
+        return '%d.%d' % (self.super.s_major_rev_level,
+                          self.super.s_minor_rev_level)
+
+class Label(Field):
+    def real_get_text(self):
+        text = self.super.s_label
+
+        if not text:
+            text = EMPTY_TEXT
+
+        return text
+
+class UUID(Field):
+    def real_get_text(self):
+        return self.super.uuid_unparsed
+
+class MaximumNodes(Field):
+    def real_get_text(self):
+        return str(self.super.s_max_nodes)
+
+class FSSize(Field):
+    def real_get_text(self):
+        return format_bytes(getattr(self.fs, self.member))
+
+class ClusterSize(FSSize):
+    member = 'fs_clustersize'
+
+class BlockSize(FSSize):
+    member = 'fs_blocksize'
+
+class Space(Field):
+    def real_get_text(self):
+        if self.dinode:
+            block_bits = self.fs.fs_clustersize >> self.super.s_blocksize_bits
+            bytes = self.get_bits() * block_bits * self.fs.fs_blocksize
+            return format_bytes(bytes, show_bytes=True)
+        else:
+            return EMPTY_TEXT
+
+class FreeSpace(Space):
+    def get_bits(self):
+        return self.dinode.i_total - self.dinode.i_used
+
+class TotalSpace(Space):
+    def get_bits(self):
+        return self.dinode.i_total
+
+fields = (Version, Label, UUID, MaximumNodes,
+          ClusterSize, BlockSize,
+          FreeSpace, TotalSpace)
+
+class General(gtk.Table):
     def __init__(self, device=None):
-        self.widget = gtk.Table(rows=5, columns=2)
+        gtk.Table.__init__(self, rows=5, columns=2)
 
-        set_props(self.widget, row_spacing=4,
-                               column_spacing=4,
-                               border_width=4)
+        set_props(self, row_spacing=4,
+                        column_spacing=4,
+                        border_width=4)
 
-        super = None
-        numbits = 0
+        fs = super = dinode = None
 
         if device:
             try:
-                super = ocfs2.get_super(device)
-                numbits, freebits = ocfs2.get_space_usage(device)
+                fs = ocfs2.Filesystem(device)
+                super = fs.fs_super
 
-                clustersize = 1L << super.s_clustersize_bits
-                blocksize = 1L << super.s_blocksize_bits
+                blkno = fs.lookup_system_inode(ocfs2.GLOBAL_BITMAP_SYSTEM_INODE)
+                dinode = fs.read_cached_inode(blkno)
             except ocfs2.error:
                 pass
 
-        self.pos = 0
+        row = 0
 
-        for desc, member in fields:
-            if super:
-                if member == 'version':
-                    val = '%d.%d' % super[0:2]
-                elif member == 's_label':
-                    val = super.s_label
-                    if not val:
-                        val = 'N/A'
-                elif member == 'numbits' or member == 'freebits':
-                    if numbits:
-                        blocks = (vars()[member] * 
-                                  (clustersize >> super.s_blocksize_bits))
-                        val = format_bytes(blocks * blocksize, show_bytes=True)
-                    else:
-                        val = 'N/A'
-                else:
-                    val = getattr(super, member)
+        for field_type in fields:
+            field = field_type(fs, super, dinode)
 
-                    if member.endswith('_bits'):
-                        val = format_bytes(1 << val)
-            else:
-                val = 'N/A'
+            label = gtk.Label(field.label + ':')
+            set_props(label, xalign=1.0)
+            self.attach(label, 0, 1, row, row + 1,
+                        xoptions=gtk.FILL, yoptions=gtk.FILL)
 
-            self.add_field(desc, val)
+            label = gtk.Label(field.text)
+            set_props(label, xalign=0.0)
+            self.attach(label, 1, 2, row, row + 1,
+                        xoptions=gtk.FILL, yoptions=gtk.FILL)
 
-    def add_field(self, desc, val):
-        label = gtk.Label(desc + ':')
-        set_props(label, xalign=1.0)
-        self.widget.attach(label, 0, 1, self.pos, self.pos + 1,
-                           xoptions=gtk.FILL, yoptions=gtk.FILL)
+            row += 1
 
-        label = gtk.Label(str(val))
-        set_props(label, xalign=0.0)
-        self.widget.attach(label, 1, 2, self.pos, self.pos + 1,
-                           xoptions=gtk.FILL, yoptions=gtk.FILL)
-
-        self.pos += 1
-
 def main():
     import sys
 
@@ -102,7 +139,7 @@
     window = gtk.Window()
     window.connect('delete_event', dummy)
 
-    general = General(sys.argv[1]).widget
+    general = General(sys.argv[1])
     window.add(general)
 
     window.show_all()

Added: trunk/ocfs2console/ocfs2interface/gidlemodule.c
===================================================================
--- trunk/ocfs2console/ocfs2interface/gidlemodule.c	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/gidlemodule.c	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,43 @@
+/*
+ * gidlemodule.c
+ *
+ * Fuller interface to GLIB's idle sources.
+ *
+ * Copyright (C) 2005 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have recieved a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <Python.h>
+
+#include <glib.h>
+
+static PyMethodDef gidle_methods[] = {
+  {NULL,       NULL}    /* sentinel */
+};
+
+void
+initgidle (void)
+{
+  PyObject *m;
+
+  m = Py_InitModule ("gidle", gidle_methods);
+
+  if (PyErr_Occurred ())
+    Py_FatalError ("can't initialize module gidle");
+}

Modified: trunk/ocfs2console/ocfs2interface/guiutil.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/guiutil.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/guiutil.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -50,37 +50,6 @@
     dialog.run()
     dialog.destroy()
 
-def query_text(parent, prompt):
-    dialog = gtk.Dialog(parent=parent,
-                        flags=gtk.DIALOG_DESTROY_WITH_PARENT,
-                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                                 gtk.STOCK_OK,     gtk.RESPONSE_OK))
-
-    table = gtk.Table(rows=1, columns=2)
-    set_props(table, row_spacing=4,
-                     column_spacing=4,
-                     border_width=4,
-                     parent=dialog.vbox)
-
-    label = gtk.Label(prompt + ':')
-    set_props(label, xalign=1.0)
-    table.attach(label, 0, 1, 0, 1)
-
-    entry = gtk.Entry()
-    entry.set_activates_default(True)
-    table.attach(entry, 1, 2, 0, 1)
-
-    dialog.show_all()
-
-    if dialog.run() == gtk.RESPONSE_OK:
-        text = entry.get_text()
-    else:
-        text = None
-
-    dialog.destroy()
-
-    return text
-
 def make_callback(obj, callback, sub_callback):
     cb = getattr(obj, callback)
 

Added: trunk/ocfs2console/ocfs2interface/ls.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/ls.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/ls.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,163 @@
+# OCFS2Console - GUI frontend for OCFS2 management and debugging
+# Copyright (C) 2005 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
+
+import stat
+import pwd
+import grp
+import time
+
+import ocfs2
+
+from classlabel import class_label
+
+class Field(object):
+    def __init__(self, dentry, dinode):
+        self.dentry = dentry
+        self.dinode = dinode
+
+    def real_get_text(self):
+        return str(getattr(self.dinode, self.dinode_member))
+
+    def get_text(self):
+        return self.real_get_text()
+
+    text = property(get_text)
+
+    label = class_label
+
+    right_justify = False
+
+file_type = {
+    ocfs2.FT_UNKNOWN  : '?',
+    ocfs2.FT_REG_FILE : '-',
+    ocfs2.FT_DIR      : 'd',
+    ocfs2.FT_CHRDEV   : 'c',
+    ocfs2.FT_BLKDEV   : 'b',
+    ocfs2.FT_FIFO     : 'p',
+    ocfs2.FT_SOCK     : 's',
+    ocfs2.FT_SYMLINK  : 'l',
+}
+
+class Mode(Field):
+    def real_get_text(self):
+        text = ['-'] * 10
+
+        text[0] = file_type[self.dentry.file_type]
+
+        mode = self.dinode.i_mode
+        pos = 0
+
+        for t in 'USR', 'GRP', 'OTH':
+            for b in 'R', 'W', 'X':
+                pos += 1
+
+                if mode & getattr(stat, 'S_I%s%s' % (b, t)):
+                    text[pos] = b.lower()
+
+        pos = 0
+
+        for t, b in (('UID', 'S'), ('GID', 'S'), ('VTX', 'T')):
+            pos += 3
+
+            if mode & getattr(stat, 'S_IS%s' % t):
+                if text[pos] == 'x':
+                    text[pos] = b
+                else:
+                    text[pos] = b.lower()
+
+        return ''.join(text)
+
+class Links(Field):
+    label = '# Links'
+    dinode_member = 'i_links_count'
+    right_justify = True
+
+class ID2Name(Field):
+    def real_get_text(self):
+        idnum = getattr(self.dinode, self.dinode_member)
+
+        try:
+            return self.get_name(idnum)[0]
+        except KeyError:
+            return str(idnum)
+
+class Owner(ID2Name):
+    dinode_member = 'i_uid'
+    get_name = pwd.getpwuid
+
+class Group(ID2Name):
+    dinode_member = 'i_gid'
+    get_name = grp.getgrgid
+
+class Size(Field):
+    dinode_member = 'i_size'
+    right_justify = True
+
+class AllocSize(Field):
+    right_justify = True
+
+    def real_get_text(self):
+        return str(self.dinode.i_clusters * self.dinode.fs.fs_clustersize)
+
+class Timestamp(Field):
+    # Ported from GNU coreutils ls
+    time_formats = ('%b %e  %Y', '%b %e %H:%M')
+
+    def real_get_text(self):
+        when = self.dinode.i_mtime
+        when_local = time.localtime(when)
+
+        current_time = long(time.time())
+
+        six_months_ago = current_time - 31556952 / 2
+        recent = (six_months_ago <= when and when < current_time)
+        fmt = self.time_formats[recent]  
+
+        return time.strftime(fmt, when_local)
+
+class Name(Field):
+    def real_get_text(self):
+        return self.dentry.name
+    
+fields = (Mode, Links, Owner, Group, Size, AllocSize, Timestamp, Name)
+
+def main():
+    import sys
+
+    fs = ocfs2.Filesystem(sys.argv[1])
+
+    dentries = []
+
+    def walk(dentry, offset, blocksize):
+        dentries.append(dentry)
+
+    fs.dir_iterate(walk)
+
+    try:
+        dentry = dentries[int(sys.argv[2])]
+    except (IndexError, ValueError):
+        dentry = dentries[0]
+
+    dinode = fs.read_cached_inode(dentry.inode)
+
+    for field_type in fields:
+        field = field_type(dentry, dinode) 
+
+        print '%s: %s' % (field.label, field.text)
+
+if __name__ == '__main__':
+    main()

Modified: trunk/ocfs2console/ocfs2interface/menu.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/menu.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/menu.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -41,6 +41,12 @@
      stock_about)
 )
 
+cluster_menu_data = (
+    ('/_Cluster',                            None,       None,  0, '<Branch>'),
+    ('/Cluster/_Configure Nodes...',         None,       'node_config'),
+    ('/Cluster/_Propagate Configuration...', None,       'push_config')
+)
+
 if fsck_ok:
     task_menu_fsck_data = (
         ('/Tasks/Chec_k...',       '<control>K', 'check',    'refresh',
@@ -61,15 +67,13 @@
 task_menu_tail_data = (
     ('/Tasks/Change _Label...',    None,         'relabel',  'refresh',
      UNMOUNTED_ONLY),
-    ('/Tasks/Edit _Node Count...', None,         'node_num', 'refresh',
+    ('/Tasks/_Edit Node Count...', None,         'node_num', 'refresh',
      UNMOUNTED_ONLY),
-    ('/Tasks/---',                 None,         None,       0, '<Separator>'),
-    ('/Tasks/_Cluster Config...',  None,         'clconfig')
 )
 
 task_menu_data = task_menu_head_data + task_menu_fsck_data + task_menu_tail_data
 
-menu_data = file_menu_data + task_menu_data + help_menu_data
+menu_data = file_menu_data + cluster_menu_data + task_menu_data + help_menu_data
 
 class Menu:
     def __init__(self, window):

Modified: trunk/ocfs2console/ocfs2interface/mount.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/mount.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/mount.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -17,18 +17,26 @@
 
 import gtk
 
-from guiutil import set_props, error_box, query_text
+import ocfs2
 
+from guiutil import set_props, error_box
+
+from fstab import FSTab
 from process import Process
 
 def mount(parent, device):
-    mountpoint = query_text(parent, 'Mountpoint')
+    mountpoint, options = query_mount(parent, device)
+
     if not mountpoint:
         return None
 
     command = ('mount', '-t', 'ocfs2', device, mountpoint)
 
-    p = Process(command, 'Mount', 'Mounting...', parent)
+    if options:
+        command = list(command)
+        command[1:1] = ('-o', options)
+
+    p = Process(command, 'Mount', 'Mounting...', parent, spin_now=True)
     success, output, killed = p.reap()
 
     if not success:
@@ -57,6 +65,83 @@
         else:
             error_box(parent, '%s: Could not unmount %s mounted on %s' %
                               (output, device, mountpoint))
+
+def query_mount(parent, device):
+    default_mountpoint, default_options = get_defaults(device)
+
+    dialog = gtk.Dialog(parent=parent,
+                        flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                                 gtk.STOCK_OK,     gtk.RESPONSE_OK))
+
+    table = gtk.Table(rows=2, columns=2)
+    set_props(table, row_spacing=6,
+                     column_spacing=6,
+                     border_width=6,
+                     parent=dialog.vbox)
+
+    def text_changed(entry):
+        text = entry.get_text()
+        valid = len(text) > 1 and text.startswith('/')
+        dialog.set_response_sensitive(gtk.RESPONSE_OK, valid)
+
+    mountpoint = gtk.Entry()
+    mountpoint.connect('changed', text_changed)
+
+    mountpoint.set_text(default_mountpoint)
+    text_changed(mountpoint)
+
+    options = gtk.Entry()
+    options.set_text(default_options)
+
+    row = 0
+    for prompt, entry in (('_Mountpoint', mountpoint),
+                          ('O_ptions',    options)):
+        label = gtk.Label()
+        label.set_text_with_mnemonic(prompt + ':')
+        set_props(label, xalign=0.0)
+        table.attach(label, 0, 1, row, row + 1)
+
+        entry.set_activates_default(True)
+        label.set_mnemonic_widget(entry)
+        table.attach(entry, 1, 2, row, row + 1)
+
+        row = row + 1
+
+    dialog.show_all()
+
+    if dialog.run() == gtk.RESPONSE_OK:
+        mount_params = mountpoint.get_text(), options.get_text()
+    else:
+        mount_params = None, None
+
+    dialog.destroy()
+
+    return mount_params
+
+def get_defaults(device):
+    label, uuid = get_ocfs2_id(device)
+
+    fstab = FSTab()
+    entry = fstab.get(device=device, label=label, uuid=uuid)
+
+    if entry and entry.vfstype == 'ocfs2':
+        return entry.mountpoint, entry.options
+    else:
+        return '', ''
+
+def get_ocfs2_id(device):
+    try:
+        fs = ocfs2.Filesystem(device)
+        super = fs.fs_super
+
+        label = super.s_label
+        uuid = super.uuid_unparsed
+    except ocfs2.error:
+        label = uuid = None
+
+    return (label, uuid)
+
 def main():
     import sys
 

Copied: trunk/ocfs2console/ocfs2interface/nodeconfig.py (from rev 789, trunk/ocfs2console/ocfs2interface/clconfig.py)
===================================================================
--- trunk/ocfs2console/ocfs2interface/clconfig.py	2005-04-06 02:39:39 UTC (rev 789)
+++ trunk/ocfs2console/ocfs2interface/nodeconfig.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,549 @@
+# OCFS2Console - GUI frontend for OCFS2 management and debugging
+# Copyright (C) 2002, 2005 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
+
+import os
+import types
+
+import gtk
+import gobject
+import pango
+
+from cStringIO import StringIO
+ 
+import ocfs2
+import o2cb
+
+from guiutil import Dialog, set_props, error_box
+from process import Process
+from ipwidget import IPEditor, IPMissing, IPError
+
+CLUSTER_NAME = 'ocfs2'
+
+O2CB_INIT = '/etc/init.d/o2cb'
+O2CB_CTL = 'o2cb_ctl'
+
+PORT_DEFAULT = 7777
+PORT_MINIMUM = 1000
+PORT_MAXIMUM = 30000
+
+(
+    COLUMN_NEW_NODE,
+    COLUMN_NAME,
+    COLUMN_NODE,
+    COLUMN_IP_ADDR,
+    COLUMN_IP_PORT
+) = range(5)
+
+fields = (
+    (COLUMN_NEW_NODE, 'Active',     None,           bool),
+    (COLUMN_NAME,     'Name',       gtk.Entry,      str),
+    (COLUMN_NODE,     'Node',       None,           int),
+    (COLUMN_IP_ADDR,  'IP Address', IPEditor,       str),
+    (COLUMN_IP_PORT,  'IP Port',    gtk.SpinButton, str)
+)
+
+# Hate your ancient distros shipping old pygtks
+typemap = { bool: gobject.TYPE_BOOLEAN }
+
+class ConfigError(Exception):
+    pass
+
+class ClusterConfig(Dialog):
+    def __init__(self, parent=None):
+        self.new_nodes = 0
+
+        Dialog.__init__(self, parent=parent, title='Node Configuration',
+                        buttons=(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
+                                 gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+
+        if parent is None:
+            self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
+
+        frame = gtk.Frame()
+        frame.set_shadow_type(gtk.SHADOW_NONE)
+        self.vbox.add(frame)
+
+        label = gtk.Label()
+        label.set_text_with_mnemonic('_Nodes:')
+        frame.set_label_widget(label)
+
+        hbox = gtk.HBox(spacing=4)
+        hbox.set_border_width(4)
+        frame.add(hbox)
+
+        scrl_win = gtk.ScrolledWindow()     
+        scrl_win.set_policy(hscrollbar_policy=gtk.POLICY_AUTOMATIC,
+                            vscrollbar_policy=gtk.POLICY_AUTOMATIC)
+        hbox.pack_start(scrl_win)
+
+        self.setup_treeview()
+        label.set_mnemonic_widget(self.tv)
+        scrl_win.add(self.tv)
+
+        vbbox = gtk.VButtonBox()
+        set_props(vbbox, layout_style=gtk.BUTTONBOX_START,
+                         spacing=5,
+                         border_width=5)
+        hbox.pack_end(vbbox, expand=False, fill=False)
+
+        self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
+        self.add_button.connect('clicked', self.add_node)
+        vbbox.add(self.add_button)
+
+        try:
+            edit_construct_args = {'stock': gtk.STOCK_EDIT}
+        except AttributeError:
+            edit_construct_args = {'label': '_Edit'}
+
+        self.edit_button = gtk.Button(**edit_construct_args)
+        self.edit_button.connect('clicked', self.edit_node)
+        vbbox.add(self.edit_button)
+        
+        self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE)
+        self.remove_button.connect('clicked', self.remove_node)
+        vbbox.add(self.remove_button)
+
+        self.can_edit(False)
+        self.can_apply(False)
+ 
+        self.sel = self.tv.get_selection()
+        self.sel.connect('changed', self.on_select)
+
+    def load_cluster_state(self):
+        query_args = '-I -t node -o'
+        o2cb_ctl = O2CBCtl(query_args, 'Querying nodes...', self)
+        success, output, k = o2cb_ctl.reap()
+
+        if not success:
+            raise ConfError, output
+
+        self.store = gtk.ListStore(*[typemap.get(f[3], f[3]) for f in fields])
+
+        def node_compare(store, a, b):
+            n1 = store[a][COLUMN_NODE]
+            n2 = store[b][COLUMN_NODE]
+
+            if n1 < 0 and n2 >= 0:
+                return 1
+            elif n1 >= 0 and n2 < 0:
+                return -1
+            else:
+                return cmp(abs(n1), abs(n2))
+              
+        self.store.set_sort_func(COLUMN_NODE, node_compare)
+        self.store.set_sort_column_id(COLUMN_NODE, gtk.SORT_ASCENDING)
+
+        buffer = StringIO(output)
+
+        for line in buffer:
+            if line.startswith('#'):
+                continue
+
+            data = list((None,) * len(fields))
+
+            try:
+                data[COLUMN_NEW_NODE] = False
+
+                (data[COLUMN_NAME],
+                 cluster,
+                 data[COLUMN_NODE],
+                 data[COLUMN_IP_ADDR],
+                 data[COLUMN_IP_PORT],
+                 state) = line.split(':')
+
+                for i in range(0, len(fields)):
+                    data[i] = fields[i][3](data[i])
+            except ValueError:
+                continue
+
+            if cluster == CLUSTER_NAME:
+                self.store.append(data)
+
+        self.new_nodes = 0
+
+        self.tv.set_model(self.store)
+        self.sel.select_iter(self.store.get_iter_first())
+
+    def setup_treeview(self):
+        self.tv = gtk.TreeView()
+        self.tv.set_size_request(350, 200)
+
+        for col, title, widget_type, field_type in fields:
+            if col == COLUMN_NEW_NODE:
+                self.tv.insert_column_with_data_func(-1, title,
+                                                     gtk.CellRendererPixbuf(),
+                                                     self.active_set_func)
+            elif col == COLUMN_NODE:
+                self.tv.insert_column_with_data_func(-1, title,
+                                                     gtk.CellRendererText(),
+                                                     self.node_set_func)
+            else:
+                cell_renderer = gtk.CellRendererText()
+                cell_renderer.set_property('style', pango.STYLE_ITALIC)
+
+                self.tv.insert_column_with_attributes(-1, title,
+                                                      cell_renderer,
+                                                      text=col,
+                                                      style_set=COLUMN_NEW_NODE)
+
+    def active_set_func(self, tree_column, cell, model, iter):
+        if model[iter][COLUMN_NEW_NODE]:
+            stock_id = None
+        else:
+            stock_id = gtk.STOCK_EXECUTE
+
+        cell.set_property('stock_id', stock_id)
+
+    def node_set_func(self, tree_column, cell, model, iter):
+        if model[iter][COLUMN_NEW_NODE]:
+            text = ''
+        else:
+            text = str(model[iter][COLUMN_NODE])
+
+        cell.set_property('text', text)
+
+    def on_select(self, sel):
+        store, iter = sel.get_selected()
+
+        if iter:
+            editable = store[iter][COLUMN_NEW_NODE]
+        else:
+            editable = False
+
+        self.can_edit(editable)
+
+    def can_edit(self, state):
+        self.edit_button.set_sensitive(state)
+        self.remove_button.set_sensitive(state)
+
+    def can_apply(self, state):
+        self.set_response_sensitive(gtk.RESPONSE_APPLY, state)
+            
+    def add_node(self, b):
+        if len(self.store) == ocfs2.MAX_NODES:
+            error_box(self, 'Cannot have more than %d nodes in a cluster' %
+                            ocfs2.MAX_NODES)
+            return
+
+        node_attrs = self.node_query(title='Add Node')
+
+        if node_attrs is None:
+            return
+
+        self.new_nodes += 1
+
+        name, ip_addr, ip_port = node_attrs
+
+        iter = self.store.append((True, name, -self.new_nodes,
+                                  ip_addr, ip_port))
+        self.sel.select_iter(iter)
+
+        self.can_apply(True)
+
+    def edit_node(self, b):
+        store, iter = self.sel.get_selected()
+        attrs = store[iter]
+
+        node_attrs = self.node_query(title='Edit Node', defaults=attrs)
+
+        if node_attrs is None:
+            return
+
+        (attrs[COLUMN_NAME],
+         attrs[COLUMN_IP_ADDR],
+         attrs[COLUMN_IP_PORT]) = node_attrs
+
+    def remove_node(self, b):
+        store, iter = self.sel.get_selected()
+
+        msg = ('Are you sure you want to delete node %s?' %
+               store[iter][COLUMN_NAME])
+
+        ask = gtk.MessageDialog(parent=self,
+                                flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+                                type=gtk.MESSAGE_QUESTION,
+                                buttons=gtk.BUTTONS_YES_NO,
+                                message_format=msg)
+
+        response = ask.run()
+        ask.destroy()
+
+        if response == gtk.RESPONSE_YES:
+            del store[iter]
+            self.new_nodes -= 1
+
+            if self.new_nodes == 0:
+                self.can_apply(False)
+
+            self.sel.select_iter(self.store.get_iter_first())
+
+    def new_node_attrs(self):
+        attrs = []
+
+        for row in self.store:
+            if row[COLUMN_NEW_NODE]:
+                attrs.append((row[COLUMN_NAME],
+                              row[COLUMN_IP_ADDR],
+                              row[COLUMN_IP_PORT]))
+
+        return attrs
+
+    def apply_changes(self):
+        success = False
+
+        for name, ip_addr, ip_port in self.new_node_attrs():
+            add_node_args = ('-C', '-n', name, '-t', 'node',
+                             '-a', 'cluster=%s' % CLUSTER_NAME,
+                             '-a', 'ip_address=%s' % ip_addr,
+                             '-a', 'ip_port=%s' % ip_port,
+                             '-i')
+
+            o2cb_ctl = O2CBCtl(add_node_args, 'Adding node %s...' % name, self)
+            success, output, k = o2cb_ctl.reap()
+
+            if not success:
+                error_box(self, '%s\nCould not add node %s' % (output, name))
+                break
+
+        self.load_cluster_state()
+        return success
+
+    def node_query(self, title='Node Attributes', defaults=None):
+        existing_names = {}
+        existing_ip_addrs = {}
+
+        for row in self.store:
+            name = row[COLUMN_NAME]
+            ip_addr = row[COLUMN_IP_ADDR]
+
+            existing_names[name] = 1
+            existing_ip_addrs[ip_addr] = name
+
+        dialog = Dialog(parent=self, title=title,
+                        buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                                 gtk.STOCK_OK,     gtk.RESPONSE_OK))
+
+        dialog.set_alternative_button_order((gtk.RESPONSE_OK,
+                                             gtk.RESPONSE_CANCEL))
+
+        dialog.set_default_response(gtk.RESPONSE_OK)
+
+        table = gtk.Table(rows=4, columns=2)
+        set_props(table, row_spacing=4,
+                         column_spacing=4,
+                         border_width=4,
+                         parent=dialog.vbox)
+
+        widgets = []
+        row = 0
+
+        for col, title, widget_type, field_type in fields:
+            if widget_type is None:
+                widgets.append(None)
+                continue
+
+            label = gtk.Label(title + ':')
+            set_props(label, xalign=1.0)
+            table.attach(label, 0, 1, row, row + 1)
+
+            widget = widget_type()
+            table.attach(widget, 1, 2, row, row + 1)
+
+            if col == COLUMN_NAME:
+                #XXX widget.set_max_length(ocfs2.MAX_NODE_NAME_LENGTH)
+                pass
+            elif col == COLUMN_IP_PORT:
+                widget.set_numeric(True)
+
+                adjustment = gtk.Adjustment(PORT_DEFAULT,
+                                            PORT_MINIMUM, PORT_MAXIMUM,
+                                            1, 100) 
+
+                widget.set_adjustment(adjustment)
+                widget.set_value(PORT_DEFAULT)
+
+            widgets.append(widget)
+
+            row = row + 1
+
+        if defaults:
+            for w, d in zip(widgets, defaults):
+                if w and d:
+                    w.set_text(d)
+
+        dialog.show_all()
+
+        while 1:
+            if dialog.run() != gtk.RESPONSE_OK:
+                dialog.destroy()
+                return None
+
+            ip_port = widgets[COLUMN_IP_PORT].get_text()
+
+            name = widgets[COLUMN_NAME].get_text()
+
+            if not name:
+                error_box(dialog, 'Node name not specified')
+                continue
+
+            try:
+                ip_addr = widgets[COLUMN_IP_ADDR].get_text()
+            except (IPMissing, IPError), msg:
+                error_box(dialog, msg[0])
+                continue
+
+            if name in existing_names:
+                error_box(dialog,
+                          'Node %s already exists in the configuration' % name)
+            elif ip_addr in existing_ip_addrs:
+                error_box(dialog,
+                          'IP %s is already assigned to node %s' %
+                          (ip_addr, existing_ip_addrs[ip_addr]))
+            else:
+                break
+
+        dialog.destroy()
+
+        return name, ip_addr, ip_port
+
+    def run(self):
+        self.show_all()
+
+        while 1:
+            if Dialog.run(self) == gtk.RESPONSE_APPLY:
+                self.apply_changes()
+            elif len(self.new_node_attrs()):
+                msg = ('New nodes have been created, but they have not been '
+                       'applied to the cluster configuration. Do you want to '
+                       'apply the changes now?')
+
+                ask = gtk.MessageDialog(parent=self,
+                                        flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+                                        type=gtk.MESSAGE_QUESTION,
+                                        buttons=gtk.BUTTONS_YES_NO,
+                                        message_format=msg)
+
+                if ask.run() == gtk.RESPONSE_NO:
+                    break
+                elif self.apply_changes():
+                    break
+            else: 
+                break
+
+class O2CBProcess(Process):
+    def __init__(self, args, desc, parent=None):
+        if isinstance(args, types.StringTypes):
+            command = '%s %s' % (self.o2cb_program, args)
+        else:
+            command = (self.o2cb_program,) + tuple(args)
+
+        Process.__init__(self, command, self.o2cb_title, desc, parent)
+
+class O2CBCtl(O2CBProcess):
+    o2cb_program = O2CB_CTL
+    o2cb_title = 'Cluster Control'
+
+class O2CBInit(O2CBProcess):
+    o2cb_program = O2CB_INIT
+    o2cb_title = 'Cluster Stack'
+
+def node_config(parent=None):
+    if not os.access(o2cb.FORMAT_CLUSTER_DIR, os.F_OK):
+        load_args = ('load',)
+        o2cb_init = O2CBInit(load_args, 'Starting cluster stack...', parent)
+        success, output, k = o2cb_init.reap()
+
+        if success:
+            msg_type = gtk.MESSAGE_INFO
+            msg = ('The cluster stack has been started. It needs to be '
+                   'running for any clustering functionality to happen. '
+                   'Please run "%s enable" to have it started upon bootup.'
+                   % o2cb_init.o2cb_program)
+        else:
+            msg_type = gtk.MESSAGE_WARNING
+            msg = ('Could not start cluster stack. This must be resolved '
+                   'before any OCFS2 filesystem can be mounted')
+
+        info = gtk.MessageDialog(parent=parent,
+                                 flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+                                 type=msg_type,
+                                 buttons=gtk.BUTTONS_CLOSE,
+                                 message_format=msg)
+
+        info.run()
+        info.destroy()
+
+        if not success:
+            return
+
+#    msg = ('Currently, the clustering software can only handle '
+#           'one cluster at a time. To keep things simple, you '
+#           'are expected to name your cluster "%s". Your '
+#           'configuration file contains a cluster named "%s". '
+#           'Please rename or delete that cluster, and restart '
+#           'the cluster stack before trying to run the cluster '
+#           'configurator again.' % (CLUSTER_NAME, cluster))
+
+    query_args = '-I -t cluster -n %s -o' % CLUSTER_NAME
+    o2cb_ctl = O2CBCtl(query_args, 'Querying cluster...', parent)
+    success, output, k = o2cb_ctl.reap()
+
+    if not success:
+        create_args = '-C -n %s -t cluster -i' % CLUSTER_NAME
+        o2cb_ctl = O2CBCtl(query_args, 'Creating cluster...', parent)
+        success, output, k = o2cb_ctl.reap()
+
+        if not success:
+            error_box(parent, '%s\nCould not create cluster' % output)
+            return
+
+    conf = ClusterConfig(parent)
+
+    try:
+        conf.load_cluster_state()
+    except ConfigError, e:
+        error_box(parent, '%s: Could not query cluster configuration' % str(e))
+        return
+
+    conf.run()
+    conf.destroy()
+
+    if not os.access(o2cb.FORMAT_CLUSTER % CLUSTER_NAME, os.F_OK):
+        online_args = ('online', CLUSTER_NAME),
+        o2cb_init = O2CBInit(online_args, 'Starting OCFS2 cluster...', parent)
+        success, output, k = o2cb_init.reap()
+
+        if not success:
+            msg = ('Could not bring OCFS2 cluster online. This must be '
+                   'resolved before any OCFS2 filesystem can be mounted')
+
+            info = gtk.MessageDialog(parent=parent,
+                                     flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+                                     type=gtk.MESSAGE_WARNING,
+                                     buttons=gtk.BUTTONS_CLOSE,
+                                     message_format=msg)
+
+            info.run()
+            info.destroy()
+
+def main():
+    from about import process_gui_args
+    process_gui_args()
+    node_config()
+
+if __name__ == '__main__':
+    main()

Added: trunk/ocfs2console/ocfs2interface/o2cbmodule.c
===================================================================
--- trunk/ocfs2console/ocfs2interface/o2cbmodule.c	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/o2cbmodule.c	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,554 @@
+/*
+ * o2cbmodule.c
+ *
+ * O2CB python binding.
+ *
+ * Copyright (C) 2005 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have recieved a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "o2cb.h"
+#include "o2cb_abi.h"
+
+
+typedef struct {
+  PyObject_HEAD
+  PyObject *name;
+} O2CBObject;
+
+#define O2CB_OBJECT_NAME(obj) (((O2CBObject *) (obj))->name)
+
+typedef O2CBObject Cluster;
+
+typedef struct {
+  O2CBObject  object;
+  Cluster    *cluster;
+} Node;
+
+
+static PyObject *o2cb_error;
+
+#define CHECK_ERROR(call)				do {	\
+  ret = call;							\
+  if (ret)							\
+    {								\
+      PyErr_SetString (o2cb_error, error_message (ret));	\
+      return NULL;						\
+    }								\
+} while (0)
+
+
+static void
+o2cb_object_dealloc (O2CBObject *self)
+{
+  Py_XDECREF (self->name);
+  PyObject_DEL (self);
+}
+
+static PyObject *
+o2cb_object_repr (O2CBObject *self,
+                  const char *type_name)
+{
+  return PyString_FromFormat ("<o2cb.%s '%s'>", type_name,
+			      PyString_AS_STRING (self->name));
+}
+
+static PyMemberDef o2cb_object_members[] = {
+  {"name", T_OBJECT, offsetof (O2CBObject, name), RO},
+  {0}
+};
+
+static PyObject *
+o2cb_object_new (O2CBObject *self,
+		 const char *name)
+{
+  if (self == NULL)
+    return NULL;
+
+  self->name = PyString_FromString (name);
+
+  if (self->name == NULL)
+    {
+      PyObject_DEL (self);
+      return NULL;
+    }
+
+  return (PyObject *) self;
+}
+
+static PyObject *
+node_number (Node *self, void *closure)
+{
+  errcode_t ret;
+  uint16_t  node_num;
+
+  CHECK_ERROR (o2cb_get_node_num (PyString_AS_STRING (self->cluster->name),
+				  PyString_AS_STRING (O2CB_OBJECT_NAME (self)),
+				  &node_num));
+
+  return PyInt_FromLong (node_num);
+}
+
+static PyGetSetDef node_getsets[] = {
+  {"number", (getter)node_number, (setter)0},
+  {NULL}
+};
+
+static void
+node_dealloc (Node *self)
+{
+  Py_XDECREF (self->cluster);
+  o2cb_object_dealloc ((O2CBObject *) self);
+}
+
+static PyObject *
+node_repr (Node *self)
+{
+  return o2cb_object_repr ((O2CBObject *) self, "Node");
+}
+
+static PyTypeObject Node_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/* ob_size */
+  "o2cb.Node",				/* tp_name */
+  sizeof(Node),				/* tp_basicsize */
+  0,					/* tp_itemsize */
+  (destructor)node_dealloc,		/* tp_dealloc */
+  0,					/* tp_print */
+  0,					/* tp_getattr */
+  0,					/* tp_setattr */
+  0,					/* tp_compare */
+  (reprfunc)node_repr,			/* tp_repr */
+  0,					/* tp_as_number */
+  0,					/* tp_as_sequence */
+  0,					/* tp_as_mapping */
+  0,					/* tp_hash */
+  0,					/* tp_call */
+  0,					/* tp_str */
+  0,					/* tp_getattro */
+  0,					/* tp_setattro */
+  0,					/* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,			/* tp_flags */
+  NULL,					/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  0,					/* tp_iter */
+  0,					/* tp_iternext */
+  0,					/* tp_methods */
+  o2cb_object_members,			/* tp_members */
+  node_getsets,				/* tp_getset */
+  0,					/* tp_base */
+  0,					/* tp_dict */
+  0,					/* tp_descr_get */
+  0,					/* tp_descr_set */
+  0,					/* tp_dictoffset */
+  0,					/* tp_init */
+  0,					/* tp_alloc */
+  0,					/* tp_new */
+};
+
+static PyObject *
+node_new (Cluster    *cluster,
+	  const char *name)
+{
+  Node *self;
+
+  self = PyObject_NEW (Node, &Node_Type);
+
+  self = (Node *) o2cb_object_new ((O2CBObject *) self, name);
+
+  if (self)
+    {
+      Py_INCREF (cluster);
+      self->cluster = cluster;
+    }
+
+  return (PyObject *) self;
+}
+
+static PyObject *
+cluster_add_node (Cluster  *self,
+		  PyObject *args,
+		  PyObject *kwargs)
+{
+  /* XXX: We could be smarter about type conversions here */
+  const char *node_name, *node_num, *ip_address, *ip_port, *local;
+  errcode_t   ret;
+
+  static char *kwlist[] = { "node_name", "node_num",
+			    "ip_address", "ip_port", "local",
+			    NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs, "sssss:add_node", kwlist,
+				    &node_name, &node_num,
+				    &ip_address, &ip_port, &local))
+    return NULL;
+
+  CHECK_ERROR (o2cb_add_node (PyString_AS_STRING (self->name),
+			      node_name, node_num,
+			      ip_address, ip_port, local));
+
+  return node_new (self, node_name);
+}
+
+static PyObject *
+cluster_create_heartbeat_region_disk (Cluster  *self,
+				      PyObject *args,
+				      PyObject *kwargs)
+{
+  const char *region_name, *device_name;
+  int         block_bytes;
+  uint64_t    start_block, blocks;
+  errcode_t   ret;
+
+  static char *kwlist[] = { "region_name", "device_name",
+			    "block_bytes", "start_block", "blocks",
+			    NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "ssiKK:create_heartbeat_region_disk",
+				    kwlist,
+				    &region_name, &device_name,
+				    &block_bytes, &start_block, &blocks))
+    return NULL;
+
+  CHECK_ERROR 
+    (o2cb_create_heartbeat_region_disk (PyString_AS_STRING (self->name),
+					region_name, device_name,
+					block_bytes, start_block, blocks));
+
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
+static PyObject *
+cluster_remove_heartbeat_region_disk (Cluster  *self,
+				      PyObject *args,
+				      PyObject *kwargs)
+{
+  const char *region_name;
+  errcode_t   ret;
+
+  static char *kwlist[] = { "region_name", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "s:remove_heartbeat_region_disk", kwlist,
+				    &region_name))
+    return NULL;
+
+  CHECK_ERROR
+    (o2cb_remove_heartbeat_region_disk (PyString_AS_STRING (self->name),
+					region_name));
+
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
+static PyMethodDef cluster_methods[] = {
+  {"add_node", (PyCFunction)cluster_add_node, METH_VARARGS | METH_KEYWORDS},
+  {"create_heartbeat_region_disk", (PyCFunction)cluster_create_heartbeat_region_disk, METH_VARARGS | METH_KEYWORDS},
+  {"remove_heartbeat_region_disk", (PyCFunction)cluster_remove_heartbeat_region_disk, METH_VARARGS | METH_KEYWORDS},
+  {NULL, NULL}
+};
+
+static PyObject *
+cluster_nodes (Cluster *self, void *closure)
+{
+  char      **nodes, **name;
+  errcode_t   ret;
+  PyObject   *list, *node;
+  int         status;
+
+  CHECK_ERROR (o2cb_list_nodes (PyString_AS_STRING (self->name), &nodes));
+
+  list = PyList_New (0);
+  if (list == NULL)
+    goto cleanup;
+
+  for (name = nodes; *name != NULL; name++)
+    {
+      node = node_new (self, *name);
+      if (node == NULL)
+	goto err;
+
+      status = PyList_Append (list, node);
+      Py_DECREF (node);
+
+      if (status)
+	goto err;
+    }
+
+  goto cleanup;
+
+err:
+  Py_DECREF (list);
+
+cleanup:
+  o2cb_free_nodes_list (nodes);
+
+  return list;
+}
+
+static PyGetSetDef cluster_getsets[] = {
+  {"nodes", (getter)cluster_nodes, (setter)0},
+  {NULL}
+};
+
+static PyObject *
+cluster_repr (Cluster *self)
+{
+  return o2cb_object_repr (self, "Cluster");
+}
+
+static int
+cluster_init (Cluster  *self,
+	      PyObject *args,
+	      PyObject *kwargs)
+{
+  errcode_t   ret;
+  const char *name;
+
+  static char *kwlist[] = { "name", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+				   "s:o2cb.Cluster.__init__", kwlist,
+				   &name))
+    return -1;
+
+  self->name = PyString_FromString (name);
+  if (self->name == NULL)
+    return -1;
+
+  ret = o2cb_create_cluster (name);
+
+  if (ret)
+    {
+      Py_DECREF (self->name);
+      PyErr_SetString (o2cb_error, error_message (ret));
+      return -1;
+    }
+
+  return 0;
+}
+
+static PyTypeObject Cluster_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/* ob_size */
+  "o2cb.Cluster",			/* tp_name */
+  sizeof(Cluster),			/* tp_basicsize */
+  0,					/* tp_itemsize */
+  (destructor)o2cb_object_dealloc,	/* tp_dealloc */
+  0,					/* tp_print */
+  0,					/* tp_getattr */
+  0,					/* tp_setattr */
+  0,					/* tp_compare */
+  (reprfunc)cluster_repr,		/* tp_repr */
+  0,					/* tp_as_number */
+  0,					/* tp_as_sequence */
+  0,					/* tp_as_mapping */
+  0,					/* tp_hash */
+  0,					/* tp_call */
+  0,					/* tp_str */
+  0,					/* tp_getattro */
+  0,					/* tp_setattro */
+  0,					/* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,			/* tp_flags */
+  NULL,					/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  0,					/* tp_iter */
+  0,					/* tp_iternext */
+  cluster_methods,			/* tp_methods */
+  o2cb_object_members,			/* tp_members */
+  cluster_getsets,			/* tp_getset */
+  0,					/* tp_base */
+  0,					/* tp_dict */
+  0,					/* tp_descr_get */
+  0,					/* tp_descr_set */
+  0,					/* tp_dictoffset */
+  (initproc)cluster_init,		/* tp_init */
+  0,					/* tp_alloc */
+  0,					/* tp_new */
+};
+
+static PyObject *
+cluster_new (const char *name)
+{
+  Cluster *self;
+
+  self = PyObject_NEW (Cluster, &Cluster_Type);
+
+  return o2cb_object_new (self, name);
+}
+
+static PyObject *
+list_clusters (PyObject *self)
+{
+  char      **clusters, **name;
+  errcode_t   ret;
+  PyObject   *list, *cluster;
+  int         status;
+
+  CHECK_ERROR (o2cb_list_clusters (&clusters));
+
+  list = PyList_New (0);
+  if (list == NULL)
+    goto cleanup;
+
+  for (name = clusters; *name != NULL; name++)
+    {
+      cluster = cluster_new (*name);
+      if (cluster == NULL)
+	goto err;
+
+      status = PyList_Append (list, cluster);
+      Py_DECREF (cluster);
+
+      if (status)
+	goto err;
+    }
+
+  goto cleanup;
+
+err:
+  Py_DECREF (list);
+
+cleanup:
+  o2cb_free_cluster_list (clusters);
+
+  return list;
+}
+
+static PyObject *
+create_heartbeat_region_disk (PyObject *self,
+			      PyObject *args,
+			      PyObject *kwargs)
+{
+  const char *cluster_name, *region_name, *device_name;
+  int         block_bytes;
+  uint64_t    start_block, blocks;
+  errcode_t   ret;
+
+  static char *kwlist[] = { "cluster_name", "region_name", "device_name",
+			    "block_bytes", "start_block", "blocks",
+			    NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "zssiKK:create_heartbeat_region_disk",
+				    kwlist,
+				    &cluster_name, &region_name, &device_name,
+				    &block_bytes, &start_block, &blocks))
+    return NULL;
+
+  CHECK_ERROR 
+    (o2cb_create_heartbeat_region_disk (cluster_name, region_name, device_name,
+					block_bytes, start_block, blocks));
+
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
+static PyObject *
+remove_heartbeat_region_disk (PyObject *self,
+			      PyObject *args,
+			      PyObject *kwargs)
+{
+  const char *cluster_name, *region_name;
+  errcode_t   ret;
+
+  static char *kwlist[] = { "cluster_name", "region_name", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "zs:remove_heartbeat_region_disk", kwlist,
+				    &cluster_name, &region_name))
+    return NULL;
+
+  CHECK_ERROR (o2cb_remove_heartbeat_region_disk (cluster_name, region_name));
+
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
+static PyMethodDef o2cb_methods[] = {
+  {"list_clusters", (PyCFunction)list_clusters, METH_NOARGS},
+  {"create_heartbeat_region_disk", (PyCFunction)create_heartbeat_region_disk, METH_VARARGS | METH_KEYWORDS},
+  {"remove_heartbeat_region_disk", (PyCFunction)remove_heartbeat_region_disk, METH_VARARGS | METH_KEYWORDS},
+  {NULL,       NULL}    /* sentinel */
+};
+
+static void
+add_constants (PyObject *m)
+{
+  PyModule_AddStringConstant (m, "CONFIGFS_PATH", CONFIGFS_PATH);
+
+#define ADD_STR_CONSTANT(name) \
+  PyModule_AddStringConstant (m, "FORMAT_" #name, O2CB_FORMAT_ ## name)
+
+  ADD_STR_CONSTANT (CLUSTER_DIR);
+  ADD_STR_CONSTANT (CLUSTER);
+  ADD_STR_CONSTANT (NODE_DIR);
+  ADD_STR_CONSTANT (NODE);
+  ADD_STR_CONSTANT (NODE_ATTR);
+  ADD_STR_CONSTANT (HEARTBEAT_DIR);
+  ADD_STR_CONSTANT (HEARTBEAT_REGION);
+  ADD_STR_CONSTANT (HEARTBEAT_REGION_ATTR);
+
+#undef ADD_STR_CONSTANT
+}
+
+void
+inito2cb (void)
+{
+  PyObject *m;
+
+  Node_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&Node_Type) < 0)
+    return;
+
+  Cluster_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&Cluster_Type) < 0)
+    return;
+
+  initialize_o2cb_error_table ();
+
+  m = Py_InitModule ("o2cb", o2cb_methods);
+
+  o2cb_error = PyErr_NewException ("o2cb.error", PyExc_RuntimeError, NULL);
+
+  if (o2cb_error)
+    {
+      Py_INCREF (o2cb_error);
+      PyModule_AddObject (m, "error", o2cb_error);
+    }
+
+  Py_INCREF (&Cluster_Type);
+  PyModule_AddObject (m, "Cluster", (PyObject *) &Cluster_Type);
+
+  add_constants (m);
+
+  if (PyErr_Occurred ())
+    Py_FatalError ("can't initialize module o2cb");
+}

Modified: trunk/ocfs2console/ocfs2interface/ocfs2module.c
===================================================================
--- trunk/ocfs2console/ocfs2interface/ocfs2module.c	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/ocfs2module.c	2005-04-13 02:02:31 UTC (rev 799)
@@ -23,299 +23,1169 @@
  * Boston, MA 021110-1307, USA.
  */
 
-#include "Python.h"
-#include "structseq.h"
+#include <Python.h>
+#include <structmember.h>
 
-#include <glib.h>
+#include <inttypes.h>
 
+#include <uuid/uuid.h>
+
 #include "ocfs2.h"
 
-#include "ocfsplist.h"
 
+#define Filesystem_Check(op)  PyObject_TypeCheck(op, &Filesystem_Type)
+#define DInode_Check(op)      PyObject_TypeCheck(op, &DInode_Type)
+#define DirEntry_Check(op)    PyObject_TypeCheck(op, &DirEntry_Type)
+#define SuperBlock_Check(op)  PyObject_TypeCheck(op, &SuperBlock_Type)
+#define DirScanIter_Check(op) PyObject_TypeCheck(op, &DirScanIter_Type)
 
+
+typedef struct {
+  PyObject_HEAD
+  PyObject      *device;
+  ocfs2_filesys *fs;
+} Filesystem;
+
+typedef struct {
+  PyObject_HEAD
+  Filesystem    *fs_obj;
+  ocfs2_dinode   dinode;
+} DInode;
+
+typedef struct {
+  PyObject_HEAD
+  Filesystem             *fs_obj;
+  struct ocfs2_dir_entry  dentry;
+} DirEntry;
+
+typedef struct {
+  PyObject_HEAD
+  Filesystem        *fs_obj;
+  ocfs2_super_block  super;
+} SuperBlock;
+
+typedef struct {
+  PyObject_HEAD
+  Filesystem     *fs_obj;
+  ocfs2_dir_scan *scan;
+} DirScanIter;
+
+
 static PyObject *ocfs2_error;
 
-typedef struct
+#define CHECK_ERROR(call)				do {	\
+  ret = call;							\
+  if (ret)							\
+    {								\
+      PyErr_SetString (ocfs2_error, error_message (ret));	\
+      return NULL;						\
+    }								\
+} while (0)
+
+
+#define DINODE_U64_GETTER(name) \
+  static PyObject *							\
+  dinode_ ## name (DInode *self, void *closure)				\
+  {									\
+    return PyLong_FromUnsignedLongLong (self->dinode.i_ ## name);	\
+  }
+
+#define DINODE_GETTER_ENTRY(name) \
+  {"i_" #name, (getter)dinode_ ## name, (setter)0}
+
+DINODE_U64_GETTER(size)
+DINODE_U64_GETTER(atime)
+DINODE_U64_GETTER(ctime)
+DINODE_U64_GETTER(mtime)
+DINODE_U64_GETTER(dtime)
+DINODE_U64_GETTER(blkno)
+DINODE_U64_GETTER(last_eb_blk)
+
+static PyObject *
+dinode_rdev (DInode *self, void *closure)
 {
-  PyObject *func;
-  PyObject *data;
-} ProxyData;
+  return PyLong_FromUnsignedLongLong (self->dinode.id1.dev1.i_rdev);
+}
 
+static PyObject *
+dinode_jflags (DInode *self, void *closure)
+{
+  return PyInt_FromLong (self->dinode.id1.journal1.ij_flags);
+}
+
+static PyGetSetDef dinode_getsets[] = {
+  DINODE_GETTER_ENTRY (size),
+  DINODE_GETTER_ENTRY (atime),
+  DINODE_GETTER_ENTRY (ctime),
+  DINODE_GETTER_ENTRY (mtime),
+  DINODE_GETTER_ENTRY (dtime),
+  DINODE_GETTER_ENTRY (blkno),
+  DINODE_GETTER_ENTRY (last_eb_blk),
+  DINODE_GETTER_ENTRY (rdev),
+  {"ij_flags", (getter)dinode_jflags, (setter)0},
+  {NULL}
+};
+
+#undef DINODE_U64_GETTER
+#undef DINODE_GETTER_ENTRY
+
+#define DINODE_STR_MEMBER(name) \
+  {"i_" #name, T_STRING_INPLACE, offsetof (DInode, dinode.i_ ## name), RO}
+#define DINODE_S16_MEMBER(name) \
+  {"i_" #name, T_SHORT, offsetof (DInode, dinode.i_ ## name), RO}
+#define DINODE_U16_MEMBER(name) \
+  {"i_" #name, T_USHORT, offsetof (DInode, dinode.i_ ## name), RO}
+#define DINODE_U32_MEMBER(name) \
+  {"i_" #name, T_UINT, offsetof (DInode, dinode.i_ ## name), RO}
+#define DINODE_BITMAP_MEMBER(name) \
+  {"i_" #name, T_UINT, offsetof (DInode, dinode.id1.bitmap1.i_ ## name), RO}
+
+static PyMemberDef dinode_members[] = {
+  DINODE_STR_MEMBER (signature),
+  DINODE_U32_MEMBER (generation),
+  DINODE_S16_MEMBER (suballoc_node),
+  DINODE_U16_MEMBER (suballoc_bit),
+  DINODE_U32_MEMBER (clusters),
+  DINODE_U32_MEMBER (uid),
+  DINODE_U32_MEMBER (gid),
+  DINODE_U16_MEMBER (mode),
+  DINODE_U16_MEMBER (links_count),
+  DINODE_U32_MEMBER (flags),
+  DINODE_U32_MEMBER (fs_generation),
+  DINODE_BITMAP_MEMBER (used),
+  DINODE_BITMAP_MEMBER (total),
+  {"fs", T_OBJECT, offsetof (DInode, fs_obj), RO},
+  {0}
+};
+
+#undef DINODE_STR_MEMBER
+#undef DINODE_S16_MEMBER
+#undef DINODE_U16_MEMBER
+#undef DINODE_U32_MEMBER
+#undef DINODE_BITMAP_MEMBER
+
 static void
-proxy_partition_func (OcfsPartitionInfo *info,
-		      gpointer           pdata)
+dinode_dealloc (DInode *self)
 {
-  ProxyData *data = pdata;
+  Py_DECREF (self->fs_obj);
+  PyObject_DEL (self);
+}
 
-  PyObject_CallFunction (data->func, "sss",
-			 info->device, info->mountpoint, info->fstype);
+static PyObject *
+dinode_repr (DInode *self)
+{
+  char blkno[32];
+
+  snprintf (blkno, sizeof (blkno), "%"PRIu64, self->dinode.i_blkno);
+  return PyString_FromFormat ("<ocfs2.DInode %s on %s>", blkno,
+			      PyString_AS_STRING (self->fs_obj->device));
 }
 
-static void
-proxy_partition_func_data (OcfsPartitionInfo *info,
-			   gpointer           pdata)
+static PyTypeObject DInode_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/* ob_size */
+  "ocfs2.DInode",			/* tp_name */
+  sizeof(DInode),			/* tp_basicsize */
+  0,					/* tp_itemsize */
+  (destructor)dinode_dealloc,		/* tp_dealloc */
+  0,					/* tp_print */
+  0,					/* tp_getattr */
+  0,					/* tp_setattr */
+  0,					/* tp_compare */
+  (reprfunc)dinode_repr,		/* tp_repr */
+  0,					/* tp_as_number */
+  0,					/* tp_as_sequence */
+  0,					/* tp_as_mapping */
+  0,					/* tp_hash */
+  0,					/* tp_call */
+  0,					/* tp_str */
+  0,					/* tp_getattro */
+  0,					/* tp_setattro */
+  0,					/* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,			/* tp_flags */
+  NULL,					/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  0,					/* tp_iter */
+  0,					/* tp_iternext */
+  0,					/* tp_methods */
+  dinode_members,			/* tp_members */
+  dinode_getsets,			/* tp_getset */
+};
+
+static PyObject *
+dinode_new (Filesystem   *fs_obj,
+	    ocfs2_dinode *dinode)
 {
-  ProxyData *data = pdata;
+  DInode *self;
 
-  PyObject_CallFunction (data->func, "sssO",
-			 info->device, info->mountpoint, info->fstype,
-			 data->data);
+  self = PyObject_NEW (DInode, &DInode_Type);
+
+  if (self == NULL)
+    return NULL;
+
+  Py_INCREF (fs_obj);
+  self->fs_obj = fs_obj;
+
+  memcpy (&self->dinode, dinode, sizeof (*dinode));
+
+  return (PyObject *) self;
 }
 
+static PyObject *
+dir_entry_name (DirEntry *self, void *closure)
+{
+  return PyString_FromStringAndSize (self->dentry.name, self->dentry.name_len);
+}
+
+static PyObject *
+dir_entry_inode (DirEntry *self, void *closure)
+{
+  return PyLong_FromUnsignedLongLong (self->dentry.inode);
+}
+
+static PyGetSetDef dir_entry_getsets[] = {
+  {"name", (getter)dir_entry_name, (setter)0},
+  {"inode", (getter)dir_entry_inode, (setter)0},
+  {NULL}
+};
+
+static PyMemberDef dir_entry_members[] = {
+  {"rec_len", T_USHORT, offsetof (DirEntry, dentry.rec_len), RO},
+  {"file_type", T_UBYTE, offsetof (DirEntry, dentry.file_type), RO},
+  {"fs", T_OBJECT, offsetof (DirEntry, fs_obj), RO},
+  {0}
+};
+
 static void
-proxy_unmounted_func (OcfsPartitionInfo *info,
-		      gpointer           pdata)
+dir_entry_dealloc (DirEntry *self)
 {
-  ProxyData *data = pdata;
+  PyObject_DEL (self);
+}
 
-  PyObject_CallFunction (data->func, "ss", info->device, info->fstype);
+static PyObject *
+dir_entry_repr (DirEntry *self)
+{
+  char name[OCFS2_MAX_FILENAME_LEN + 1];
+
+  strncpy (name, self->dentry.name, self->dentry.name_len);
+  name[self->dentry.name_len] = '\0';
+
+  return PyString_FromFormat ("<ocfs2.DirEntry '%s' on %s>", name,
+			      PyString_AS_STRING (self->fs_obj->device));
 }
 
+static PyTypeObject DirEntry_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/* ob_size */
+  "ocfs2.DirEntry",			/* tp_name */
+  sizeof(DirEntry),			/* tp_basicsize */
+  0,					/* tp_itemsize */
+  (destructor)dir_entry_dealloc,	/* tp_dealloc */
+  0,					/* tp_print */
+  0,					/* tp_getattr */
+  0,					/* tp_setattr */
+  0,					/* tp_compare */
+  (reprfunc)dir_entry_repr,		/* tp_repr */
+  0,					/* tp_as_number */
+  0,					/* tp_as_sequence */
+  0,					/* tp_as_mapping */
+  0,					/* tp_hash */
+  0,					/* tp_call */
+  0,					/* tp_str */
+  0,					/* tp_getattro */
+  0,					/* tp_setattro */
+  0,					/* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,			/* tp_flags */
+  NULL,					/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  0,					/* tp_iter */
+  0,					/* tp_iternext */
+  0,					/* tp_methods */
+  dir_entry_members,			/* tp_members */
+  dir_entry_getsets,			/* tp_getset */
+};
+
+static PyObject *
+dir_entry_new (Filesystem             *fs_obj,
+	       struct ocfs2_dir_entry *dentry)
+{
+  DirEntry *self;
+
+  self = PyObject_NEW (DirEntry, &DirEntry_Type);
+
+  if (self == NULL)
+    return NULL;
+
+  Py_INCREF (fs_obj);
+  self->fs_obj = fs_obj;
+
+  memcpy (&self->dentry, dentry, sizeof (*dentry));
+
+  return (PyObject *) self;
+}
+
+#define SUPER_U64_GETTER(name) \
+  static PyObject *							\
+  super_ ## name (SuperBlock *self, void *closure)			\
+  {									\
+    return PyLong_FromUnsignedLongLong (self->super.s_ ## name);	\
+  }
+
+#define SUPER_GETTER_ENTRY(name) \
+  {"s_" #name, (getter)super_ ## name, (setter)0}
+
+SUPER_U64_GETTER (lastcheck)
+SUPER_U64_GETTER (root_blkno)
+SUPER_U64_GETTER (system_dir_blkno)
+SUPER_U64_GETTER (first_cluster_group)
+
+static PyObject *
+super_uuid (SuperBlock *self, void *closure)
+{
+  return PyString_FromStringAndSize (self->super.s_uuid,
+                                     sizeof (self->super.s_uuid));
+}
+
+static PyObject *
+super_uuid_unparsed (SuperBlock *self, void *closure)
+{
+  char buf[40];
+
+  uuid_unparse (self->super.s_uuid, buf);
+  return PyString_FromString (buf);
+}
+
+static PyGetSetDef super_getsets[] = {
+  SUPER_GETTER_ENTRY (lastcheck),
+  SUPER_GETTER_ENTRY (root_blkno),
+  SUPER_GETTER_ENTRY (system_dir_blkno),
+  SUPER_GETTER_ENTRY (first_cluster_group),
+  SUPER_GETTER_ENTRY (uuid),
+  {"uuid_unparsed", (getter)super_uuid_unparsed, (setter)0},
+  {NULL}
+};
+
+#undef SUPER_U64_GETTER
+#undef SUPER_GETTER_ENTRY
+
+#define SUPER_U16_MEMBER(name) \
+  {"s_" #name, T_USHORT, offsetof (SuperBlock, super.s_ ## name), RO}
+#define SUPER_U32_MEMBER(name) \
+  {"s_" #name, T_UINT, offsetof (SuperBlock, super.s_ ## name), RO}
+
+static PyMemberDef super_members[] = {
+  SUPER_U16_MEMBER (major_rev_level),
+  SUPER_U16_MEMBER (minor_rev_level),
+  SUPER_U16_MEMBER (mnt_count),
+  SUPER_U16_MEMBER (state),
+  SUPER_U16_MEMBER (errors),
+  SUPER_U32_MEMBER (checkinterval),
+  SUPER_U32_MEMBER (creator_os),
+  SUPER_U32_MEMBER (feature_compat),
+  SUPER_U32_MEMBER (feature_incompat),
+  SUPER_U32_MEMBER (feature_ro_compat),
+  SUPER_U32_MEMBER (blocksize_bits),
+  SUPER_U32_MEMBER (clustersize_bits),
+  SUPER_U16_MEMBER (max_nodes),
+  {"s_label", T_STRING_INPLACE, offsetof (SuperBlock, super.s_label), RO},
+  {"fs", T_OBJECT, offsetof (SuperBlock, fs_obj), RO},
+  {0}
+};
+
+#undef SUPER_U16_MEMBER
+#undef SUPER_U32_MEMBER
+
 static void
-proxy_unmounted_func_data (OcfsPartitionInfo *info,
-			   gpointer           pdata)
+super_dealloc (SuperBlock *self)
 {
-  ProxyData *data = pdata;
+  Py_DECREF (self->fs_obj);
+  PyObject_DEL (self);
+}
 
-  PyObject_CallFunction (data->func, "ssO", info->device, info->fstype,
-			 data->data);
+static PyObject *
+super_repr (SuperBlock *self)
+{
+  return PyString_FromFormat ("<ocfs2.SuperBlock on %s>",
+			      PyString_AS_STRING (self->fs_obj->device));
 }
 
+static PyTypeObject SuperBlock_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/* ob_size */
+  "ocfs2.SuperBlock",			/* tp_name */
+  sizeof(SuperBlock),			/* tp_basicsize */
+  0,					/* tp_itemsize */
+  (destructor)super_dealloc,		/* tp_dealloc */
+  0,					/* tp_print */
+  0,					/* tp_getattr */
+  0,					/* tp_setattr */
+  0,					/* tp_compare */
+  (reprfunc)super_repr,			/* tp_repr */
+  0,					/* tp_as_number */
+  0,					/* tp_as_sequence */
+  0,					/* tp_as_mapping */
+  0,					/* tp_hash */
+  0,					/* tp_call */
+  0,					/* tp_str */
+  0,					/* tp_getattro */
+  0,					/* tp_setattro */
+  0,					/* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,			/* tp_flags */
+  NULL,					/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  0,					/* tp_iter */
+  0,					/* tp_iternext */
+  0,					/* tp_methods */
+  super_members,			/* tp_members */
+  super_getsets,			/* tp_getset */
+};
 
 static PyObject *
-partition_list (PyObject *self,
-		PyObject *args,
-		PyObject *kwargs)
+super_new (Filesystem   *fs_obj,
+	   ocfs2_dinode *fs_super)
 {
-  ProxyData              proxy_data;
-  OcfsPartitionListFunc  func;
-  PyObject              *py_func, *py_data = NULL;
-  gchar                 *filter = NULL, *fstype = NULL;
-  gboolean               unmounted = FALSE, async = FALSE;
+  SuperBlock *self;
 
-  static gchar *kwlist[] = {
-    "callback", "data",
-    "filter", "fstype", "unmounted", "async",
-    NULL
-  };
+  self = PyObject_NEW (SuperBlock, &SuperBlock_Type);
 
-  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
-				    "O|Ozzii:partition_list", kwlist,
-				    &py_func, &py_data,
-				    &filter, &fstype, &unmounted, &async))
+  if (self == NULL)
     return NULL;
 
-  if (!PyCallable_Check (py_func))
+  Py_INCREF (fs_obj);
+  self->fs_obj = fs_obj;
+
+  memcpy (&self->super, &fs_super->id2.i_super, sizeof (self->super));
+
+  return (PyObject *) self;
+}
+
+static void
+dir_scan_iter_dealloc (DirScanIter *self)
+{
+  if (self->scan)
+    ocfs2_close_dir_scan (self->scan);
+
+  Py_XDECREF (self->fs_obj);
+  PyObject_DEL (self);
+}
+
+static PyObject *
+dir_scan_iter_getiter (DirScanIter *self)
+{
+  Py_INCREF (self);
+  return (PyObject *) self;
+}
+
+static PyObject *
+dir_scan_iter_next (DirScanIter *self)
+{
+  errcode_t              ret;
+  struct ocfs2_dir_entry dirent;
+
+  if (self->scan == NULL)
     {
-      PyErr_SetString (PyExc_TypeError, "callback must be a callable object");
+      PyErr_SetNone (PyExc_StopIteration);
       return NULL;
     }
 
-  proxy_data.func = py_func;
+  CHECK_ERROR (ocfs2_get_next_dir_entry (self->scan, &dirent));
 
-  if (py_data)
+  if (dirent.rec_len == 0)
     {
-      proxy_data.data = py_data;
+      ocfs2_close_dir_scan (self->scan);
+      self->scan = NULL;
 
-      if (unmounted)
-	func = proxy_unmounted_func_data;
-      else
-	func = proxy_partition_func_data;
+      Py_DECREF (self->fs_obj);
+      self->fs_obj = NULL;
+
+      PyErr_SetNone (PyExc_StopIteration);
+      return NULL;
     }
-  else
+
+  return dir_entry_new (self->fs_obj, &dirent);
+}
+
+static PyTypeObject DirScanIter_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/* ob_size */
+  "ocfs2.DirScanIter",			/* tp_name */
+  sizeof(DirScanIter),			/* tp_basicsize */
+  0,					/* tp_itemsize */
+  (destructor)dir_scan_iter_dealloc,	/* tp_dealloc */
+  0,					/* tp_print */
+  0,					/* tp_getattr */
+  0,					/* tp_setattr */
+  0,					/* tp_compare */
+  0,					/* tp_repr */
+  0,					/* tp_as_number */
+  0,					/* tp_as_sequence */
+  0,					/* tp_as_mapping */
+  0,					/* tp_hash */
+  0,					/* tp_call */
+  0,					/* tp_str */
+  0,					/* tp_getattro */
+  0,					/* tp_setattro */
+  0,					/* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_ITER, /* tp_flags */
+  NULL,					/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  (getiterfunc)dir_scan_iter_getiter,	/* tp_iter */
+  (iternextfunc)dir_scan_iter_next,	/* tp_iternext */
+};
+
+static PyObject *
+dir_scan_iter_new (Filesystem     *fs_obj,
+		   ocfs2_dir_scan *scan)
+{
+  DirScanIter *self;
+
+  self = PyObject_NEW (DirScanIter, &DirScanIter_Type);
+
+  if (self == NULL)
     {
-      if (unmounted)
-	func = proxy_unmounted_func;
-      else
-	func = proxy_partition_func;
+      ocfs2_close_dir_scan (scan);
+      return NULL;
     }
 
-  ocfs_partition_list (func, &proxy_data, filter, fstype, unmounted, async);
+  Py_INCREF (fs_obj);
+  self->fs_obj = fs_obj;
 
-  Py_INCREF (Py_None);
+  self->scan = scan;
+
+  return (PyObject *) self;
+}
+
+static PyObject *
+fs_flush (Filesystem *self)
+{
+  errcode_t ret;
+
+  CHECK_ERROR (ocfs2_flush (self->fs));
+
+  Py_INCREF(Py_None);
   return Py_None;
 }
 
-static PyStructSequence_Field struct_ocfs2_super_fields[] = {
-  {"s_major_rev_level",  "major revision level"},
-  {"s_minor_rev_level",  "minor revision level"},
-  {"s_blocksize_bits",   "blocksize bits"},
-  {"s_clustersize_bits", "cluster size bits"},
-  {"s_max_nodes",        "maximum nodes"},
-  {"s_label",            "volume label"},
-  {"s_uuid",             "UUID"},
-  {0}
-};
+static PyObject *
+fs_clusters_to_blocks (Filesystem *self,
+		       PyObject   *args,
+		       PyObject   *kwargs)
+{
+  unsigned int clusters;
+  uint64_t     blocks;
 
-struct PyStructSequence_Desc struct_ocfs2_super_desc = {
-  "ocfs2.struct_ocfs2_super",
-  NULL,
-  struct_ocfs2_super_fields,
-  7,
-};
+  static char *kwlist[] = { "clusters", NULL };
 
-static PyTypeObject StructOcfs2SuperType;
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "I:clusters_to_blocks", kwlist,
+				    &clusters))
+    return NULL;
 
+  blocks = ocfs2_clusters_to_blocks (self->fs, clusters);
+
+  return PyLong_FromUnsignedLongLong (blocks);
+}
+
 static PyObject *
-get_super (PyObject *self,
-	   PyObject *args)
+fs_blocks_to_clusters (Filesystem *self,
+		       PyObject   *args,
+		       PyObject   *kwargs)
 {
-  gchar         *device;
-  errcode_t      ret;
-  ocfs2_filesys *fs;
-  PyObject      *v;
-  gint           index = 0;
-  GString       *uuid;
-  gint           i;
+  unsigned long long blocks;
+  uint32_t           clusters;
 
-  static gchar *fmt[] = { "%02x", "%02x", "%02x", "%02x",
-			  "-%02x", "%02x", "-%02x", "%02x", "-%02x", "%02x",
-			  "-%02x", "%02x", "%02x", "%02x", "%02x", "%02x" };
+  static char *kwlist[] = { "blocks", NULL };
 
-  if (!PyArg_ParseTuple (args, "s:get_super", &device))
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "K:blocks_to_clusters", kwlist,
+				    &blocks))
     return NULL;
 
-  ret = ocfs2_open (device, OCFS2_FLAG_RO, 0, 0, &fs);
-  if (ret)
-    {
-      PyErr_SetString (ocfs2_error, error_message (ret));
-      return NULL;
-    }
+  clusters = ocfs2_clusters_to_blocks (self->fs, blocks);
 
-  v = PyStructSequence_New (&StructOcfs2SuperType);
-  if (v == NULL)
+  return PyInt_FromLong (clusters);
+}
+
+static PyObject *
+fs_blocks_in_bytes (Filesystem *self,
+		    PyObject   *args,
+		    PyObject   *kwargs)
+{
+  unsigned long long bytes;
+  uint64_t           blocks;
+
+  static char *kwlist[] = { "bytes", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "K:blocks_in_bytes", kwlist,
+				    &bytes))
     return NULL;
 
-#define SET(m) \
-  PyStructSequence_SET_ITEM (v, index++, PyInt_FromLong \
-    (OCFS2_RAW_SB (fs->fs_super)->m));
+  blocks = ocfs2_blocks_in_bytes (self->fs, bytes);
 
-  SET (s_major_rev_level);
-  SET (s_minor_rev_level);
-  SET (s_blocksize_bits);
-  SET (s_clustersize_bits);
-  SET (s_max_nodes);
+  return PyLong_FromUnsignedLongLong (blocks);
+}
 
-#undef SET
-#define SET(m) \
-  PyStructSequence_SET_ITEM \
-    (v, index++, PyString_FromString (OCFS2_RAW_SB (fs->fs_super)->m));
+static PyObject *
+fs_clusters_in_blocks (Filesystem *self,
+		       PyObject   *args,
+		       PyObject   *kwargs)
+{
+  unsigned long long blocks;
+  uint32_t           clusters;
 
-  PyStructSequence_SET_ITEM
-    (v, index++, PyString_FromString (OCFS2_RAW_SB (fs->fs_super)->s_label));
+  static char *kwlist[] = { "blocks", NULL };
 
-#undef SET
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "K:clusters_in_blocks", kwlist,
+				    &blocks))
+    return NULL;
 
-  uuid = g_string_sized_new (32);
+  clusters = ocfs2_clusters_in_blocks (self->fs, blocks);
 
-  for (i = 0; i < 16; i++)
-    g_string_append_printf (uuid, fmt[i],
-			    OCFS2_RAW_SB (fs->fs_super)->s_uuid[i]);
+  return PyInt_FromLong (clusters);
+}
 
-  PyStructSequence_SET_ITEM (v, index++, PyString_FromString (uuid->str));
+static PyObject *
+fs_block_out_of_range (Filesystem *self,
+		       PyObject   *args,
+		       PyObject   *kwargs)
+{
+  unsigned long long block;
+  int                ret;
 
-  g_string_free (uuid, TRUE);
+  static char *kwlist[] = { "block", NULL };
 
-  ocfs2_close (fs);
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "K:block_out_of_range", kwlist,
+				    &block))
+    return NULL;
 
-  if (PyErr_Occurred())
+  ret = ocfs2_block_out_of_range(self->fs, block);
+
+  return PyBool_FromLong (ret);
+}
+
+static PyObject *
+fs_lookup_system_inode (Filesystem *self,
+                        PyObject   *args,
+                        PyObject   *kwargs)
+{
+  errcode_t ret;
+  int       type, node_num = -1;
+  uint64_t  blkno;
+
+  static char *kwlist[] = { "type", "node_num", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "i|i:lookup_system_inode", kwlist,
+				    &type, &node_num))
+    return NULL;
+
+  CHECK_ERROR (ocfs2_lookup_system_inode(self->fs, type, node_num, &blkno));
+
+  return PyLong_FromUnsignedLongLong (blkno);
+}
+
+static PyObject *
+fs_read_cached_inode (Filesystem *self,
+                      PyObject   *args,
+                      PyObject   *kwargs)
+{
+  errcode_t           ret;
+  unsigned long long  blkno;
+  ocfs2_cached_inode *cinode;
+  PyObject           *dinode;
+
+  static char *kwlist[] = { "blkno", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "K:read_cached_inode", kwlist,
+				    &blkno))
+    return NULL;
+
+  CHECK_ERROR (ocfs2_read_cached_inode (self->fs, blkno, &cinode));
+
+  dinode = dinode_new (self, cinode->ci_inode);
+
+  /* XXX: error check */
+  ocfs2_free_cached_inode (self->fs, cinode);
+
+  return dinode;
+}
+
+typedef struct
+{
+  PyObject   *func;
+  PyObject   *data;
+  Filesystem *fs;
+} WalkData;
+
+static int
+walk_dirs (struct ocfs2_dir_entry *dirent,
+           int                     offset,
+	   int                     blocksize,
+	   char                   *buf,
+	   void                   *priv_data)
+{
+  PyObject *de;
+  WalkData *data = priv_data;
+
+  de = dir_entry_new (data->fs, dirent);
+
+  if (de == NULL)
+    return OCFS2_DIRENT_ERROR;
+
+  /* XXX: handle errors */
+  if (data->data)
+    PyObject_CallFunction (data->func, "OiiO", de, offset, blocksize,
+			   data->data);
+  else
+    PyObject_CallFunction (data->func, "Oii", de, offset, blocksize);
+
+  Py_DECREF (de);
+
+  return 0;
+}
+
+static PyObject *
+fs_dir_iterate (Filesystem *self,
+                PyObject   *args,
+                PyObject   *kwargs)
+{
+  errcode_t  ret;
+  WalkData   wdata;
+  PyObject  *py_func, *py_data = NULL, *py_dir = NULL;
+  uint64_t   dir;
+  int        flags = OCFS2_DIRENT_FLAG_EXCLUDE_DOTS;
+
+  static char *kwlist[] = { "callback", "data", "dir", "flags", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "O|OOi:dir_iterate", kwlist,
+				    &py_func, &py_data, &py_dir, &flags))
+    return NULL;
+
+  if (!PyCallable_Check (py_func))
     {
-      Py_DECREF (v);
+      PyErr_SetString (PyExc_TypeError, "callback must be a callable object");
       return NULL;
     }
 
-  return v;
+  if (py_dir == NULL || py_dir == Py_None)
+    dir = self->fs->fs_root_blkno;
+  else if (DirEntry_Check (py_dir))
+    dir = ((DirEntry *) py_dir)->dentry.inode;
+  else if (PyInt_Check (py_dir))
+    dir = PyInt_AsUnsignedLongMask (py_dir);
+  else if (PyLong_Check (py_dir))
+    dir = PyLong_AsUnsignedLongLongMask (py_dir);
+  else
+    {
+      PyErr_SetString (PyExc_TypeError, "dir must be DirEntry or integer");
+      return NULL;
+    }
+
+  Py_INCREF (py_func);
+  wdata.func = py_func;
+
+  Py_XINCREF (py_data);
+  wdata.data = py_data;
+
+  wdata.fs = self;
+
+  /* XXX: handle errors */
+  ret = ocfs2_dir_iterate (self->fs, dir, flags, NULL, walk_dirs, &wdata);
+
+  Py_DECREF (py_func);
+  Py_XDECREF (py_data);
+
+  Py_INCREF (Py_None);
+  return Py_None;
 }
 
 static PyObject *
-get_space_usage (PyObject *self,
-		 PyObject *args)
+fs_dir_scan (Filesystem *self,
+             PyObject   *args,
+             PyObject   *kwargs)
 {
-  gchar              *device;
-  errcode_t           ret;
-  PyObject           *v = NULL;
-  ocfs2_filesys      *fs = NULL;
-  uint64_t            blkno;
-  ocfs2_cached_inode *cinode = NULL;
-  ocfs2_dinode       *bm;
-  uint32_t            numbits, freebits;
+  errcode_t       ret;
+  PyObject       *py_dir = NULL;
+  uint64_t        dir;
+  int             flags = OCFS2_DIR_SCAN_FLAG_EXCLUDE_DOTS;
+  ocfs2_dir_scan *scan;
 
-  if (!PyArg_ParseTuple (args, "s:get_super", &device))
+  static char *kwlist[] = { "dir", "flags", NULL };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "|Oi:dir_scan", kwlist,
+				    &py_dir, &flags))
     return NULL;
 
-#define CHECK_ERROR(call)			G_STMT_START {	\
-  ret = call;							\
-  if (ret)							\
-    {								\
-      PyErr_SetString (ocfs2_error, error_message (ret));	\
-      goto bail;						\
-    }								\
-} G_STMT_END
-  
-  CHECK_ERROR (ocfs2_open (device, OCFS2_FLAG_RO, 0, 0, &fs));
-  CHECK_ERROR (ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE, -1,
-					 &blkno));
-  CHECK_ERROR (ocfs2_read_cached_inode(fs, blkno, &cinode));
+  if (py_dir == NULL || py_dir == Py_None)
+    dir = self->fs->fs_root_blkno;
+  else if (DirEntry_Check (py_dir))
+    dir = ((DirEntry *) py_dir)->dentry.inode;
+  else if (PyInt_Check (py_dir))
+    dir = PyInt_AsUnsignedLongMask (py_dir);
+  else if (PyLong_Check (py_dir))
+    dir = PyLong_AsUnsignedLongLongMask (py_dir);
+  else
+    {
+      PyErr_SetString (PyExc_TypeError, "dir must be DirEntry or integer");
+      return NULL;
+    }
 
-#undef CHECK_ERROR
+  CHECK_ERROR (ocfs2_open_dir_scan (self->fs, dir, flags, &scan));
 
-  bm = cinode->ci_inode;
+  return dir_scan_iter_new (self, scan);
+}
 
-  numbits = le32_to_cpu (bm->id1.bitmap1.i_total);
-  freebits = numbits - le32_to_cpu (bm->id1.bitmap1.i_used);
+static PyMethodDef fs_methods[] = {
+  {"flush", (PyCFunction)fs_flush, METH_NOARGS},
+  {"clusters_to_blocks", (PyCFunction)fs_clusters_to_blocks, METH_VARARGS | METH_KEYWORDS},
+  {"blocks_to_clusters", (PyCFunction)fs_blocks_to_clusters, METH_VARARGS | METH_KEYWORDS},
+  {"blocks_in_bytes", (PyCFunction)fs_blocks_in_bytes, METH_VARARGS | METH_KEYWORDS},
+  {"clusters_in_blocks", (PyCFunction)fs_clusters_in_blocks, METH_VARARGS | METH_KEYWORDS},
+  {"block_out_of_range", (PyCFunction)fs_block_out_of_range, METH_VARARGS | METH_KEYWORDS},
+  {"lookup_system_inode", (PyCFunction)fs_lookup_system_inode, METH_VARARGS | METH_KEYWORDS},
+  {"read_cached_inode", (PyCFunction)fs_read_cached_inode, METH_VARARGS | METH_KEYWORDS},
+  {"dir_iterate", (PyCFunction)fs_dir_iterate, METH_VARARGS | METH_KEYWORDS},
+  {"iterdir", (PyCFunction)fs_dir_scan, METH_VARARGS | METH_KEYWORDS},
+  {NULL, NULL}
+};
 
-  v = PyTuple_New (2);
+static PyObject *
+fs_super (Filesystem *self, void *closure)
+{
+  return super_new (self, self->fs->fs_super);
+}
 
-  PyTuple_SetItem(v, 0, PyInt_FromLong (numbits));
-  PyTuple_SetItem(v, 1, PyInt_FromLong (freebits));
+static PyObject *
+fs_orig_super (Filesystem *self, void *closure)
+{
+  return super_new (self, self->fs->fs_orig_super);
+}
 
-bail:
-  if (cinode)
-    ocfs2_free_cached_inode(fs, cinode);
+static PyObject *
+fs_uuid_str (Filesystem *self, void *closure)
+{
+  return PyString_FromString (self->fs->uuid_str);
+}
 
-  if (fs)
-    ocfs2_close (fs);
+#define FS_U32_GETTER(name) \
+  static PyObject *							\
+  fs_ ## name (Filesystem *self, void *closure)				\
+  {									\
+    return PyInt_FromLong (self->fs->fs_ ## name);			\
+  }
 
-  return v;
+#define FS_UINT_GETTER FS_U32_GETTER
+
+#define FS_U64_GETTER(name) \
+  static PyObject *							\
+  fs_ ## name (Filesystem *self, void *closure)				\
+  {									\
+    return PyLong_FromUnsignedLongLong (self->fs->fs_ ## name);		\
+  }
+
+#define FS_GETTER_ENTRY(name) \
+  {"fs_" #name, (getter)fs_ ## name, (setter)0}
+
+FS_U32_GETTER  (flags)
+FS_UINT_GETTER (blocksize)
+FS_UINT_GETTER (clustersize)
+FS_U32_GETTER  (clusters)
+FS_U64_GETTER  (blocks)
+FS_U32_GETTER  (umask)
+FS_U64_GETTER  (root_blkno)
+FS_U64_GETTER  (sysdir_blkno)
+FS_U64_GETTER  (first_cg_blkno)
+
+static PyGetSetDef fs_getsets[] = {
+  FS_GETTER_ENTRY (flags),
+  FS_GETTER_ENTRY (super),
+  FS_GETTER_ENTRY (orig_super),
+  FS_GETTER_ENTRY (blocksize),
+  FS_GETTER_ENTRY (clustersize),
+  FS_GETTER_ENTRY (clusters),
+  FS_GETTER_ENTRY (blocks),
+  FS_GETTER_ENTRY (umask),
+  FS_GETTER_ENTRY (root_blkno),
+  FS_GETTER_ENTRY (sysdir_blkno),
+  FS_GETTER_ENTRY (first_cg_blkno),
+  {"uuid_str", (getter)fs_uuid_str, (setter)0},
+  {NULL}
+};
+
+#undef FS_UINT_GETTER
+#undef FS_U32_GETTER
+#undef FS_U64_GETTER
+#undef FS_GETTER_ENTRY
+
+static PyMemberDef fs_members[] = {
+  {"device", T_OBJECT, offsetof (Filesystem, device), RO},
+  {"fs_devname", T_OBJECT, offsetof (Filesystem, device), RO},
+  {0}
+};
+
+static void
+fs_dealloc (Filesystem *self)
+{
+  if (self->fs)
+    ocfs2_close (self->fs);
+
+  Py_XDECREF (self->device);
+  PyObject_DEL (self);
 }
 
+static PyObject *
+fs_repr (Filesystem *self)
+{
+  return PyString_FromFormat("<ocfs2.Filesystem on %s>",
+			     PyString_AS_STRING (self->device));
+}
+
+static int
+fs_init (Filesystem *self,
+	 PyObject *args,
+	 PyObject *kwargs)
+{
+  errcode_t     ret;
+  char         *device;
+  int           flags = OCFS2_FLAG_RO | OCFS2_FLAG_BUFFERED;
+  unsigned int  superblock = 0, blksize = 0;
+
+  static char *kwlist[] = { "device", "flags", "superblock", "blocksize",
+			    NULL };
+
+  if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+				   "s|iII:ocfs2.Filesystem.__init__", kwlist,
+				   &device, &flags, &superblock, &blksize))
+    return -1;
+
+  self->fs = NULL;
+  self->device = PyString_FromString (device);
+
+  if (self->device == NULL)
+    return -1;
+
+  ret = ocfs2_open (device, flags, superblock, blksize, &self->fs); 
+
+  if (ret)
+    {
+      Py_DECREF (self->device);
+      PyErr_SetString (ocfs2_error, error_message (ret));
+      return -1;
+    }
+
+  return 0;
+}
+
+static PyTypeObject Filesystem_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,					/* ob_size */
+  "ocfs2.Filesystem",			/* tp_name */
+  sizeof(Filesystem),			/* tp_basicsize */
+  0,					/* tp_itemsize */
+  (destructor)fs_dealloc,		/* tp_dealloc */
+  0,					/* tp_print */
+  0,					/* tp_getattr */
+  0,					/* tp_setattr */
+  0,					/* tp_compare */
+  (reprfunc)fs_repr,			/* tp_repr */
+  0,					/* tp_as_number */
+  0,					/* tp_as_sequence */
+  0,					/* tp_as_mapping */
+  0,					/* tp_hash */
+  0,					/* tp_call */
+  0,					/* tp_str */
+  0,					/* tp_getattro */
+  0,					/* tp_setattro */
+  0,					/* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT,			/* tp_flags */
+  NULL,					/* tp_doc */
+  0,					/* tp_traverse */
+  0,					/* tp_clear */
+  0,					/* tp_richcompare */
+  0,					/* tp_weaklistoffset */
+  0,					/* tp_iter */
+  0,					/* tp_iternext */
+  fs_methods,				/* tp_methods */
+  fs_members,				/* tp_members */
+  fs_getsets,				/* tp_getset */
+  0,					/* tp_base */
+  0,					/* tp_dict */
+  0,					/* tp_descr_get */
+  0,					/* tp_descr_set */
+  0,					/* tp_dictoffset */
+  (initproc)fs_init,			/* tp_init */
+  0,					/* tp_alloc */
+  0,					/* tp_new */
+};
+
 static PyMethodDef ocfs2_methods[] = {
-  {"partition_list", (PyCFunction)partition_list, METH_VARARGS | METH_KEYWORDS},
-  {"get_super", (PyCFunction)get_super, METH_VARARGS},
-  {"get_space_usage", (PyCFunction)get_space_usage, METH_VARARGS},
   {NULL,       NULL}    /* sentinel */
 };
 
+static void
+add_constants (PyObject *m)
+{
+#define ADD_INT_CONSTANT(name) \
+  PyModule_AddIntConstant (m, #name, OCFS2_ ## name)
+#define ADD_STR_CONSTANT(name) \
+  PyModule_AddStringConstant (m, #name, OCFS2_ ## name)
+
+  ADD_INT_CONSTANT (SUPER_BLOCK_BLKNO);
+
+  ADD_INT_CONSTANT (MIN_CLUSTERSIZE);
+  ADD_INT_CONSTANT (MAX_CLUSTERSIZE);
+
+  ADD_INT_CONSTANT (MIN_BLOCKSIZE);
+  ADD_INT_CONSTANT (MAX_BLOCKSIZE);
+
+  ADD_INT_CONSTANT (SUPER_MAGIC);
+
+  ADD_STR_CONSTANT (SUPER_BLOCK_SIGNATURE);
+  ADD_STR_CONSTANT (INODE_SIGNATURE);
+  ADD_STR_CONSTANT (EXTENT_BLOCK_SIGNATURE);
+  ADD_STR_CONSTANT (GROUP_DESC_SIGNATURE);
+
+  ADD_INT_CONSTANT (VALID_FL);
+  ADD_INT_CONSTANT (ORPHANED_FL);
+
+  ADD_INT_CONSTANT (SYSTEM_FL);
+  ADD_INT_CONSTANT (SUPER_BLOCK_FL);
+  ADD_INT_CONSTANT (LOCAL_ALLOC_FL);
+  ADD_INT_CONSTANT (BITMAP_FL);
+  ADD_INT_CONSTANT (JOURNAL_FL);
+  ADD_INT_CONSTANT (HEARTBEAT_FL);
+  ADD_INT_CONSTANT (CHAIN_FL);
+
+  ADD_INT_CONSTANT (JOURNAL_DIRTY_FL);
+  
+  ADD_INT_CONSTANT (ERROR_FS);
+
+  ADD_INT_CONSTANT (MAX_FILENAME_LEN);
+
+  ADD_INT_CONSTANT (MAX_NODES);
+
+  ADD_INT_CONSTANT (VOL_UUID_LEN);
+  ADD_INT_CONSTANT (MAX_VOL_LABEL_LEN);
+
+  ADD_INT_CONSTANT (MAX_CLUSTER_NAME_LEN);
+
+  ADD_INT_CONSTANT (MIN_JOURNAL_SIZE);
+  ADD_INT_CONSTANT (MAX_JOURNAL_SIZE);
+
+  ADD_INT_CONSTANT (FIRST_ONLINE_SYSTEM_INODE);
+  ADD_INT_CONSTANT (LAST_GLOBAL_SYSTEM_INODE);
+
+  ADD_INT_CONSTANT (FT_UNKNOWN);
+  ADD_INT_CONSTANT (FT_REG_FILE);
+  ADD_INT_CONSTANT (FT_DIR);
+  ADD_INT_CONSTANT (FT_CHRDEV);
+  ADD_INT_CONSTANT (FT_BLKDEV);
+  ADD_INT_CONSTANT (FT_FIFO);
+  ADD_INT_CONSTANT (FT_SOCK);
+  ADD_INT_CONSTANT (FT_SYMLINK);
+  ADD_INT_CONSTANT (FT_MAX);
+
+  ADD_INT_CONSTANT (LINK_MAX);
+
+  ADD_INT_CONSTANT (FLAG_RO);
+  ADD_INT_CONSTANT (FLAG_RW);
+  ADD_INT_CONSTANT (FLAG_CHANGED);
+  ADD_INT_CONSTANT (FLAG_DIRTY);
+  ADD_INT_CONSTANT (FLAG_SWAP_BYTES);
+  ADD_INT_CONSTANT (FLAG_BUFFERED);
+  ADD_INT_CONSTANT (FLAG_NO_REV_CHECK);
+
+  ADD_INT_CONSTANT (DIRENT_CHANGED);
+  ADD_INT_CONSTANT (DIRENT_ABORT);
+  ADD_INT_CONSTANT (DIRENT_ERROR);
+
+  ADD_INT_CONSTANT (DIRENT_FLAG_INCLUDE_EMPTY);
+  ADD_INT_CONSTANT (DIRENT_FLAG_INCLUDE_REMOVED);
+  ADD_INT_CONSTANT (DIRENT_FLAG_EXCLUDE_DOTS);
+
+#undef ADD_INT_CONSTANT
+#undef ADD_STR_CONSTANT
+
+#define ADD_INT_CONSTANT(name) \
+  PyModule_AddIntConstant (m, #name, name)
+
+  ADD_INT_CONSTANT (BAD_BLOCK_SYSTEM_INODE);
+  ADD_INT_CONSTANT (GLOBAL_INODE_ALLOC_SYSTEM_INODE);
+  ADD_INT_CONSTANT (SLOT_MAP_SYSTEM_INODE);
+  ADD_INT_CONSTANT (HEARTBEAT_SYSTEM_INODE);
+  ADD_INT_CONSTANT (GLOBAL_BITMAP_SYSTEM_INODE);
+  ADD_INT_CONSTANT (ORPHAN_DIR_SYSTEM_INODE);
+  ADD_INT_CONSTANT (EXTENT_ALLOC_SYSTEM_INODE);
+  ADD_INT_CONSTANT (INODE_ALLOC_SYSTEM_INODE);
+  ADD_INT_CONSTANT (JOURNAL_SYSTEM_INODE);
+  ADD_INT_CONSTANT (LOCAL_ALLOC_SYSTEM_INODE);
+  ADD_INT_CONSTANT (NUM_SYSTEM_INODES);
+
+  //ADD_INT_CONSTANT (MAX_NODE_NAME_LEN);
+
+#undef ADD_INT_CONSTANT
+}
+
 void
 initocfs2 (void)
 {
   PyObject *m;
 
+  DInode_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&DInode_Type) < 0)
+    return;
+
+  DirEntry_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&DirEntry_Type) < 0)
+    return;
+
+  SuperBlock_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&SuperBlock_Type) < 0)
+    return;
+
+  DirScanIter_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&DirScanIter_Type) < 0)
+    return;
+
+  Filesystem_Type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&Filesystem_Type) < 0)
+    return;
+
   initialize_ocfs_error_table ();
 
   m = Py_InitModule ("ocfs2", ocfs2_methods);
 
-  PyStructSequence_InitType (&StructOcfs2SuperType, &struct_ocfs2_super_desc);
-  Py_INCREF (&StructOcfs2SuperType);
-  PyModule_AddObject (m, "struct_ocfs2_super",
-                      (PyObject *) &StructOcfs2SuperType);
+  ocfs2_error = PyErr_NewException ("ocfs2.error", PyExc_RuntimeError, NULL);
 
-  ocfs2_error = PyErr_NewException ("ocfs2.error", PyExc_RuntimeError, NULL);
   if (ocfs2_error)
     {
       Py_INCREF (ocfs2_error);
       PyModule_AddObject (m, "error", ocfs2_error);
     }
 
-  PyModule_AddIntConstant (m, "MAX_VOL_LABEL_LEN", OCFS2_MAX_VOL_LABEL_LEN);
-  PyModule_AddIntConstant (m, "MAX_NODES", OCFS2_MAX_NODES);
+  Py_INCREF (&Filesystem_Type);
+  PyModule_AddObject (m, "Filesystem", (PyObject *) &Filesystem_Type);
 
-  PyModule_AddIntConstant (m, "MIN_BLOCKSIZE", OCFS2_MIN_BLOCKSIZE);
-  PyModule_AddIntConstant (m, "MAX_BLOCKSIZE", OCFS2_MAX_BLOCKSIZE);
+  add_constants (m);
 
-  PyModule_AddIntConstant (m, "MIN_CLUSTERSIZE", OCFS2_MIN_CLUSTERSIZE);
-  PyModule_AddIntConstant (m, "MAX_CLUSTERSIZE", OCFS2_MAX_CLUSTERSIZE);
-
   if (PyErr_Occurred ())
     Py_FatalError ("can't initialize module ocfs2");
 }

Modified: trunk/ocfs2console/ocfs2interface/partitionview.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/partitionview.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/partitionview.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -17,7 +17,7 @@
 
 import gtk
 
-import ocfs2
+from plist import partition_list
 
 COLUMN_DEVICE, COLUMN_MOUNTPOINT = range(2)
 
@@ -80,7 +80,7 @@
             if frame.child:
                 frame.child.destroy()
 
-            frame.add(info(device).widget)
+            frame.add(info(device))
             frame.show_all()
 
     def select_device(self, device):
@@ -122,8 +122,8 @@
         store.set_sort_func(COLUMN_DEVICE, list_compare)
         store.set_sort_column_id(COLUMN_DEVICE, gtk.SORT_ASCENDING)
 
-        ocfs2.partition_list(self.add_partition, data=old_device,
-                             filter=filter, fstype='ocfs2', async=True)
+        partition_list(self.add_partition, old_device,
+                       filter=filter, fstype='ocfs2', async=True)
 
         if len(store):
             if not self.selected:

Added: trunk/ocfs2console/ocfs2interface/plistmodule.c
===================================================================
--- trunk/ocfs2console/ocfs2interface/plistmodule.c	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/plistmodule.c	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,156 @@
+/*
+ * plistmodule.c
+ *
+ * Partition list python binding.
+ *
+ * Copyright (C) 2004, 2005 Oracle.  All rights reserved.
+ *
+ * Author: Manish Singh <manish.singh at oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have recieved a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <Python.h>
+
+#include <glib.h>
+
+#include "ocfsplist.h"
+
+typedef struct
+{
+  PyObject *func;
+  PyObject *data;
+
+  gboolean  mountpoint;
+  gboolean  seen_error;
+} ProxyData;
+
+
+static void
+proxy (OcfsPartitionInfo *info,
+       gpointer           pdata)
+{
+  ProxyData *data = pdata;
+  PyObject  *tuple, *val, *ret;
+  gint       len = 2, pos = 0;
+
+  if (data->seen_error)
+    return;
+
+  if (data->mountpoint) len++;
+  if (data->data)       len++;
+
+  tuple = PyTuple_New (len);
+
+  PyTuple_SET_ITEM (tuple, pos, PyString_FromString (info->device));
+  pos++;
+
+  if (data->mountpoint)
+    {
+      if (info->mountpoint == NULL)
+	{
+	  Py_INCREF (Py_None);
+	  val = Py_None;
+	}
+      else
+	val = PyString_FromString (info->mountpoint);
+
+      PyTuple_SET_ITEM (tuple, pos, val);
+      pos++;
+    }
+
+  PyTuple_SET_ITEM (tuple, pos, PyString_FromString (info->fstype));
+  pos++;
+
+  if (data->data)
+    {
+      Py_INCREF (data->data);
+      PyTuple_SET_ITEM (tuple, pos, data->data);
+      pos++;
+    }
+
+  ret = PyObject_CallObject (data->func, tuple);
+
+  if (ret == NULL)
+    {
+      PyErr_Print();
+      data->seen_error = TRUE;
+    }
+
+  Py_DECREF (tuple);
+}
+
+static PyObject *
+partition_list (PyObject *self,
+		PyObject *args,
+		PyObject *kwargs)
+{
+  ProxyData  pdata;
+  PyObject  *py_func, *py_data = NULL;
+  gchar     *filter = NULL, *fstype = NULL;
+  gboolean   unmounted = FALSE, async = FALSE;
+
+  static gchar *kwlist[] = {
+    "callback", "data",
+    "filter", "fstype", "unmounted", "async",
+    NULL
+  };
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+				    "O|Ozzii:partition_list", kwlist,
+				    &py_func, &py_data,
+				    &filter, &fstype, &unmounted, &async))
+    return NULL;
+
+  if (!PyCallable_Check (py_func))
+    {
+      PyErr_SetString (PyExc_TypeError, "callback must be a callable object");
+      return NULL;
+    }
+
+  Py_INCREF (py_func);
+  pdata.func = py_func;
+
+  Py_XINCREF (py_data);
+  pdata.data = py_data;
+
+  pdata.mountpoint = !unmounted;
+  pdata.seen_error = FALSE;
+
+  ocfs_partition_list (proxy, &pdata, filter, fstype, unmounted, async);
+
+  Py_DECREF (py_func);
+  Py_XDECREF (py_data);
+
+  Py_INCREF (Py_None);
+  return Py_None;
+}
+
+static PyMethodDef plist_methods[] = {
+  {"partition_list", (PyCFunction)partition_list, METH_VARARGS | METH_KEYWORDS},
+  {NULL,       NULL}    /* sentinel */
+};
+
+void
+initplist (void)
+{
+  PyObject *m;
+
+  m = Py_InitModule ("plist", plist_methods);
+
+  if (PyErr_Occurred ())
+    Py_FatalError ("can't initialize module plist");
+}

Added: trunk/ocfs2console/ocfs2interface/pushconfig.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/pushconfig.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/pushconfig.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -0,0 +1,83 @@
+# OCFS2Console - GUI frontend for OCFS2 management and debugging
+# Copyright (C) 2005 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
+
+import gobject
+
+from terminal import TerminalDialog, terminal_ok as push_config_ok
+
+CONFIG_FILE = '/etc/ocfs2/cluster.conf'
+
+def push_config(parent):
+    commands = generate_commands(hosts)
+
+    title = 'Propagate Cluster Configuration'
+
+    dialog = TerminalDialog(parent=parent, title=title)
+    terminal = dialog.terminal
+
+    terminal.connect('child-exited', child_exited, dialog)
+
+    command = None
+    gobject.idle_add(start_command, terminal, command, dialog)
+
+    dialog.show_all()
+
+    while 1:
+        dialog.run()
+
+        if dialog.finished:
+            break
+
+        msg = ('Cluster propagation is still running. You should not close '
+               'this window until it is finished')
+
+        info = gtk.MessageDialog(parent=dialog,
+                                 flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+                                 type=gtk.MESSAGE_WARNING,
+                                 buttons=gtk.BUTTONS_CLOSE,
+                                 message_format=msg)
+        info.run()
+        info.destroy()
+ 
+    dialog.destroy()
+
+def start_command(terminal, command, dialog):
+    terminal.fork_command(command=command[0], argv=command)
+    return False
+
+def child_exited(terminal, dialog):
+    dialog.finished = True
+
+def fsck_command(device, check):
+    command = list(base_command)
+
+    if check:
+        command.append('-n')
+    else:
+        command.append('-y')
+
+    command.append("'%s'" % device)
+
+    realcommand = '%s; sleep 1' % ' '.join(command)
+
+    return ['/bin/sh', '-c', realcommand]
+
+def main():
+    push_config(None)
+
+if __name__ == '__main__':
+    main()

Modified: trunk/ocfs2console/ocfs2interface/tune.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/tune.py	2005-04-13 02:00:52 UTC (rev 798)
+++ trunk/ocfs2console/ocfs2interface/tune.py	2005-04-13 02:02:31 UTC (rev 799)
@@ -30,12 +30,11 @@
     def __init__(self, device=None):
         VolumeLabel.__init__(self)
 
-        if device:
-            try:
-                super = ocfs2.get_super(device)
-                self.set_text(super.s_label)
-            except ocfs2.error:
-                pass
+        try:
+            fs = ocfs2.Filesystem(device)
+            self.set_text(fs.fs_super.s_label)
+        except ocfs2.error:
+            pass
 
     title = 'Changing Label'
     action = 'Changing label'
@@ -46,9 +45,8 @@
     def __init__(self, device=None):
         NumNodes.__init__(self)
 
-        if device:
-            super = ocfs2.get_super(device)
-            self.set_range(super.s_max_nodes, ocfs2.MAX_NODES)
+        fs = ocfs2.Filesystem(device)
+        self.set_range(fs.fs_super.s_max_nodes, ocfs2.MAX_NODES)
 
     title = 'Edit Node Count'
     action = 'Changing node count'
@@ -59,8 +57,10 @@
     try:
         widget = widget_type(device)
     except ocfs2.error:
-        error_box(parent, 'Could not get current %s for device $s' %
-                          (widget_type.label.lower(), device))
+        desc = widget_type.label.replace('_', '')
+        desc = desc.lower()
+        error_box(parent, 'Could not get current %s for device %s' %
+                          (desc, device))
         return False
 
     dialog = Dialog(parent=parent, title=widget_type.title,



More information about the Ocfs2-tools-commits mailing list