changeset 63:2332aedba328

* Allow specifying a different name for a build slave (default is the host name). * Improve handling of aborted or failed builds.
author cmlenz
date Tue, 28 Jun 2005 13:53:27 +0000
parents d108f780b76c
children de1a7499f4d6
files bitten/master.py bitten/slave.py
diffstat 2 files changed, 77 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/bitten/master.py
+++ b/bitten/master.py
@@ -90,7 +90,7 @@
                                              status=Build.IN_PROGRESS)
                 if not list(active_builds):
                     slave.send_initiation(build)
-                    break
+                    return
 
     def get_snapshot(self, build, type, encoding):
         formats = {
@@ -182,7 +182,7 @@
                 type = None
             if not type:
                 xml = xmlio.Element('error', code=550)[
-                    'None of the supported archive formats accepted'
+                    'None of the accepted archive formats supported'
                 ]
                 self.channel.send_err(beep.MIMEMessage(xml))
                 return
@@ -192,16 +192,18 @@
         self.channel.send_msg(beep.MIMEMessage(xml), handle_reply=handle_reply)
 
     def send_snapshot(self, build, type, encoding):
+
         def handle_reply(cmd, msgno, ansno, msg):
             if cmd == 'ERR':
-                if msg.get_content_type() == beep.BEEP_XML:
-                    elem = xmlio.parse(msg.get_payload())
-                    if elem.name == 'error':
-                        logging.warning('Slave did not accept archive: %s (%d)',
-                                        elem.gettext(), int(elem.attr['code']))
-                return
+                assert msg.get_content_type() == beep.BEEP_XML
+                elem = xmlio.parse(msg.get_payload())
+                if elem.name == 'error':
+                    logging.warning('Slave did not accept archive: %s (%d)',
+                                    elem.gettext(), int(elem.attr['code']))
             if cmd == 'ANS':
-                if ansno == 0:
+                elem = xmlio.parse(msg.get_payload())
+                logging.debug('Received build answer <%s>' % elem.name)
+                if elem.name == 'started':
                     self.steps = []
                     build.slave = self.name
                     build.time = int(time.time())
@@ -209,21 +211,34 @@
                     build.update()
                     logging.info('Slave %s started build of "%s" as of [%s]',
                                  self.name, build.config, build.rev)
-                else:
-                    elem = xmlio.parse(msg.get_payload())
-                    assert elem.name == 'step'
-                    logging.info('Slave completed step "%s"', elem.attr['id'])
+                elif elem.name == 'step':
+                    logging.info('Slave completed step "%s"',
+                                 elem.attr['id'])
                     if elem.attr['result'] == 'failure':
                         logging.warning('Step failed: %s', elem.gettext())
-                    self.steps.append((elem.attr['id'], elem.attr['result']))
+                    self.steps.append((elem.attr['id'],
+                                       elem.attr['result']))
+                elif elem.name == 'abort':
+                    logging.info('Slave "%s" aborted build', self.name)
+                    build.slave = None
+                    build.time = 0
+                    build.status = Build.PENDING
+                elif elem.name == 'error':
+                    build.status = Build.FAILURE
             elif cmd == 'NUL':
-                logging.info('Slave %s completed build of "%s" as of [%s]',
-                             self.name, build.config, build.rev)
-                build.duration = int(time.time()) - build.time
-                if [step for step in self.steps if step[1] == 'failure']:
-                    build.status = Build.FAILURE
-                else:
-                    build.status = Build.SUCCESS
+                if build.status != Build.PENDING: # Completed
+                    logging.info('Slave %s completed build of "%s" as of [%s]',
+                                 self.name, build.config, build.rev)
+                    build.duration = int(time.time()) - build.time
+                    if build.status is Build.IN_PROGRESS:
+                        # Find out whether the build failed or succeeded
+                        if [st for st in self.steps if st[1] == 'failure']:
+                            build.status = Build.FAILURE
+                        else:
+                            build.status = Build.SUCCESS
+                else: # Aborted
+                    build.slave = None
+                    build.time = 0
                 build.update()
 
         # TODO: should not block while reading the file; rather stream it using
--- a/bitten/slave.py
+++ b/bitten/slave.py
@@ -25,13 +25,17 @@
 import time
 
 from bitten.build import BuildError
-from bitten.recipe import Recipe
+from bitten.recipe import Recipe, InvalidRecipeError
 from bitten.util import archive, beep, xmlio
 
 
 class Slave(beep.Initiator):
     """Build slave."""
 
+    def __init__(self, ip, port, name=None):
+        beep.Initiator.__init__(self, ip, port)
+        self.name = name
+
     def greeting_received(self, profiles):
         if OrchestrationProfileHandler.URI not in profiles:
             err = 'Peer does not support the Bitten orchestration profile'
@@ -61,6 +65,8 @@
             logging.info('Registration successful')
 
         sysname, nodename, release, version, machine = os.uname()
+        if self.session.name is not None:
+            nodename = self.session.name
         logging.info('Registering with build master as %s', nodename)
         xml = xmlio.Element('register', name=nodename)[
             xmlio.Element('platform')[machine],
@@ -123,26 +129,39 @@
         logging.info('Building in directory %s using recipe %s', basedir,
                      recipe_path)
 
-        recipe = Recipe(recipe_path, basedir)
-
-        xml = xmlio.Element('started')
-        self.channel.send_ans(msgno, beep.MIMEMessage(xml))
+        try:
+            recipe = Recipe(recipe_path, basedir)
 
-        for step in recipe:
-            logging.info('Executing build step "%s"', step.id)
-            try:
-                for function, args in step:
-                    logging.debug('Executing command "%s"', function)
-                    function(recipe.ctxt, **args)
-                xml = xmlio.Element('step', id=step.id, result='success',
-                                    description=step.description)
-                self.channel.send_ans(msgno, beep.MIMEMessage(xml))
-            except BuildError, e:
-                xml = xmlio.Element('step', id=step.id, result='failure',
-                                    description=step.description)[e]
-                self.channel.send_ans(msgno, beep.MIMEMessage(xml))
+            xml = xmlio.Element('started')
+            self.channel.send_ans(msgno, beep.MIMEMessage(xml))
 
-        self.channel.send_nul(msgno)
+            for step in recipe:
+                logging.info('Executing build step "%s"', step.id)
+                try:
+                    for function, args in step:
+                        logging.debug('Executing command "%s"', function)
+                        function(recipe.ctxt, **args)
+                    xml = xmlio.Element('step', id=step.id, result='success',
+                                        description=step.description)
+                    self.channel.send_ans(msgno, beep.MIMEMessage(xml))
+                except (BuildError, InvalidRecipeError), e:
+                    xml = xmlio.Element('step', id=step.id, result='failure',
+                                        description=step.description)[e]
+                    self.channel.send_ans(msgno, beep.MIMEMessage(xml))
+
+            self.channel.send_nul(msgno)
+
+        except InvalidRecipeError, e:
+            xml = xmlio.Element('error')[e]
+            self.channel.send_ans(msgno, beep.MIMEMessage(xml))
+            self.channel.send_nul(msgno)
+
+        except (KeyboardInterrupt, SystemExit), e:
+            xml = xmlio.Element('abort')['Build cancelled']
+            self.channel.send_ans(msgno, beep.MIMEMessage(xml))
+            self.channel.send_nul(msgno)
+
+            raise beep.TerminateSession, 'Cancelled'
 
 
 def main():
@@ -151,6 +170,8 @@
 
     parser = OptionParser(usage='usage: %prog [options] host [port]',
                           version='%%prog %s' % VERSION)
+    parser.add_option('-n', '--name', action='store', dest='name',
+                      help='name of this slave (defaults to host name)')
     parser.add_option('--debug', action='store_const', dest='loglevel',
                       const=logging.DEBUG, help='enable debugging output')
     parser.add_option('-v', '--verbose', action='store_const', dest='loglevel',
@@ -174,7 +195,7 @@
 
     logging.getLogger().setLevel(options.loglevel)
 
-    slave = Slave(host, port)
+    slave = Slave(host, port, options.name)
     try:
         slave.run()
     except KeyboardInterrupt:
Copyright (C) 2012-2017 Edgewall Software