| from autotest_lib.client.common_lib import utils |
| |
| class HostSchedulingUtility(object): |
| """Interface to host availability information from the scheduler.""" |
| def hosts_in_label(self, label_id): |
| """Return potentially usable hosts with the given label.""" |
| raise NotImplementedError |
| |
| |
| def remove_host_from_label(self, host_id, label_id): |
| """Remove this host from the internal list of usable hosts in the label. |
| |
| This is provided as an optimization -- when code gets a host from a |
| label and concludes it's unusable, it can call this to avoid getting |
| that host again in the future (within this tick). This function should |
| not affect correctness. |
| """ |
| raise NotImplementedError |
| |
| |
| def pop_host(self, host_id): |
| """Remove and return a host from the internal list of available hosts. |
| """ |
| raise NotImplementedError |
| |
| |
| def ineligible_hosts_for_entry(self, queue_entry): |
| """Get the list of hosts ineligible to run the given queue entry.""" |
| raise NotImplementedError |
| |
| |
| def is_host_usable(self, host_id): |
| """Determine if the host is currently usable at all.""" |
| raise NotImplementedError |
| |
| |
| def is_host_eligible_for_job(self, host_id, queue_entry): |
| """Determine if the host is eligible specifically for this queue entry. |
| |
| @param queue_entry: a HostQueueEntry DBObject |
| """ |
| raise NotImplementedError |
| |
| |
| class MetahostScheduler(object): |
| def can_schedule_metahost(self, queue_entry): |
| """Return true if this object can schedule the given queue entry. |
| |
| At most one MetahostScheduler should return true for any given entry. |
| |
| @param queue_entry: a HostQueueEntry DBObject |
| """ |
| raise NotImplementedError |
| |
| |
| def schedule_metahost(self, queue_entry, scheduling_utility): |
| """Schedule the given queue entry, if possible. |
| |
| This method should make necessary database changes culminating in |
| assigning a host to the given queue entry in the database. It may |
| take no action if no host can be assigned currently. |
| |
| @param queue_entry: a HostQueueEntry DBObject |
| @param scheduling_utility: a HostSchedulingUtility object |
| """ |
| raise NotImplementedError |
| |
| |
| def recovery_on_startup(self): |
| """Perform any necessary recovery upon scheduler startup.""" |
| pass |
| |
| |
| def tick(self): |
| """Called once per scheduler cycle; any actions are allowed.""" |
| pass |
| |
| |
| class LabelMetahostScheduler(MetahostScheduler): |
| def can_schedule_metahost(self, queue_entry): |
| return bool(queue_entry.meta_host) |
| |
| |
| def schedule_metahost(self, queue_entry, scheduling_utility): |
| label_id = queue_entry.meta_host |
| hosts_in_label = scheduling_utility.hosts_in_label(label_id) |
| ineligible_host_ids = scheduling_utility.ineligible_hosts_for_entry( |
| queue_entry) |
| |
| for host_id in hosts_in_label: |
| if not scheduling_utility.is_host_usable(host_id): |
| scheduling_utility.remove_host_from_label(host_id, label_id) |
| continue |
| if host_id in ineligible_host_ids: |
| continue |
| if not scheduling_utility.is_host_eligible_for_job(host_id, |
| queue_entry): |
| continue |
| |
| # Remove the host from our cached internal state before returning |
| scheduling_utility.remove_host_from_label(host_id, label_id) |
| host = scheduling_utility.pop_host(host_id) |
| queue_entry.set_host(host) |
| return |
| |
| |
| def get_metahost_schedulers(): |
| return [LabelMetahostScheduler()] |