Modify application to be totally driven by subscribe events.
authorTim Potter <tpot@hp.com>
Tue, 8 Apr 2008 03:47:31 +0000 (13:47 +1000)
committerTim Potter <tpot@hp.com>
Tue, 8 Apr 2008 03:47:31 +0000 (13:47 +1000)
gbuildbotclient

index 7c786f8278b6f55a92290e47ee1c155a963a3d7c..70611646696a5d77e07de61ffe0eb3ae78f3fa3d 100755 (executable)
@@ -22,16 +22,13 @@ from twisted.python import log
 
 # A class to manage the remote connection to the buildbot server
 
-class BuildbotClient(pb.Referenceable):
+class BuildbotClient:
 
     def __init__(self):
 
         self.builders = {}
         self.remote = None
         
-    def callRemote(self, *args, **kwargs):
-        return self.remote.callRemote(*args, **kwargs)
-
     def connect(self, host, port, username, password):
 
         cf = pb.PBClientFactory()
@@ -40,7 +37,7 @@ class BuildbotClient(pb.Referenceable):
         d = cf.login(creds)
         reactor.connectTCP(host, port, cf)
 
-        d.addCallback(self.connected)
+        d.addCallbacks(self.connected, lambda arg: log.err(arg))
         
         log.msg('connecting to %s:%d as %s' % (host, port, username))
 
@@ -61,21 +58,32 @@ class BuildbotClient(pb.Referenceable):
 
         ref = None        
 
+    def subscribe(self, mode, interval, target):
+        return self.remote.callRemote('subscribe', mode, interval, target)
+
 # Application class
 
-class App:
+class App(pb.Referenceable):
+
+    # Model/View column identities
 
-    COL_BUILDER = 0             # PYOBJECT: RemoteBuilder objecr
-    COL_BUILDER_STATE = 1       # PYOBJECT: result of builder.getState() 
-    COL_BUILDER_NAME = 2        # str: Name of builder
-    COL_BUILDER_LAST_STATUS = 3 # str: Status of last build
-    COL_BUILDER_STATUS = 4      # str: Current status
+    COL_BUILDER = 0               # PYOBJECT: RemoteBuilder objecr
+    COL_BUILDER_NAME = 1          # str: Name of builder
+    COL_BUILDER_STATE = 2         # str: State of builder
+
+    # Subscription modes.  Each mode includes events of previous mode.
+
+    MODE_BUILDERS = 'builders' # builderAdded, builderRemoved
+    MODE_BUILDS = 'builds'  # builderChangedState, buildStarted, buildFinished
+    MODE_STEPS = 'steps'    # buildETAUpdate, stepStarted, stepFinished
+    MODE_LOGS = 'logs'      # stepETAUpdate, logStarted, logFinished
+    MODE_FULL = 'full'      # logChunk
 
     # Results constants
 
     Results = ["success", "warnings", "failure", "skipped", "exception"]
 
-    def __init__(self, host, port, username, password):
+    def __init__(self, host, port, username, password, updateInterval = 5):
 
         # Initialise buildbot client
 
@@ -88,8 +96,7 @@ class App:
         self.win = self.xml.get_widget('toplevel')
         self.win.connect('destroy', gtk.main_quit)
 
-        self.model = gtk.ListStore(
-            gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, str, str, str)
+        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT, str, str)
 
         self.model.set_sort_column_id(self.COL_BUILDER_NAME, gtk.SORT_ASCENDING)
 
@@ -106,10 +113,7 @@ class App:
             SortableTreeViewColumn('Name', self.COL_BUILDER_NAME))
 
         view.append_column(
-            SortableTreeViewColumn('Last Build', self.COL_BUILDER_LAST_STATUS))
-
-        view.append_column(
-            SortableTreeViewColumn('Current Status', self.COL_BUILDER_STATUS))
+            SortableTreeViewColumn('State', self.COL_BUILDER_STATE))
 
         view.show()
 
@@ -119,49 +123,80 @@ class App:
 
         self.win.show()
 
-        # Initialise model with list of builders
+        # Subscribe to build events to update main window
 
         d = self.client.connect(host, port, username, password)
-        d.addCallback(self._initModel)
 
-    def _initModel(self, *args):
-        
-        # Call getBuilder() on each item returned by getBuilderNames()
-        # and add (builder, name) tuple to the model.
+        def subscribe(arg):
+
+            d = self.client.subscribe(self.MODE_BUILDS, 5, self)
+
+            d.addCallbacks(lambda arg: sys.stdout.write("ok\n"),
+                           lambda *args: sys.stdout.write("error: %s\n" % args))
+
+        d.addCallback(subscribe)
+
+    # Callbacks for subscription mode >= MODE_BUILDERS
+
+    def remote_builderAdded(self, buildername, builder):
+        """Called by the PB server when a builder has been added to the
+        buildbot.  The buildername parameter is the name of the build
+        as a string, and builder is a RemoteBuilder object."""
+
+        log.msg('added builder "%s"' % buildername)
+
+        # Add builder to model
+
+        iter = self.model.append()
+
+        self.model.set_value(iter, self.COL_BUILDER, builder)
+        self.model.set_value(iter, self.COL_BUILDER_NAME, buildername)
+
+    def remote_builderRemoved(self, buildername):
+        """Called by the PB server when a builder has been removed from the
+        buildbot."""
+
+        log.msg('removed builder "%s"' % buildername)
+
+        # Remove builder from model
+
+        def delBuilder(model, path, iter, user_data):
+            if model.get_value(iter, self.COL_BUILDER_NAME) == buildername:
+                self.model.remove(iter)
 
-        def addBuilderAndName(builder, name):
+        self.model.foreach(delBuilder, None)
 
-            # Add builder to model
+    # Subscription mode >= builds
 
-            iter = self.model.append()
+    def remote_buildStarted(self, buildername, build):
+        """Called by the PB server when a builder has started a build. The
+        buildername parameter is the name of the build as a string,
+        and build is a RemoteBuild object."""
 
-            self.model.set_value(iter, self.COL_BUILDER, builder)
-            self.model.set_value(iter, self.COL_BUILDER_NAME, name)
-            
-            # Fetch current state of builder
+        log.msg('remote_buildStarted')
 
-            def setStateAndStatus(state):
-                self.model.set_value(iter, self.COL_BUILDER_STATE, state)
-                self.model.set_value(iter, self.COL_BUILDER_STATUS, state[0])
+    def remote_builderChangedState(self, buildername, statename, eta):
+        """Called by the PB server when a builder has changed state.  The
+        buildername parameter is the name of the build, state is a
+        description of the state of the build, and eta is the
+        estimated time in seconds until the completion of the build."""
 
-            builder.callRemote('getState').addCallback(setStateAndStatus)
+        log.msg('builder "%s" changed state to "%s"' % (buildername, statename))
 
-            # Fetch last build status
+        # Reflect state change in model
 
-            def setLastBuildStatus(result):
+        def updateState(model, path, iter, user_data):
+            if model.get_value(iter, self.COL_BUILDER_NAME) == buildername:
+                model.set_value(iter, self.COL_BUILDER_STATE, statename)
 
-                self.model.set_value(iter, self.COL_BUILDER_LAST_STATUS,
-                                     self.Results[result])
+        self.model.foreach(updateState, None)
 
-            builder.callRemote('getLastFinishedBuild').addCallback(
-                lambda build: 
-                    build and build.callRemote('getResults')
-                        .addCallback(setLastBuildStatus))
+    def remote_buildFinished(self, buildername, build, result):
+        """Called by the PB server when a build has finished.  Buildername is
+        the name of the build, build a RemoteBuild object, and result an
+        integer exit code being the result of the build."""
 
-        self.client.callRemote('getBuilderNames').addCallback(
-            lambda names: 
-                [self.client.callRemote('getBuilder', name).addCallback
-                 (addBuilderAndName, name) for name in names])
+        log.msg('remote_buildFinished')
 
 # Main function