[Ocfs2-tools-commits] manish commits r743 - trunk/ocfs2console/ocfs2interface

svn-commits at oss.oracle.com svn-commits at oss.oracle.com
Sun Mar 27 16:06:25 CST 2005


Author: manish
Date: 2005-03-27 16:06:23 -0600 (Sun, 27 Mar 2005)
New Revision: 743

Modified:
   trunk/ocfs2console/ocfs2interface/clconfig.py
Log:
Cluster config now has a commit phase, which means you can edit and remove
nodes you haven't committed to the config.

The GUI is also prettied up.


Modified: trunk/ocfs2console/ocfs2interface/clconfig.py
===================================================================
--- trunk/ocfs2console/ocfs2interface/clconfig.py	2005-03-27 22:05:21 UTC (rev 742)
+++ trunk/ocfs2console/ocfs2interface/clconfig.py	2005-03-27 22:06:23 UTC (rev 743)
@@ -16,77 +16,125 @@
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
 
 import os
+import types
 
 import gtk
+import pango
 
 from cStringIO import StringIO
 
-from guiutil import set_props, error_box
-
+from guiutil import Dialog, set_props, error_box
 from process import Process
 from ipwidget import IPEditor, IPMissing, IPError
 
-COLUMN_NAME, COLUMN_NODE, COLUMN_IP_ADDR, COLUMN_IP_PORT = range(4)
+CLUSTER_NAME = 'ocfs2'
+CONFIG_FS_PATH = '/usys/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_NAME,    'Name',       gtk.Entry),
-    (COLUMN_NODE,    'Node',       None),
-    (COLUMN_IP_ADDR, 'IP Address', IPEditor),
-    (COLUMN_IP_PORT, 'IP Port',    gtk.SpinButton)
+    (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)
 )
 
-class ConfError(Exception):
+class ConfigError(Exception):
     pass
 
-class ClusterConf(gtk.HBox):
-    def __init__(self, toplevel=None):
-        self.toplevel = toplevel
+class ClusterConfig(Dialog):
+    def __init__(self, parent=None):
+        self.new_nodes = 0
 
-        self.get_cluster_state()
+        Dialog.__init__(self, parent=parent, title='Cluster Configurator',
+                        buttons=(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY,
+                                 gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
 
-        gtk.HBox.__init__(self, spacing=4)
-        self.set_border_width(4)
+        frame = gtk.Frame()
+        frame.set_shadow_type(gtk.SHADOW_NONE)
+        self.vbox.add(frame)
 
-        self.tv = gtk.TreeView(self.store)
-        self.tv.set_size_request(350, 200)
+        label = gtk.Label()
+        label.set_text_with_mnemonic('_Nodes:')
+        frame.set_label_widget(label)
 
-        for col, title, widget_type in fields:
-            self.tv.insert_column_with_attributes(-1, title,
-                                                  gtk.CellRendererText(),
-                                                  text=col)
+        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)
-        self.pack_start(scrl_win)
+        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)
-        self.pack_end(vbbox, expand=False, fill=False)
+        hbox.pack_end(vbbox, expand=False, fill=False)
 
-        button = gtk.Button(stock=gtk.STOCK_ADD)
-        button.connect('clicked', self.add_node)
-        vbbox.add(button)
+        self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
+        self.add_button.connect('clicked', self.add_node)
+        vbbox.add(self.add_button)
 
-        button = gtk.Button(stock=gtk.STOCK_APPLY)
-        button.connect('clicked', self.apply_changes)
-        vbbox.add(button)
+        try:
+            edit_construct_args = {'stock': gtk.STOCK_EDIT}
+        except AttributeError:
+            edit_construct_args = {'label': '_Edit'}
 
-    def get_cluster_state(self):
-        command = 'o2cb_ctl -I -t node -o'
+        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)
 
-        o2cb_ctl = Process(command, 'Cluster Control', 'Querying nodes...',
-                           self.toplevel)
+        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(str, str, str, str)
+        self.store = gtk.ListStore(*[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)
@@ -95,21 +143,190 @@
             if line.startswith('#'):
                 continue
 
+            data = list((None,) * len(fields))
+
             try:
-                name, cluster, node, ip_addr, ip_port, state = line.split(':')
+                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 == 'ocfs2':
-                iter = self.store.append((name, node, ip_addr, ip_port))
+            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):
-        toplevel = self.get_toplevel()
+        node_attrs = self.node_query(title='Add Node')
 
-        dialog = gtk.Dialog(parent=toplevel, title='Add Node',
-                            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                                     gtk.STOCK_OK,     gtk.RESPONSE_OK))
+        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,
@@ -119,7 +336,7 @@
         widgets = []
         row = 0
 
-        for col, title, widget_type in fields:
+        for col, title, widget_type, field_type in fields:
             if widget_type is None:
                 widgets.append(None)
                 continue
@@ -134,22 +351,31 @@
             if isinstance(widget, gtk.SpinButton):
                 widget.set_numeric(True)
 
-                adjustment = gtk.Adjustment(7777, 1000, 30000, 1, 100) 
+                adjustment = gtk.Adjustment(PORT_DEFAULT,
+                                            PORT_MINIMUM, PORT_MAXIMUM,
+                                            1, 100) 
+
                 widget.set_adjustment(adjustment)
+                widget.set_value(PORT_DEFAULT)
 
-                widget.set_value(7777)
-        
             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
+                return None
 
+            ip_port = widgets[COLUMN_IP_PORT].get_text()
+
             name = widgets[COLUMN_NAME].get_text()
 
             if not name:
@@ -162,46 +388,73 @@
                 error_box(dialog, msg[0])
                 continue
 
-            ip_port = widgets[COLUMN_IP_PORT].get_text()
-            break
+            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()
 
-        command = ('o2cb_ctl', '-C', '-n', name, '-t', 'node',
-                   '-a', 'cluster=ocfs2',
-                   '-a', 'ip_address=%s' % ip_addr,
-                   '-a', 'ip_port=%s' % ip_port,
-                   '-i')
+        return name, ip_addr, ip_port
 
-        o2cb_ctl = Process(command, 'Cluster Control', 'Adding node...',
-                           self.toplevel)
-        success, output, k = o2cb_ctl.reap()
+    def run(self):
+        self.show_all()
 
-        if not success:
-            error_box(self.toplevel,
-                      '%s\nCould not update configuration' % output)
-            return
+        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?')
 
-        self.get_cluster_state()
-        self.tv.set_model(self.store)
+                ask = gtk.MessageDialog(parent=self,
+                                        flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+                                        type=gtk.MESSAGE_QUESTION,
+                                        buttons=gtk.BUTTONS_YES_NO,
+                                        message_format=msg)
 
-    def apply_changes(self, b):
-        pass
+                if ask.run() == gtk.RESPONSE_NO:
+                    break
+                elif self.apply_changes():
+                    break
+            else: 
+                break
 
-def cluster_configurator(parent):
-    if not os.access('/usys/cluster', os.F_OK):
-        command = ('/etc/init.d/o2cb load')
+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)
 
-        o2cb = Process(command, 'Cluster Stack', 'Starting cluster stack...',
-                       parent)
-        success, output, k = o2cb.reap()
+        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 "/etc/init.d/o2cb enable" to have it started '
-                   'upon bootup.')
+                   '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 '
@@ -219,43 +472,35 @@
         if not success:
             return
 
-    command = 'o2cb_ctl -I -t cluster -n ocfs2 -o'
-    o2cb_ctl = Process(command, 'Cluster Control', 'Querying cluster...',
-                       parent)
+    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:
-        command = 'o2cb_ctl -C -n ocfs2 -t cluster -i'
-        o2cb_ctl = Process(command, 'Cluster Control', 'Creating cluster...',
-                           parent)
+        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 = ClusterConf(parent)
-    except ConfError, e:
+        conf.load_cluster_state()
+    except ConfigError, e:
         error_box(parent, '%s: Could not query cluster configuration' % str(e))
         return
 
-    dialog = gtk.Dialog(parent=parent, title='Cluster Configurator',
-                        buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+    conf.run()
+    conf.destroy()
 
-    dialog.vbox.add(conf)
-    dialog.show_all()
+    if not os.access(os.path.join(CONFIG_FS_PATH, CLUSTER_NAME), os.F_OK):
+        online_args = ('online', CLUSTER_NAME),
+        o2cb_init = Process(online_args, 'Starting OCFS2 cluster...', parent)
+        success, output, k = o2cb_init.reap()
 
-    dialog.run()
-    dialog.destroy()
-
-    if not os.access('/usys/cluster/ocfs2', os.F_OK):
-        command = ('/etc/init.d/o2cb online ocfs2')
-
-        o2cb = Process(command, 'Cluster Stack', 'Starting OCFS2 cluster...',
-                       parent)
-        success, output, k = o2cb.reap()
-
         if not success:
             msg = ('Could not bring OCFS2 cluster online. This must be '
                    'resolved before any OCFS2 filesystem can be mounted')
@@ -270,7 +515,7 @@
             info.destroy()
 
 def main():
-    cluster_configurator(None)
+    cluster_configurator()
 
 if __name__ == '__main__':
     main()



More information about the Ocfs2-tools-commits mailing list