# HG changeset patch # User cmlenz # Date 1215530236 0 # Node ID 9c9bf7a69bf0eb616d278e323626cb0d0a07cc96 # Parent f10da09852270ebca4e87d3e2fa776ff6efb63ee Apply patch by Emmanuel Blot to allow a single slave to connect to multiple masters. Closes #271. diff --git a/bitten/master.py b/bitten/master.py --- a/bitten/master.py +++ b/bitten/master.py @@ -198,8 +198,10 @@ stepname = elem.attr['step'] # make sure it's the right slave. - if build.status != Build.IN_PROGRESS or build.slave_info.get(Build.IP_ADDRESS) != req.remote_addr: - raise HTTPForbidden('Build %s has been invalidated for host %s.' % (build.id, req.remote_addr)) + if build.status != Build.IN_PROGRESS or \ + build.slave_info.get(Build.IP_ADDRESS) != req.remote_addr: + raise HTTPForbidden('Build %s has been invalidated for host %s.' + % (build.id, req.remote_addr)) step = BuildStep.fetch(self.env, build=build.id, name=stepname) if step: @@ -216,8 +218,9 @@ raise HTTPForbidden('No such build step') last_step = index == num - self.log.debug('Slave %s (build %d) completed step %d (%s) with status %s', - build.slave, build.id, index, stepname, elem.attr['status']) + self.log.debug('Slave %s (build %d) completed step %d (%s) with ' + 'status %s', build.slave, build.id, index, stepname, + elem.attr['status']) db = self.env.get_db_cnx() diff --git a/bitten/slave.py b/bitten/slave.py --- a/bitten/slave.py +++ b/bitten/slave.py @@ -68,15 +68,16 @@ class BuildSlave(object): """BEEP initiator implementation for the build slave.""" - def __init__(self, url, name=None, config=None, dry_run=False, + def __init__(self, urls, name=None, config=None, dry_run=False, work_dir=None, build_dir="build_${build}", keep_files=False, single_build=False, poll_interval=300, username=None, password=None, dump_reports=False): """Create the build slave instance. - :param url: the URL of the build master, or the path to a build recipe - file + :param urls: a list of URLs of the build masters to connect to, or a + single-element list containing the path to a build recipe + file :param name: the name with which this slave should identify itself :param config: the path to the slave configuration file :param dry_run: wether the build outcome should not be reported back @@ -97,8 +98,9 @@ standard output, in addition to being transmitted to the build master """ - self.url = url - self.local = not url.startswith('http') and not url.startswith('https') + self.urls = urls + self.local = len(urls) == 1 and not urls[0].startswith('http://') \ + and not urls[0].startswith('https://') if name is None: name = platform.node().split('.', 1)[0].lower() self.name = name @@ -120,7 +122,7 @@ password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() if username and password: log.debug('Enabling authentication with username %r', username) - password_mgr.add_password(None, url, username, password) + password_mgr.add_password(None, urls, username, password) self.opener.add_handler(urllib2.HTTPBasicAuthHandler(password_mgr)) self.opener.add_handler(urllib2.HTTPDigestAuthHandler(password_mgr)) @@ -137,17 +139,21 @@ def run(self): if self.local: - fileobj = open(self.url) + fileobj = open(self.urls[0]) try: self._execute_build(None, fileobj) finally: fileobj.close() return os.EX_OK + urls = [] while True: + if not urls: + urls[:] = self.urls + url = urls.pop(0) try: try: - job_done = self._create_build() + job_done = self._create_build(url) if job_done: continue except urllib2.HTTPError, e: @@ -171,7 +177,7 @@ log.info('Shutting down') raise ExitSlave(os.EX_OK) - def _create_build(self): + def _create_build(self, url): xml = xmlio.Element('slave', name=self.name)[ xmlio.Element('platform', processor=self.config['processor'])[ self.config['machine'] @@ -188,7 +194,7 @@ body = str(xml) log.debug('Sending slave configuration: %s', body) - resp = self.request('POST', self.url, body, { + resp = self.request('POST', url, body, { 'Content-Length': len(body), 'Content-Type': 'application/x-bitten+xml' }) @@ -313,7 +319,7 @@ from bitten import __version__ as VERSION from optparse import OptionParser - parser = OptionParser(usage='usage: %prog [options] url', + parser = OptionParser(usage='usage: %prog [options] url1 [url2] ...', version='%%prog %s' % VERSION) parser.add_option('--name', action='store', dest='name', help='name of this slave (defaults to host name)') @@ -358,7 +364,7 @@ if len(args) < 1: parser.error('incorrect number of arguments') - url = args[0] + urls = args logger = logging.getLogger('bitten') logger.setLevel(options.loglevel) @@ -375,7 +381,7 @@ handler.setFormatter(formatter) logger.addHandler(handler) - slave = BuildSlave(url, name=options.name, config=options.config, + slave = BuildSlave(urls, name=options.name, config=options.config, dry_run=options.dry_run, work_dir=options.work_dir, build_dir=options.build_dir, keep_files=options.keep_files,