def at_lease_done(self, lease): """See AccountingProbe.at_lease_done""" if lease.get_state() == Lease.STATE_DONE: self.accounting.incr_counter(PriceProbe.COUNTER_REVENUE, lease.id, lease.price) self.accounting.set_lease_stat(PriceProbe.LEASE_STAT_PRICE, lease.id, lease.price) if lease.extras.has_key("simul_userrate"): user_rate = float(lease.extras["simul_userrate"]) user_price = get_policy().pricing.get_base_price(lease, user_rate) if lease.get_state() == Lease.STATE_DONE and lease.extras.has_key("rate"): surcharge = lease.price - get_policy().pricing.get_base_price(lease, lease.extras["rate"]) self.accounting.incr_counter(PriceProbe.COUNTER_MISSED_REVENUE_UNDERCHARGE, lease.id, user_price - lease.price) self.accounting.incr_counter(PriceProbe.COUNTER_SURCHARGE, lease.id, surcharge) elif lease.get_state() == Lease.STATE_REJECTED: self.accounting.incr_counter(PriceProbe.COUNTER_MISSED_REVENUE_REJECT, lease.id, user_price) elif lease.get_state() == Lease.STATE_REJECTED_BY_USER: self.accounting.incr_counter(PriceProbe.COUNTER_MISSED_REVENUE_REJECT_BY_USER, lease.id, user_price)
def request_lease(self, lease): """Requests a leases. This is the entry point of leases into the scheduler. Request a lease. The decision on whether to accept or reject a lease is deferred to the policy manager (through its admission control policy). If the policy determines the lease can be accepted, it is marked as "Pending". This still doesn't guarantee that the lease will be scheduled (e.g., an AR lease could still be rejected if the scheduler determines there are no resources for it; but that is a *scheduling* decision, not a admission control policy decision). The ultimate fate of the lease is determined the next time the scheduling function is called. If the policy determines the lease cannot be accepted, it is marked as rejected. Arguments: lease -- Lease object. Its state must be STATE_NEW. """ self.logger.info("Lease #%i has been requested." % lease.id) if lease.submit_time == None: lease.submit_time = round_datetime(get_clock().get_time()) lease.print_contents() lease.set_state(Lease.STATE_PENDING) if get_policy().accept_lease(lease): self.logger.info("Lease #%i has been marked as pending." % lease.id) self.leases.add(lease) else: self.logger.info("Lease #%i has not been accepted" % lease.id) lease.set_state(Lease.STATE_REJECTED) self.completed_leases.add(lease) self.accounting.at_lease_request(lease) get_persistence().persist_lease(lease)
def __schedule_lease(self, lease, nexttime): """ Schedules a lease. This method orchestrates the preparation and VM scheduler to schedule a lease. Arguments: lease -- Lease to schedule. nexttime -- The next time at which the scheduler can allocate resources. """ lease_state = lease.get_state() migration = get_config().get("migration") # Determine earliest start time in each node if lease_state == Lease.STATE_PENDING or lease_state == Lease.STATE_QUEUED: # This lease might require preparation. Ask the preparation # scheduler for the earliest starting time. earliest = self.preparation_scheduler.find_earliest_starting_times(lease, nexttime) elif lease_state == Lease.STATE_SUSPENDED_PENDING or lease_state == Lease.STATE_SUSPENDED_QUEUED: # This lease may have to be migrated. # We have to ask both the preparation scheduler and the VM # scheduler what would be the earliest possible starting time # on each node, assuming we have to transfer files between # nodes. node_ids = self.slottable.nodes.keys() earliest = {} if migration == constants.MIGRATE_NO: # If migration is disabled, the earliest starting time # is simply nexttime. for node in node_ids: earliest[node] = EarliestStartingTime(nexttime, EarliestStartingTime.EARLIEST_NOPREPARATION) else: # Otherwise, we ask the preparation scheduler and the VM # scheduler how long it would take them to migrate the # lease state. prep_migr_time = self.preparation_scheduler.estimate_migration_time(lease) vm_migr_time = self.vm_scheduler.estimate_migration_time(lease) for node in node_ids: earliest[node] = EarliestStartingTime(nexttime + prep_migr_time + vm_migr_time, EarliestStartingTime.EARLIEST_MIGRATION) else: raise InconsistentLeaseStateError(lease, doing = "scheduling a best-effort lease") # Now, we give the lease to the VM scheduler, along with the # earliest possible starting times. If the VM scheduler can # schedule VMs for this lease, it will return a resource reservation # that we can add to the slot table, along with a list of # leases that have to be preempted. # If the VM scheduler can't schedule the VMs, it will throw an # exception (we don't catch it here, and it is just thrown up # to the calling method. (vmrr, preemptions) = self.vm_scheduler.schedule(lease, lease.duration.get_remaining_duration(), nexttime, earliest) ## BEGIN NOT-FIT-FOR-PRODUCTION CODE ## Pricing shouldn't live here. Instead, it should happen before a lease is accepted ## It is being done here in the interest of developing a first prototype ## that incorporates pricing in simulations (but not interactively yet) # Call pricing policy lease_price = get_policy().price_lease(lease, preemptions) # Determine whether to accept price or not (this in particular # should happen in the lease admission step) if lease.extras.has_key("simul_userrate"): user_rate = float(lease.extras["simul_userrate"]) if get_config().get("policy.pricing") != "free": user_price = get_policy().pricing.get_base_price(lease, user_rate) # We want to record the rate at which the lease was priced lease.extras["rate"] = get_policy().pricing.rate if lease_price > user_price: lease.price = -1 lease.extras["rejected_price"] = lease_price raise NotSchedulableException, "Lease priced at %.2f. User is only willing to pay %.2f" % (lease_price, user_price) lease.price = lease_price ## END NOT-FIT-FOR-PRODUCTION CODE # Schedule lease preparation is_ready = False preparation_rrs = [] if lease_state in (Lease.STATE_SUSPENDED_PENDING, Lease.STATE_SUSPENDED_QUEUED) and migration != constants.MIGRATE_NO: # The lease might require migration migr_rrs = self.preparation_scheduler.schedule_migration(lease, vmrr, nexttime) if len(migr_rrs) > 0: end_migr = migr_rrs[-1].end else: end_migr = nexttime migr_rrs += self.vm_scheduler.schedule_migration(lease, vmrr, end_migr) migr_rrs.reverse() for migr_rr in migr_rrs: vmrr.pre_rrs.insert(0, migr_rr) if len(migr_rrs) == 0: is_ready = True elif lease_state in (Lease.STATE_SUSPENDED_PENDING, Lease.STATE_SUSPENDED_QUEUED) and migration == constants.MIGRATE_NO: # No migration means the lease is ready is_ready = True elif lease_state in (Lease.STATE_PENDING, Lease.STATE_QUEUED): # The lease might require initial preparation preparation_rrs, is_ready = self.preparation_scheduler.schedule(lease, vmrr, earliest, nexttime) # If scheduling the lease involves preempting other leases, # go ahead and preempt them. if len(preemptions) > 0: self.logger.info("Must preempt leases %s to make room for lease #%i" % ([l.id for l in preemptions], lease.id)) for l in preemptions: self.__preempt_lease(l, preemption_time=vmrr.start) # At this point, the lease is feasible. # Commit changes by adding RRs to lease and to slot table # Add preparation RRs (if any) to lease for rr in preparation_rrs: lease.append_preparationrr(rr) # Add VMRR to lease lease.append_vmrr(vmrr) # Add resource reservations to slottable # Preparation RRs (if any) for rr in preparation_rrs: self.slottable.add_reservation(rr) # Pre-VM RRs (if any) for rr in vmrr.pre_rrs: self.slottable.add_reservation(rr) # VM self.slottable.add_reservation(vmrr) # Post-VM RRs (if any) for rr in vmrr.post_rrs: self.slottable.add_reservation(rr) # Change lease state if lease_state == Lease.STATE_PENDING or lease_state == Lease.STATE_QUEUED: lease.set_state(Lease.STATE_SCHEDULED) if is_ready: lease.set_state(Lease.STATE_READY) elif lease_state == Lease.STATE_SUSPENDED_PENDING or lease_state == Lease.STATE_SUSPENDED_QUEUED: lease.set_state(Lease.STATE_SUSPENDED_SCHEDULED) get_persistence().persist_lease(lease) lease.print_contents()
def schedule(self, nexttime): """ The main scheduling function The scheduling function looks at all pending requests and schedules them. Note that most of the actual scheduling code is contained in the __schedule_lease method and in the VMScheduler and PreparationScheduler classes. Arguments: nexttime -- The next time at which the scheduler can allocate resources. """ # Get pending leases pending_leases = self.leases.get_leases_by_state(Lease.STATE_PENDING) # we process all the leases at one time, without notion of queued leases # and now leases for choice in generate_combination(pending_leases): # we first check if IM leases are feasible im_leases = [l for l in choice if l.get_type() == Lease.IMMEDIATE] # Process leases that have to be queued. Right now, only best-effort leases get queued. queue_leases = [req for req in pending_leases if req.get_type() == Lease.BEST_EFFORT] # Queue leases for lease in queue_leases: self.__enqueue(lease) lease.set_state(Lease.STATE_QUEUED) self.logger.info("Queued lease request #%i, %i nodes for %s." % (lease.id, lease.numnodes, lease.duration.requested)) get_persistence().persist_lease(lease) # Process leases that have to be scheduled right away. Right now, this is any # lease that is not a best-effort lease (ARs, immediate, and deadlined leases) now_leases = [req for req in pending_leases if req.get_type() != Lease.BEST_EFFORT] # Schedule leases for lease in now_leases: lease_type = Lease.type_str[lease.get_type()] self.logger.info("Scheduling lease #%i (%i nodes) -- %s" % (lease.id, lease.numnodes, lease_type)) if lease.get_type() == Lease.ADVANCE_RESERVATION: self.logger.info("From %s to %s" % (lease.start.requested, lease.start.requested + lease.duration.requested)) elif lease.get_type() == Lease.DEADLINE: self.logger.info("Starting at %s. Deadline: %s" % (lease.start.requested, lease.deadline)) lease.print_contents() try: self.__schedule_lease(lease, nexttime=nexttime) self.logger.info("Lease #%i has been scheduled." % lease.id) ## BEGIN NOT-FIT-FOR-PRODUCTION CODE ## This should happen when the lease is requested. get_policy().pricing.feedback(lease) ## END NOT-FIT-FOR-PRODUCTION CODE lease.print_contents() except NotSchedulableException, exc: self.logger.info("Lease request #%i cannot be scheduled: %s" % (lease.id, exc.reason)) ## BEGIN NOT-FIT-FOR-PRODUCTION CODE ## This should happen when the lease is requested. if lease.price == -1: lease.set_state(Lease.STATE_REJECTED_BY_USER) else: lease.set_state(Lease.STATE_REJECTED) self.completed_leases.add(lease) self.accounting.at_lease_done(lease) ## BEGIN NOT-FIT-FOR-PRODUCTION CODE ## This should happen when the lease is requested. get_policy().pricing.feedback(lease) ## END NOT-FIT-FOR-PRODUCTION CODE self.leases.remove(lease) get_persistence().persist_lease(lease)