2013-06-23 17:03:55 +02:00
|
|
|
from common.exceptions import TaskListError
|
2013-06-09 20:29:54 +02:00
|
|
|
import logging
|
|
|
|
log = logging.getLogger(__name__)
|
2013-05-02 19:13:35 +02:00
|
|
|
|
|
|
|
|
2013-06-23 15:26:08 +02:00
|
|
|
class TaskList(object):
|
2013-05-16 08:00:28 +02:00
|
|
|
|
2013-06-23 15:26:08 +02:00
|
|
|
def __init__(self):
|
|
|
|
self.tasks = set()
|
2013-06-26 23:40:42 +02:00
|
|
|
self.tasks_completed = []
|
2013-06-23 15:26:08 +02:00
|
|
|
|
|
|
|
def add(self, *args):
|
|
|
|
self.tasks.update(args)
|
|
|
|
|
2013-06-30 23:54:28 +02:00
|
|
|
def remove(self, *args):
|
2013-09-22 17:31:43 +02:00
|
|
|
for task in args:
|
|
|
|
self.tasks.discard(task)
|
2013-06-23 15:26:08 +02:00
|
|
|
|
2013-05-16 08:00:28 +02:00
|
|
|
def run(self, bootstrap_info):
|
2013-06-24 23:12:39 +02:00
|
|
|
task_list = self.create_list(self.tasks)
|
2013-06-23 17:54:25 +02:00
|
|
|
log.debug('Tasklist:\n\t{list}'.format(list='\n\t'.join(repr(task) for task in task_list)))
|
2013-06-23 17:03:55 +02:00
|
|
|
|
2013-09-22 17:31:43 +02:00
|
|
|
for task_type in task_list:
|
|
|
|
task = task_type()
|
2013-06-26 23:40:42 +02:00
|
|
|
if hasattr(task, 'description'):
|
|
|
|
log.info(task.description)
|
|
|
|
else:
|
|
|
|
log.info('Running {task}'.format(task=task))
|
|
|
|
task.run(bootstrap_info)
|
|
|
|
self.tasks_completed.append(task)
|
2013-06-24 23:12:39 +02:00
|
|
|
|
|
|
|
def create_list(self, tasks):
|
2013-06-23 17:54:25 +02:00
|
|
|
from common.phases import order
|
2013-06-23 17:03:55 +02:00
|
|
|
graph = {}
|
2013-06-24 23:12:39 +02:00
|
|
|
for task in tasks:
|
2013-09-22 17:31:43 +02:00
|
|
|
self.check_ordering(task)
|
|
|
|
successors = set()
|
|
|
|
successors.update(task.before)
|
|
|
|
successors.update(filter(lambda succ: task in succ.after, tasks))
|
2013-08-17 17:28:46 +02:00
|
|
|
succeeding_phases = order[order.index(task.phase) + 1:]
|
2013-09-22 17:31:43 +02:00
|
|
|
successors.update(filter(lambda succ: succ.phase in succeeding_phases, tasks))
|
|
|
|
graph[task] = filter(lambda succ: succ in tasks, successors)
|
2013-06-23 17:54:25 +02:00
|
|
|
|
2013-06-23 17:03:55 +02:00
|
|
|
components = self.strongly_connected_components(graph)
|
|
|
|
cycles_found = 0
|
|
|
|
for component in components:
|
|
|
|
if len(component) > 1:
|
|
|
|
cycles_found += 1
|
2013-06-23 17:54:25 +02:00
|
|
|
log.debug('Cycle: {list}\n'.format(list=', '.join(repr(task) for task in component)))
|
2013-06-23 17:03:55 +02:00
|
|
|
if cycles_found > 0:
|
|
|
|
msg = ('{0} cycles were found in the tasklist, '
|
|
|
|
'consult the logfile for more information.'.format(cycles_found))
|
|
|
|
raise TaskListError(msg)
|
|
|
|
|
|
|
|
sorted_tasks = self.topological_sort(graph)
|
|
|
|
|
2013-06-23 17:54:25 +02:00
|
|
|
return sorted_tasks
|
2013-06-23 17:03:55 +02:00
|
|
|
|
2013-09-22 17:31:43 +02:00
|
|
|
def check_ordering(self, task):
|
|
|
|
for successor in task.before:
|
|
|
|
if successor.phase > successor.phase:
|
|
|
|
msg = ("The task {task} is specified as running before {other}, "
|
|
|
|
"but its phase '{phase}' lies after the phase '{other_phase}'"
|
|
|
|
.format(task=task, other=successor, phase=task.phase, other_phase=successor.phase))
|
|
|
|
raise TaskListError(msg)
|
|
|
|
for predecessor in task.after:
|
|
|
|
if task.phase < predecessor.phase:
|
|
|
|
msg = ("The task {task} is specified as running after {other}, "
|
|
|
|
"but its phase '{phase}' lies before the phase '{other_phase}'"
|
|
|
|
.format(task=task, other=predecessor, phase=task.phase, other_phase=predecessor.phase))
|
|
|
|
raise TaskListError(msg)
|
|
|
|
|
2013-06-23 17:03:55 +02:00
|
|
|
def strongly_connected_components(self, graph):
|
|
|
|
# Source: http://www.logarithmic.net/pfh-files/blog/01208083168/sort.py
|
|
|
|
# Find the strongly connected components in a graph using Tarjan's algorithm.
|
|
|
|
# graph should be a dictionary mapping node names to lists of successor nodes.
|
|
|
|
|
2013-06-26 20:14:37 +02:00
|
|
|
result = []
|
|
|
|
stack = []
|
|
|
|
low = {}
|
2013-06-23 17:03:55 +02:00
|
|
|
|
|
|
|
def visit(node):
|
2013-06-26 20:14:37 +02:00
|
|
|
if node in low:
|
|
|
|
return
|
2013-06-23 17:03:55 +02:00
|
|
|
|
|
|
|
num = len(low)
|
|
|
|
low[node] = num
|
|
|
|
stack_pos = len(stack)
|
|
|
|
stack.append(node)
|
|
|
|
|
|
|
|
for successor in graph[node]:
|
|
|
|
visit(successor)
|
|
|
|
low[node] = min(low[node], low[successor])
|
|
|
|
|
|
|
|
if num == low[node]:
|
|
|
|
component = tuple(stack[stack_pos:])
|
|
|
|
del stack[stack_pos:]
|
|
|
|
result.append(component)
|
|
|
|
for item in component:
|
|
|
|
low[item] = len(graph)
|
|
|
|
|
|
|
|
for node in graph:
|
|
|
|
visit(node)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
def topological_sort(self, graph):
|
|
|
|
# Source: http://www.logarithmic.net/pfh-files/blog/01208083168/sort.py
|
2013-06-26 20:14:37 +02:00
|
|
|
count = {}
|
2013-06-23 17:03:55 +02:00
|
|
|
for node in graph:
|
|
|
|
count[node] = 0
|
|
|
|
for node in graph:
|
|
|
|
for successor in graph[node]:
|
|
|
|
count[successor] += 1
|
|
|
|
|
2013-06-26 20:14:37 +02:00
|
|
|
ready = [node for node in graph if count[node] == 0]
|
2013-06-23 17:03:55 +02:00
|
|
|
|
2013-06-26 20:14:37 +02:00
|
|
|
result = []
|
2013-06-23 17:03:55 +02:00
|
|
|
while ready:
|
|
|
|
node = ready.pop(-1)
|
|
|
|
result.append(node)
|
|
|
|
|
|
|
|
for successor in graph[node]:
|
|
|
|
count[successor] -= 1
|
|
|
|
if count[successor] == 0:
|
|
|
|
ready.append(successor)
|
|
|
|
|
|
|
|
return result
|