def __init__(self, arch, distribution, pkg_format, suite, conn_info, settings) : self.state_table = {} self.state_table["UNKNOWN"] = self.fatal_error_handler self.state_table["IDLE"] = self.idle_handler self.state_table["FATAL_ERROR"] = self.fatal_error_handler self.state_table["CHECKOUT"] = self.checkout_handler self.state_table["BUILD"] = self.build_handler self.state_table["UPLOAD"] = self.upload_handler self.state_table["CLEAN"] = self.clean_handler self.overall_success = None self.state = "UNKNOWN" self.arch = arch self.distribution = distribution self.pkg_format = pkg_format self.suite = suite self.conn = None self.command_chan = None self.message_chan = None self.settings = settings self.routing_key = pybit.get_build_route_name(self.distribution, self.arch, self.suite, self.pkg_format) self.queue_name = pybit.get_build_queue_name(self.distribution, self.arch, self.suite, self.pkg_format) self.conn_info = conn_info #self.message_chan.basic_consume(queue=self.queue_name, no_ack=False, callback=self.message_handler, consumer_tag="build_callback") #self.command_chan.basic_consume(queue=self.client_queue_name, no_ack=True, callback=self.command_handler, consumer_tag="cmd_callback") if (pkg_format == "deb") : self.format_handler = DebianBuildClient(self.settings) else: print "Empty build client" self.format_handler = None self._clean_current() self.move_state("IDLE")
class PyBITClient(object): def _clean_current(self): self.vcs_handler = None self.process = None self.current_msg = None self.current_request = None self.overall_success = None def set_status(self, status, request=None, client = None): if request is None: request = self.current_request if status is not None and request is not None: print "Marking JOB id: %s as: %s" % (request.get_job_id(), status) #FIXME: this clears/resets 'cancelled' state payload = {'status' : status } if client is not None: payload['client'] = client job_status_url = "http://%s/job/%s" % (request.web_host, request.get_job_id()) requests.put(job_status_url, payload) else: print "Couldn't find status or current_request" def get_status(self, request = None): if request is None: request = self.current_request if request is not None: job_status_get_url = "http://%s/job/%s/status" % (request.web_host, request.get_job_id()) r = requests.get(job_status_get_url) if r.status_code == 200 and r.headers['content-type'] == 'application/json': status_list = jsonpickle.decode(r.content) if (len(status_list) > 0): return status_list[-1].status def wait(self): time.sleep(5) if self.state == "IDLE" : msg = self.message_chan.basic_get() if msg is not None : self.message_handler(msg) cmd = self.command_chan.basic_get() if cmd is not None: self.command_handler(cmd) def move_state(self, new_state): if (new_state in self.state_table): #FIXME: we can stack state handling in here. self.old_state = self.state self.state = new_state if self.state == "CHECKOUT": args = (self.current_request,self.conn_info) self.process = multiprocessing.Process(target=self.vcs_handler.fetch_source,args=args) self.process.start() self.set_status(ClientMessage.building,None,self.conn_info.client_name) elif self.state == "BUILD": args = (self.current_request,self.conn_info) if self.current_request.job.packageinstance.master == True: self.process = multiprocessing.Process(target=self.format_handler.build_master, args=args) else: self.process = multiprocessing.Process(target=self.format_handler.build_slave, args=args) self.process.start() elif self.state == "CLEAN": args = (self.current_request,self.conn_info) self.process = multiprocessing.Process(target=self.vcs_handler.clean_source, args=args) self.process.start() elif self.state == "UPLOAD": args = (self.current_request,self.conn_info) self.process = multiprocessing.Process(target=self.format_handler.upload, args=args) self.process.start() elif self.state == "IDLE": overall_success = self.overall_success current_msg = self.current_msg current_req = self.current_request self._clean_current() if current_msg is not None : self.message_chan.basic_ack(current_msg.delivery_tag) if overall_success == True: self.set_status(ClientMessage.done, current_req) elif overall_success == False: self.set_status(ClientMessage.failed, current_req) print "Moved from %s to %s" % (self.old_state, self.state) else: print "Unhandled state: %s" % (new_state) def idle_handler(self, msg, decoded): if isinstance(decoded, BuildRequest): self.current_msg = msg self.current_request = decoded if (self.current_request.transport.method == "svn" or self.current_request.transport.method == "svn+ssh"): if self.get_status() == ClientMessage.waiting: self.vcs_handler = SubversionClient(self.settings) self.move_state("CHECKOUT") elif self.get_status() == ClientMessage.cancelled: print "jobid: %s has been cancelled. Acking." % (self.current_request.get_job_id()) self.move_state("IDLE") else: self.overall_success = False self.move_state("IDLE") def fatal_error_handler(self, msg, decoded): print "Fatal Error handler" def checkout_handler(self, msg, decoded): if isinstance(decoded, TaskComplete) : self.process.join() if decoded.success == True: self.move_state("BUILD") else: self.move_state("CLEAN") def build_handler(self, msg, decoded): if isinstance(decoded, TaskComplete) : self.process.join() if decoded.success == True: self.move_state("UPLOAD") else: self.move_state("CLEAN") def upload_handler(self, msg, decoded): if isinstance(decoded, TaskComplete) : self.overall_success = decoded.success self.process.join() self.move_state("CLEAN") def clean_handler(self, msg, decoded): if isinstance(decoded, TaskComplete) : self.process.join() if decoded.success == True: self.move_state("IDLE") else: self.move_state("FATAL_ERROR") def __init__(self, arch, distribution, pkg_format, suite, conn_info, settings) : self.state_table = {} self.state_table["UNKNOWN"] = self.fatal_error_handler self.state_table["IDLE"] = self.idle_handler self.state_table["FATAL_ERROR"] = self.fatal_error_handler self.state_table["CHECKOUT"] = self.checkout_handler self.state_table["BUILD"] = self.build_handler self.state_table["UPLOAD"] = self.upload_handler self.state_table["CLEAN"] = self.clean_handler self.overall_success = None self.state = "UNKNOWN" self.arch = arch self.distribution = distribution self.pkg_format = pkg_format self.suite = suite self.conn = None self.command_chan = None self.message_chan = None self.settings = settings self.routing_key = pybit.get_build_route_name(self.distribution, self.arch, self.suite, self.pkg_format) self.queue_name = pybit.get_build_queue_name(self.distribution, self.arch, self.suite, self.pkg_format) self.conn_info = conn_info #self.message_chan.basic_consume(queue=self.queue_name, no_ack=False, callback=self.message_handler, consumer_tag="build_callback") #self.command_chan.basic_consume(queue=self.client_queue_name, no_ack=True, callback=self.command_handler, consumer_tag="cmd_callback") if (pkg_format == "deb") : self.format_handler = DebianBuildClient(self.settings) else: print "Empty build client" self.format_handler = None self._clean_current() self.move_state("IDLE") def message_handler(self, msg): build_req = jsonpickle.decode(msg.body) if not isinstance(build_req, BuildRequest) : self.message_chan.basic_ack(msg.delivery_tag) return if self.process: print "Detected a running process" self.state_table[self.state](msg, build_req) def command_handler(self, msg): cmd_req = jsonpickle.decode(msg.body) if (not isinstance(cmd_req, TaskComplete) and not isinstance(cmd_req, CommandRequest)): print "Can't handle message type." self.command_chan.basic_ack(msg.delivery_tag) elif isinstance(cmd_req, CommandRequest) : if isinstance(cmd_req, CancelRequest) : print "Received CANCEL request for jobid:", cmd_req.get_job_id() self.set_status(ClientMessage.cancelled, cmd_req) if (self.current_request.get_job_id() == cmd_req.get_job_id()) and (self.process is not None) : self.process.terminate() self.process.join() self.process = None self.move_state("IDLE") else : print "Received COMMAND request for jobid:", cmd_req.get_job_id() else: self.state_table[self.state](msg, cmd_req) def is_building(self): if self.format_handler.is_building() : # FIXME return True return False def connect(self): try: self.conn = amqp.Connection(host=self.conn_info.host, userid=self.conn_info.userid, password=self.conn_info.password, virtual_host=self.conn_info.vhost, insist=False) self.command_chan = self.conn.channel() self.message_chan = self.conn.channel() self.message_chan.basic_qos(0,1,False) except socket.error as e: print "Couldn't connect rabbitmq server with: %s" % repr(self.conn_info) return print "Creating queue with name:" + self.queue_name self.message_chan.queue_declare(queue=self.queue_name, durable=True, exclusive=False, auto_delete=False) self.message_chan.queue_bind(queue=self.queue_name, exchange=pybit.exchange_name, routing_key=self.routing_key) print "Creating private command queue with name:" + self.conn_info.client_name self.command_chan.queue_declare(queue=self.conn_info.client_name, durable=False, exclusive=True, auto_delete=False) self.command_chan.queue_bind(queue=self.conn_info.client_name, exchange=pybit.exchange_name, routing_key=self.conn_info.client_name) def disconnect(self): if self.conn: if self.command_chan: #self.command_chan.basic_cancel("build_callback") self.command_chan.close() if self.message_chan: #self.message_chan.basic_cancel("build_callback") self.message_chan.close() self.conn.close() def __enter__(self): self.connect() return self def __exit__(self, type, value, traceback): self.disconnect()