class ResourceAgent(object): def __init__(self, node): self.node = node self.id = node.id self.trace = BaseTracer() # sellers are trading on multiple jobs self.accept_processes = {} self.confirm_processes = {} # use ring buffers for space saving since # resource agents are long lived self.nrejected = 0 self.cancelled = RingBuffer(400) self.timedout = RingBuffer(400) @property def resource(self): return self.node.resource # utility functions def confirm_and_start_job(self, job, other, value): # sending confirmation message confirm = Confirm(self, other, value) confirm.send_msg(self.node, other.node) # start procsses to listen for cancellations confirm_process = ConfirmProcess(self, value) activate(confirm_process, confirm_process.confirm()) self.confirm_processes[job.id] = confirm_process # start the job self.resource.start(job, confirm_process) def send_reject(self, other, value): reject = Reject(self, other, value) reject.send_msg(self.node, other.node) # public AcceptProcess message interface def confirm(self, confirm): if confirm.id in self.accept_processes: process = self.accept_processes[confirm.id] process.signal("confirm", confirm) else: self.trace("WARNING: no accept process for confirm %s" % reject.str(self)) def reject(self, reject): if reject.id in self.accept_processes: process = self.accept_processes[reject.id] process.signal("reject", reject) else: self.trace("WARNING: no accept process for reject %s" % reject.str(self)) # public ConfirmProcess message interface def cancel(self, cancel): trace = self.trace.add("j%-5s" % cancel.id) self.cancelled.add(cancel.id) if cancel.id in self.confirm_processes: process = self.confirm_processes[cancel.id] process.signal("cancel", cancel) else: trace and trace("no confirm process for cancel, " "probably out of sync") def complete(self, complete): trace = self.trace.add("j%-5s" % cancel.id) if complete.id in self.confirm_processes: process = self.confirm_processes[complete.id] process.signal("complete", complete) else: trace("WARNING: no confirm process for complete") #internal ConfirmProcess interface # this should be the same for all resource agents def cancel_received(self, cancel): trace = self.trace.add('j%-5s' % cancel.job) if cancel.job in self.resource.jobs: trace and trace("got cancel, cancelling job %s" % cancel.job.id) self.resource.cancel(cancel.job); del self.confirm_processes[cancel.job.id] else: trace("WARNING: got cancel for job not running (%s)" % cancel.job.id) def complete_received(self, complete): trace = self.trace.add('j%-5s' % complete.job.id) trace and trace("job %s completed, cleaning up" % complete.job.id) del self.confirm_processes[complete.job.id]
class SBSeller(Seller): def __init__(self, node, rationale, **kw): super(SBSeller, self).__init__(node, rationale, **kw) self.offers = RingBuffer(500) # internal ListenProcess interface def quote_received(self, quote): trace = self.trace.add('j%-5s' % quote.job.id) trace and trace("advert from %s at %s" % (quote.buyer, quote.buyer.node)) if quote not in self.offers: if self.resource.can_allocate(quote.job): # generate a quote and send it self.price = self.rationale.quote() quote = Ask(quote.buyer, self, quote.job, self.price) private = PrivateQuote(quote) private.send_msg(self.node, quote.buyer.node) self.set_quote_timeout(quote, trace) self.offers.add(quote) trace and trace("sending offer %s" % quote) else: # TODO: add this record to the job object record.record_failure_reason(quote.job.id, "Too Busy") trace and trace("resource has no room for job") self.nrejected += 1 else: trace("WARNING: received advert for job already trading for") def accept_received(self, quote): trace = self.trace.add('j%-5s' % quote.job.id) if quote in self.offers: # we know this one trace and trace("got accept from %s" % quote.buyer) self.cancel_quote_timeout(quote.id, trace) self.rationale.observe(quote, True) # can we still do it? if self.resource.can_allocate(quote.job): self.confirm_and_start_job( quote.job, quote.buyer, quote) else: # we cannot honour our original quote trace and trace("got accept, now too busy, rejecting") record.record_failure_reason(quote.job.id, "Too Busy Later") self.send_reject(quote.buyer, quote) self.nrejected += 1 else: trace and trace("got an accept for a job we've timed out on") # diable confirm/reject as in sealedbid these are buyer only confirm = Seller.disable("confirm") reject = Seller.disable("reject") def quote_timedout(self): """this is for a regular pulse timeout""" pass