Beispiel #1
0
    def get_logical_task_state(self, task_ex):
        task_spec = self.wf_spec.get_tasks()[task_ex.name]

        if not task_spec.get_join():
            # A simple 'non-join' task does not have any preconditions
            # based on state of other tasks so its logical state always
            # equals to its real state.
            return base.TaskLogicalState(task_ex.state, task_ex.state_info)

        return self._get_join_logical_state(task_spec)
 def get_logical_task_state(self, task_ex):
     # TODO(rakhmerov): Implement.
     return base.TaskLogicalState(task_ex.state, task_ex.state_info)
Beispiel #3
0
    def _get_join_logical_state(self, task_spec):
        """Evaluates logical state of 'join' task.

        :param task_spec: 'join' task specification.
        :return: TaskLogicalState (state, state_info, cardinality,
            triggered_by) where 'state' and 'state_info' describe the logical
            state of the given 'join' task and 'cardinality' gives the
            remaining number of unfulfilled preconditions. If logical state
            is not WAITING then 'cardinality' should always be 0.
        """

        # TODO(rakhmerov): We need to use task_ex instead of task_spec
        # in order to cover a use case when there's more than one instance
        # of the same 'join' task in a workflow.
        # TODO(rakhmerov): In some cases this method will be expensive because
        # it uses a multi-step recursive search. We need to optimize it moving
        # forward (e.g. with Workflow Execution Graph).

        join_expr = task_spec.get_join()

        in_task_specs = self.wf_spec.find_inbound_task_specs(task_spec)

        if not in_task_specs:
            return base.TaskLogicalState(states.RUNNING)

        # List of tuples (task_name, task_ex, state, depth, event_name).
        induced_states = []

        for t_s in in_task_specs:
            t_ex = self._find_task_execution_by_name(t_s.get_name())

            tup = self._get_induced_join_state(t_s, t_ex, task_spec)

            induced_states.append(
                (t_s.get_name(), t_ex, tup[0], tup[1], tup[2]))

        def count(state):
            cnt = 0
            total_depth = 0

            for s in induced_states:
                if s[2] == state:
                    cnt += 1
                    total_depth += s[3]

            return cnt, total_depth

        errors_tuple = count(states.ERROR)
        runnings_tuple = count(states.RUNNING)
        total_count = len(induced_states)

        def _blocked_message():
            return ('Blocked by tasks: %s' %
                    [s[0] for s in induced_states if s[2] == states.WAITING])

        def _failed_message():
            return ('Failed by tasks: %s' %
                    [s[0] for s in induced_states if s[2] == states.ERROR])

        def _triggered_by(state):
            return [{
                'task_id': s[1].id,
                'event': s[4]
            } for s in induced_states if s[2] == state and s[1] is not None]

        # If "join" is configured as a number or 'one'.
        if isinstance(join_expr, int) or join_expr == 'one':
            spec_cardinality = 1 if join_expr == 'one' else join_expr

            if runnings_tuple[0] >= spec_cardinality:
                return base.TaskLogicalState(states.RUNNING,
                                             triggered_by=_triggered_by(
                                                 states.RUNNING))

            # E.g. 'join: 3' with inbound [ERROR, ERROR, RUNNING, WAITING]
            # No chance to get 3 RUNNING states.
            if errors_tuple[0] > (total_count - spec_cardinality):
                return base.TaskLogicalState(states.ERROR, _failed_message())

            # Calculate how many tasks need to finish to trigger this 'join'.
            cardinality = spec_cardinality - runnings_tuple[0]

            return base.TaskLogicalState(states.WAITING,
                                         _blocked_message(),
                                         cardinality=cardinality)

        if join_expr == 'all':
            if total_count == runnings_tuple[0]:
                return base.TaskLogicalState(states.RUNNING,
                                             triggered_by=_triggered_by(
                                                 states.RUNNING))

            if errors_tuple[0] > 0:
                return base.TaskLogicalState(states.ERROR,
                                             _failed_message(),
                                             triggered_by=_triggered_by(
                                                 states.ERROR))

            # Remaining cardinality is just a difference between all tasks and
            # a number of those tasks that induce RUNNING state.
            cardinality = total_count - runnings_tuple[1]

            return base.TaskLogicalState(states.WAITING,
                                         _blocked_message(),
                                         cardinality=cardinality)

        raise RuntimeError('Unexpected join expression: %s' % join_expr)