Ejemplo n.º 1
0
 def test_executes_multiple_concurrent(self):
     """Independent tasks will be executed concurrently within the same
     iteration of the graph loop.
     """
     g = TaskDependencyGraph(MockWorkflowContext())
     task1 = tasks.NOPLocalWorkflowTask(None)
     task2 = tasks.NOPLocalWorkflowTask(None)
     g.add_task(task1)
     g.add_task(task2)
     with limited_sleep_mock(limit=1):
         g.execute()
     self.assertTrue(task1.is_terminated)
     self.assertTrue(task2.is_terminated)
Ejemplo n.º 2
0
 def test_executes_single_task(self):
     """A single NOP task is executed within a single iteration of the
     tasks graph loop"""
     g = TaskDependencyGraph(MockWorkflowContext())
     task = tasks.NOPLocalWorkflowTask(None)
     g.add_task(task)
     with limited_sleep_mock(limit=1):
         g.execute()
     self.assertTrue(task.is_terminated)
Ejemplo n.º 3
0
def execute_operation(ctx, operation, operation_kwargs, allow_kwargs_override,
                      run_by_dependency_order, type_names, node_ids,
                      node_instance_ids, **kwargs):
    """ A generic workflow for executing arbitrary operations on nodes """

    graph = ctx.graph_mode()

    send_event_starting_tasks = {}
    send_event_done_tasks = {}

    # filtering node instances
    filtered_node_instances = []
    for node in ctx.nodes:
        if node_ids and node.id not in node_ids:
            continue
        if type_names and not next((type_name for type_name in type_names if
                                    type_name in node.type_hierarchy), None):
            continue

        for instance in node.instances:
            if node_instance_ids and instance.id not in node_instance_ids:
                continue
            filtered_node_instances.append(instance)

    # pre-preparing events tasks
    for instance in filtered_node_instances:
        start_event_message = 'Starting operation {0}'.format(operation)
        if operation_kwargs:
            start_event_message += ' (Operation parameters: {0})'.format(
                operation_kwargs)

        send_event_starting_tasks[instance.id] = \
            instance.send_event(start_event_message)
        send_event_done_tasks[instance.id] = \
            instance.send_event('Finished operation {0}'.format(operation))

    if run_by_dependency_order:
        # if run by dependency order is set, then create NOP tasks for the
        # rest of the instances. This is done to support indirect
        # dependencies, i.e. when instance A is dependent on instance B
        # which is dependent on instance C, where A and C are to be executed
        # with the operation on (i.e. they're in filtered_node_instances)
        # yet B isn't.
        # We add the NOP tasks rather than creating dependencies between A
        # and C themselves since even though it may sometimes increase the
        # number of dependency relationships in the execution graph, it also
        # ensures their number is linear to the number of relationships in
        # the deployment (e.g. consider if A and C are one out of N instances
        # of their respective nodes yet there's a single instance of B -
        # using NOP tasks we'll have 2N relationships instead of N^2).
        filtered_node_instances_ids = set(inst.id for inst in
                                          filtered_node_instances)
        for node in ctx.nodes:
            for instance in node.instances:
                if instance.id not in filtered_node_instances_ids:
                    nop_task = workflow_tasks.NOPLocalWorkflowTask(ctx)
                    send_event_starting_tasks[instance.id] = nop_task
                    send_event_done_tasks[instance.id] = nop_task
                    graph.add_task(nop_task)

    # preparing the parameters to the execute_operation call
    exec_op_params = dict()
    exec_op_params['kwargs'] = operation_kwargs
    exec_op_params['operation'] = operation
    if allow_kwargs_override is not None:
        exec_op_params['allow_kwargs_override'] = allow_kwargs_override

    # registering actual tasks to sequences
    for instance in filtered_node_instances:
        sequence = graph.sequence()
        sequence.add(
            send_event_starting_tasks[instance.id],
            instance.execute_operation(**exec_op_params),
            send_event_done_tasks[instance.id])

    # adding tasks dependencies if required
    if run_by_dependency_order:
        for node in ctx.nodes:
            for instance in node.instances:
                for rel in instance.relationships:
                    instance_starting_task = \
                        send_event_starting_tasks[instance.id]
                    target_done_task = \
                        send_event_done_tasks.get(rel.target_id)

                    graph.add_dependency(instance_starting_task,
                                         target_done_task)

    graph.execute()