class Seller(ResourceAgent, Trader): def __init__(self, node, rationale, **kw): ResourceAgent.__init__(self, node) Trader.__init__(self, rationale, **kw) self.trace = Tracer(node) self.trace = self.trace.add('%-12s' % self) self.listen_process = None self.quote_timeouts = dict() def start(self): self.listen_process = ListenProcess(self) activate(self.listen_process, self.listen_process.listen()) def set_quote_timeout(self, quote, trace): trace and trace("setting rationale timeout %s" % (id, )) p = RationaleTimeout() p.start(p.timeout(self, quote.id, quote, self.quote_timeout, trace)) self.quote_timeouts[quote.id] = p def cancel_quote_timeout(self, id, trace): if id in self.quote_timeouts: trace and trace("cancelling rationale timeout %s" % (id, )) process = self.quote_timeouts[id] cancel_process(process) del self.quote_timeouts[id] elif id in self.timedout: trace and trace("rationale timeout already fired %s" % (id, )) elif trace: trace("WARNING: unknown quote timeout cancelled %s" % (id, )) def process_quote_timeout(self, id, quote, trace): if id in self.quote_timeouts: trace and trace("observing failed quote %s" % (id, )) self.rationale.observe(quote, False) self.timedout.add(id) del self.quote_timeouts[id] elif id in self.timedout: trace("WARNING: quote timeout already timed out %s" % (id, )) else: trace("WARNING: unknown quote timeout firing %s" % (id, )) # utility function def create_quote(self): self.price = self.rationale.quote() return Ask(None, self, self.resource.free, self.price) def create_accept(self, quote): return Ask(quote.buyer, self, quote.job, quote.price) def viable_quote(self, q): return (self.active and self.bid and q.price >= self.price and q.size <= self.node.resource.free)
class MdsResourceAgent(ResourceAgent): def __init__(self, node, update_time): ResourceAgent.__init__(self, node) self.update_time = update_time self.update_process = None self.listen_process = None self.trace = Tracer(self.node).add("ragent%-6s" % node.id) def accept(self, value): if self.listen_process: self.listen_process.signal("accept", value) else: self.trace("WARNING: no listen process for accept") @property def broker(self): return self.node.broker def start(self): self.update_process = ResourceUpdateProcess(self) self.update_process.start(self.update_process.update()) self.listen_process = ResourceListenProcess(self) self.listen_process.start(self.listen_process.listen()) # accept received def accept_received(self, alloc): job = alloc.jagent.job trace = self.trace.add("j%-5s" % job.id) if self.resource.can_allocate(job): trace and trace("accept from %s, starting" % alloc.jagent) self.confirm_and_start_job(job, alloc.jagent, alloc) else: trace and trace("accept from %s, busy, rejecting" % alloc.jagent) self.send_reject(alloc.jagent, alloc) def __str__(self): return "ragent%d" % self.node.id
class Broker(object): def __init__(self, region, node, registry, sync_time, others): self.region = region self.node = node self.registry = registry self.sync_time = sync_time self.others = others self.listen_process = None self.trace = Tracer(node).add("bkr%-9s" % region) def __str__(self): return "broker %d" % self.region def start(self): self.listen_process = BrokerListenProcess(self) self.listen_process.start(self.listen_process.listen()) self.update_process = BrokerUpdateProcess(self) self.update_process.start(self.update_process.update()) def allocate(self, allocation): trace = self.trace.add("j%-5s" % allocation.jagent.job.id) trace and trace("alloc request from %s" % allocation.jagent) states = self.registry.get_resources(allocation) # return the best system wide fit states.sort(key=lambda x: x.free) if states: state = states[0] alloc = Allocation(allocation.jagent, state.agent) msg = AllocationResponse(alloc) msg.send_msg(self.node, alloc.jagent.node) trace and trace("returning alloc %s" % alloc) state.free -= allocation.job.size else: trace and trace("no valid allocations") def update(self, state): self.trace and self.trace("updating state for %s" % state.agent) self.registry.update_state(state) @property def other_brokers(self): for other in self.others.itervalues(): if other is not self: yield other def send_sync(self): states = SyncDict(self) for agent, state in self.registry.states.iteritems(): states[agent] = ResourceState(agent, state.free) for other in self.other_brokers: self.trace and self.trace("sending sync to %s" % other) msg = SyncMessage(states) msg.send_msg(self.node, other.node) def sync(self, states): self.trace and self.trace("syncing states from %s" % states.broker) #print "xxx" #print "----" #for state in self.registry.states.itervalues(): # print state.agent, state.free #print "----" for state in states.itervalues(): #print state.agent, state.free self.registry.update_state(state)
class Buyer(JobAgent, Trader): def __init__(self, job, rationale, **kw): JobAgent.__init__(self, job) Trader.__init__(self, rationale, **kw) self.migrations = 0 self.listen_process = None self.accepted = set() def start(self): self.start_time = now() self.node.job_agents.add(self) self.listen_process = ListenProcess(self) activate(self.listen_process, self.listen_process.listen()) def start_on_node(self, node): self.node = node self.regions.add(self.node.region) self.trace = Tracer(node) self.trace = self.trace.add('%-12s' % self) self.trace = self.trace.add('j%-5d' % self.job.id) self.trace and self.trace("starting on %s" % node) self.start() # buyer mobility utilities def remove_from_node(self): self.trace and self.trace("removing from %s" % self.node) self.node.job_agents.remove(self) self.node.old_job_agents.add(self) def migrate(self): self.trace and self.trace("migrating") current = self.node self.remove_from_node() r = self.node.graph.regions max = r[0] * r[1] if len(self.regions) == max: self.regions = set() others = self.node.graph.nodes() else: # choose another node in a different region others = [n for n in self.node.graph.nodes_iter() if n.region not in self.regions] other = random.choice(others) while other == current: other = random.choice(others) #cancel any events (there shouldn't be, but just in case) self.cancel_all() self.trace and self.trace("moving to %s" % other) self.start_on_node(other) def finish_trading(self): self.remove_from_node() self.active = False # utility functions def create_quote(self): self.price = self.rationale.quote() return Bid(self, None, self.job, self.price) def create_accept(self, quote): return Bid(self, quote.seller, self.job, quote.price) def viable_quote(self, q): return (self.active and q.ask and q.price <= self.price and q.size >= self.job.size)
class MdsJobAgent(JobAgent): def __init__(self, job, allocate_timeout, accept_timeout, max_attempts): JobAgent.__init__(self, job) self.allocate_timeout = allocate_timeout self.accept_timeout = accept_timeout self.allocation = Allocation(self, None) self.attempts = 0 self.max_attempts = max_attempts @property def broker(self): return self.node.broker def send_allocation_request(self): if self.attempts < self.max_attempts: self.allocation = Allocation(self, None) self.attempts += 1 self.trace and self.trace("sending allocation (%s)", self.attempt) self.allocate_process = AllocateProcess(self) self.allocate_process.start(self.allocate_process.allocate()) msg = AllocationRequest(self.allocation) msg.send_msg(self.node, self.broker.node) else: self.trace and self.trace("allocation attemps execeeded") self.record_failure(self.allocation) self.cancel_all() def start(self): self.send_allocation_request() def start_on(self, node): self.node = node self.node.job_agents.add(self) self.trace = Tracer(self.node).add("jagent%-6s" % self.job.id) self.trace = self.trace.add("j%-5s" % self.job.id) self.start() def response(self, alloc): self.trace and self.trace("got allocation: %s" % alloc) if (alloc.ragent): self.allocation = alloc self.trace and self.trace("sending accept to %s" % alloc.ragent) self.start_accept_process(alloc.ragent, alloc, self.accept_timeout) else: self.trace and self.trace("broker gave null alloc") self.send_allocation_request() def allocate_timedout(self): self.trace and self.trace("allocation request timedout") self.send_allocation_request() #internal AcceptProcess interface def confirm_received(self, confirm): if confirm == self.allocation: self.trace and self.trace("accept confirmed") self.record_success(confirm) self.cancel_all() self.accept_process = None elif confirm in self.timedout: # old confirms self.trace and self.trace("got confirm from timed out quote") else: # unknown confirm self.trace("WARNING: got random confirm: %s" % confirm) def reject_received(self, reject): if reject == self.allocation: # allocation rejected self.trace and self.trace("accept rejected") self.send_allocation_request() elif reject in self.timedout: self.trace and self.trace("accept rejected, but had already timed out") else: self.trace("WARNING: got reject for unknown quote: %s" % reject) def accept_timedout(self, accept): self.trace and self.trace(" accept timed out, sending cancel") cancel = Cancel(self, accept.ragent, accept) cancel.send_msg(self.node, accept.ragent.node) self.send_allocation_request() def record_failure(self, alloc): record.record_failure(self, alloc) def record_success(self, alloc): record.record_success(self, alloc) def __str__(self): return "jagent%d" % self.job.id