コード例 #1
0
ファイル: tasks.py プロジェクト: shubhamdang/mistral
    def update(self, state, state_info=None):
        """Update task and set specified state.

        Method sets specified task state.

        :param state: New task state.
        :param state_info: New state information (i.e. error message).
        """

        assert self.task_ex

        # Ignore if task already completed.
        if states.is_completed(self.task_ex.state):
            return

        # Update only if state transition is valid.
        if not states.is_valid_transition(self.task_ex.state, state):
            return

        # We can't set the task state to RUNNING if some other
        # child executions are paused.
        child_states = [a_ex.state for a_ex in self.task_ex.executions]

        if state == states.RUNNING and states.PAUSED in child_states:
            return

        self.set_state(state, state_info)

        if states.is_completed(self.task_ex.state):
            self.register_workflow_completion_check()
コード例 #2
0
    def set_state(self, state, state_info=None, recursive=False):
        assert self.wf_ex

        cur_state = self.wf_ex.state

        if states.is_valid_transition(cur_state, state):
            wf_ex = db_api.update_workflow_execution_state(
                id=self.wf_ex.id,
                cur_state=cur_state,
                state=state
            )

            if wf_ex is None:
                # Do nothing because the state was updated previously.
                return

            self.wf_ex = wf_ex
            self.wf_ex.state_info = state_info

            wf_trace.info(
                self.wf_ex,
                "Workflow '%s' [%s -> %s, msg=%s]"
                % (self.wf_ex.workflow_name, cur_state, state, state_info)
            )
        else:
            msg = ("Can't change workflow execution state from %s to %s. "
                   "[workflow=%s, execution_id=%s]" %
                   (cur_state, state, self.wf_ex.name, self.wf_ex.id))

            raise exc.WorkflowException(msg)

        # Workflow result should be accepted by parent workflows (if any)
        # only if it completed successfully or failed.
        self.wf_ex.accepted = states.is_completed(state)

        if states.is_completed(state):
            # No need to keep task executions of this workflow in the
            # lookup cache anymore.
            lookup_utils.invalidate_cached_task_executions(self.wf_ex.id)

            triggers.on_workflow_complete(self.wf_ex)

        if recursive and self.wf_ex.task_execution_id:
            parent_task_ex = db_api.get_task_execution(
                self.wf_ex.task_execution_id
            )

            parent_wf = Workflow(wf_ex=parent_task_ex.workflow_execution)

            parent_wf.lock()

            parent_wf.set_state(state, recursive=recursive)

            # TODO(rakhmerov): It'd be better to use instance of Task here.
            parent_task_ex.state = state
            parent_task_ex.state_info = None
            parent_task_ex.processed = False
コード例 #3
0
    def update(self, state):
        assert self.action_ex

        if state == states.PAUSED and self.is_sync(self.action_ex.input):
            raise exc.InvalidStateTransitionException(
                'Transition to the PAUSED state is only supported '
                'for asynchronous action execution.')

        if not states.is_valid_transition(self.action_ex.state, state):
            raise exc.InvalidStateTransitionException(
                'Invalid state transition from %s to %s.' %
                (self.action_ex.state, state))

        self.action_ex.state = state
コード例 #4
0
ファイル: actions.py プロジェクト: openstack/mistral
    def update(self, state):
        assert self.action_ex

        if state == states.PAUSED and self.is_sync(self.action_ex.input):
            raise exc.InvalidStateTransitionException(
                'Transition to the PAUSED state is only supported '
                'for asynchronous action execution.'
            )

        if not states.is_valid_transition(self.action_ex.state, state):
            raise exc.InvalidStateTransitionException(
                'Invalid state transition from %s to %s.' %
                (self.action_ex.state, state)
            )

        self.action_ex.state = state
コード例 #5
0
ファイル: actions.py プロジェクト: shubhamdang/mistral
    def update(self, state):
        assert self.action_ex

        # TODO(rakhmerov): Not sure we can do it for all actions.
        action = self.action_desc.instantiate(self.action_ex.input, {})

        if state == states.PAUSED and action.is_sync():
            raise exc.InvalidStateTransitionException(
                'Transition to the PAUSED state is only supported '
                'for asynchronous action execution.')

        if not states.is_valid_transition(self.action_ex.state, state):
            raise exc.InvalidStateTransitionException(
                'Invalid state transition from %s to %s.' %
                (self.action_ex.state, state))

        self.action_ex.state = state
コード例 #6
0
    def update(self, state, state_info=None):
        """Update task and set specified state.

        Method sets specified task state.

        :param state: New task state.
        :param state_info: New state information (i.e. error message).
        """

        assert self.task_ex

        # Ignore if task already completed.
        if states.is_completed(self.task_ex.state):
            return

        # Update only if state transition is valid.
        if states.is_valid_transition(self.task_ex.state, state):
            self.set_state(state, state_info)
コード例 #7
0
def set_execution_state(wf_ex, state, state_info=None, set_upstream=False):
    cur_state = wf_ex.state

    if states.is_valid_transition(cur_state, state):
        wf_ex.state = state
        wf_ex.state_info = state_info

        wf_trace.info(
            wf_ex,
            "Execution of workflow '%s' [%s -> %s]"
            % (wf_ex.workflow_name, cur_state, state)
        )
    else:
        msg = ("Can't change workflow execution state from %s to %s. "
               "[workflow=%s, execution_id=%s]" %
               (cur_state, state, wf_ex.name, wf_ex.id))
        raise exc.WorkflowException(msg)

    # Workflow result should be accepted by parent workflows (if any)
    # only if it completed successfully or failed.
    wf_ex.accepted = wf_ex.state in (states.SUCCESS, states.ERROR)

    # If specified, then recursively set the state of the parent workflow
    # executions to the same state. Only changing state to RUNNING is
    # supported.
    if set_upstream and state == states.RUNNING and wf_ex.task_execution_id:
        task_ex = db_api.get_task_execution(wf_ex.task_execution_id)

        parent_wf_ex = lock_workflow_execution(task_ex.workflow_execution_id)

        set_execution_state(
            parent_wf_ex,
            state,
            state_info=state_info,
            set_upstream=set_upstream
        )

        task_handler.set_task_state(
            task_ex,
            state,
            state_info=None,
            processed=False
        )
コード例 #8
0
def set_execution_state(wf_ex, state, state_info=None):
    cur_state = wf_ex.state

    if states.is_valid_transition(cur_state, state):
        wf_ex.state = state
        wf_ex.state_info = state_info

        wf_trace.info(
            wf_ex, "Execution of workflow '%s' [%s -> %s]" %
            (wf_ex.workflow_name, cur_state, state))
    else:
        msg = ("Can't change workflow execution state from %s to %s. "
               "[workflow=%s, execution_id=%s]" %
               (cur_state, state, wf_ex.name, wf_ex.id))
        raise exc.WorkflowException(msg)

    # Workflow result should be accepted by parent workflows (if any)
    # only if it completed successfully.
    wf_ex.accepted = wf_ex.state == states.SUCCESS
コード例 #9
0
ファイル: workflows.py プロジェクト: openstack/mistral
    def set_state(self, state, state_info=None):
        assert self.wf_ex

        cur_state = self.wf_ex.state

        if states.is_valid_transition(cur_state, state):
            wf_ex = db_api.update_workflow_execution_state(
                id=self.wf_ex.id,
                cur_state=cur_state,
                state=state
            )

            if wf_ex is None:
                # Do nothing because the state was updated previously.
                return False

            self.wf_ex = wf_ex
            self.wf_ex.state_info = json.dumps(state_info) \
                if isinstance(state_info, dict) else state_info

            wf_trace.info(
                self.wf_ex,
                "Workflow '%s' [%s -> %s, msg=%s]" %
                (self.wf_ex.workflow_name,
                 cur_state,
                 state,
                 self.wf_ex.state_info)
            )
        else:
            msg = ("Can't change workflow execution state from %s to %s. "
                   "[workflow=%s, execution_id=%s]" %
                   (cur_state, state, self.wf_ex.name, self.wf_ex.id))

            raise exc.WorkflowException(msg)

        # Workflow result should be accepted by parent workflows (if any)
        # only if it completed successfully or failed.
        self.wf_ex.accepted = states.is_completed(state)

        if states.is_completed(state):
            triggers.on_workflow_complete(self.wf_ex)

        return True
コード例 #10
0
ファイル: workflows.py プロジェクト: kurtenbachkyle/mistral
    def set_state(self, state, state_info=None):
        assert self.wf_ex

        cur_state = self.wf_ex.state

        if states.is_valid_transition(cur_state, state):
            wf_ex = db_api.update_workflow_execution_state(
                id=self.wf_ex.id,
                cur_state=cur_state,
                state=state
            )

            if wf_ex is None:
                # Do nothing because the state was updated previously.
                return False

            self.wf_ex = wf_ex
            self.wf_ex.state_info = json.dumps(state_info) \
                if isinstance(state_info, dict) else state_info

            wf_trace.info(
                self.wf_ex,
                "Workflow '%s' [%s -> %s, msg=%s]" %
                (self.wf_ex.workflow_name,
                 cur_state,
                 state,
                 self.wf_ex.state_info)
            )
        else:
            msg = ("Can't change workflow execution state from %s to %s. "
                   "[workflow=%s, execution_id=%s]" %
                   (cur_state, state, self.wf_ex.name, self.wf_ex.id))

            raise exc.WorkflowException(msg)

        # Workflow result should be accepted by parent workflows (if any)
        # only if it completed successfully or failed.
        self.wf_ex.accepted = states.is_completed(state)

        if states.is_completed(state):
            triggers.on_workflow_complete(self.wf_ex)

        return True
コード例 #11
0
ファイル: workflows.py プロジェクト: anilyadav/mistral
    def set_state(self, state, state_info=None, recursive=False):
        assert self.wf_ex

        cur_state = self.wf_ex.state

        if states.is_valid_transition(cur_state, state):
            self.wf_ex.state = state
            self.wf_ex.state_info = state_info

            wf_trace.info(
                self.wf_ex,
                "Execution of workflow '%s' [%s -> %s]"
                % (self.wf_ex.workflow_name, cur_state, state)
            )
        else:
            msg = ("Can't change workflow execution state from %s to %s. "
                   "[workflow=%s, execution_id=%s]" %
                   (cur_state, state, self.wf_ex.name, self.wf_ex.id))

            raise exc.WorkflowException(msg)

        # Workflow result should be accepted by parent workflows (if any)
        # only if it completed successfully or failed.
        self.wf_ex.accepted = states.is_completed(state)

        if recursive and self.wf_ex.task_execution_id:
            parent_task_ex = db_api.get_task_execution(
                self.wf_ex.task_execution_id
            )

            parent_wf = Workflow(
                db_api.get_workflow_definition(parent_task_ex.workflow_id),
                parent_task_ex.workflow_execution
            )

            parent_wf.lock()

            parent_wf.set_state(state, recursive=recursive)

            # TODO(rakhmerov): It'd be better to use instance of Task here.
            parent_task_ex.state = state
            parent_task_ex.state_info = None
            parent_task_ex.processed = False
コード例 #12
0
ファイル: workflows.py プロジェクト: xavierhardy/mistral
    def set_state(self, state, state_info=None, recursive=False):
        assert self.wf_ex

        cur_state = self.wf_ex.state

        if states.is_valid_transition(cur_state, state):
            self.wf_ex.state = state
            self.wf_ex.state_info = state_info

            wf_trace.info(
                self.wf_ex,
                "Execution of workflow '%s' [%s -> %s]"
                % (self.wf_ex.workflow_name, cur_state, state)
            )
        else:
            msg = ("Can't change workflow execution state from %s to %s. "
                   "[workflow=%s, execution_id=%s]" %
                   (cur_state, state, self.wf_ex.name, self.wf_ex.id))

            raise exc.WorkflowException(msg)

        # Workflow result should be accepted by parent workflows (if any)
        # only if it completed successfully or failed.
        self.wf_ex.accepted = state in (states.SUCCESS, states.ERROR)

        if recursive and self.wf_ex.task_execution_id:
            parent_task_ex = db_api.get_task_execution(
                self.wf_ex.task_execution_id
            )

            parent_wf = Workflow(
                db_api.get_workflow_definition(parent_task_ex.workflow_id),
                parent_task_ex.workflow_execution
            )

            parent_wf.lock()

            parent_wf.set_state(state, recursive=recursive)

            # TODO(rakhmerov): It'd be better to use instance of Task here.
            parent_task_ex.state = state
            parent_task_ex.state_info = None
            parent_task_ex.processed = False
コード例 #13
0
def set_workflow_state(wf_ex, state, state_info=None, set_upstream=False):
    cur_state = wf_ex.state

    if states.is_valid_transition(cur_state, state):
        wf_ex.state = state
        wf_ex.state_info = state_info

        wf_trace.info(
            wf_ex, "Execution of workflow '%s' [%s -> %s]" %
            (wf_ex.workflow_name, cur_state, state))
    else:
        msg = ("Can't change workflow execution state from %s to %s. "
               "[workflow=%s, execution_id=%s]" %
               (cur_state, state, wf_ex.name, wf_ex.id))
        raise exc.WorkflowException(msg)

    # Workflow result should be accepted by parent workflows (if any)
    # only if it completed successfully or failed.
    wf_ex.accepted = wf_ex.state in (states.SUCCESS, states.ERROR)

    # If specified, then recursively set the state of the parent workflow
    # executions to the same state. Only changing state to RUNNING is
    # supported.
    # TODO(rakhmerov): I don't like this hardcoded special case. It's
    # used only to continue the workflow (rerun) but at the first glance
    # seems like a generic behavior. Need to handle it differently.
    if set_upstream and state == states.RUNNING and wf_ex.task_execution_id:
        task_ex = db_api.get_task_execution(wf_ex.task_execution_id)

        parent_wf_ex = lock_workflow_execution(task_ex.workflow_execution_id)

        set_workflow_state(parent_wf_ex,
                           state,
                           state_info=state_info,
                           set_upstream=set_upstream)

        # TODO(rakhmerov): How do we need to set task state properly?
        # It doesn't seem right to intervene into the parent workflow
        # internals. We just need to communicate changes back to parent
        # worklfow and it should do what's needed itself.
        task_ex.state = state
        task_ex.state_info = None
        task_ex.processed = False
コード例 #14
0
ファイル: workflow_handler.py プロジェクト: ainkov/mistral
def set_execution_state(wf_ex, state, state_info=None):
    cur_state = wf_ex.state

    if states.is_valid_transition(cur_state, state):
        wf_ex.state = state
        wf_ex.state_info = state_info

        wf_trace.info(
            wf_ex,
            "Execution of workflow '%s' [%s -> %s]"
            % (wf_ex.workflow_name, cur_state, state)
        )
    else:
        msg = ("Can't change workflow execution state from %s to %s. "
               "[workflow=%s, execution_id=%s]" %
               (cur_state, state, wf_ex.name, wf_ex.id))
        raise exc.WorkflowException(msg)

    # Workflow result should be accepted by parent workflows (if any)
    # only if it completed successfully.
    wf_ex.accepted = wf_ex.state == states.SUCCESS
コード例 #15
0
    def update(self, state, state_info=None):
        """Update task and set specified state.

        Method sets specified task state.

        :param state: New task state.
        :param state_info: New state information (i.e. error message).
        """

        assert self.task_ex

        # Record the current task state.
        old_task_state = self.task_ex.state

        # Ignore if task already completed.
        if states.is_completed(self.task_ex.state):
            # Publish task event again so subscribers know
            # task completed state is being processed again.
            self.notify(old_task_state, self.task_ex.state)

            return

        # Update only if state transition is valid.
        if not states.is_valid_transition(self.task_ex.state, state):
            return

        # We can't set the task state to RUNNING if some other
        # child executions are paused.
        child_states = [a_ex.state for a_ex in self.task_ex.executions]

        if state == states.RUNNING and states.PAUSED in child_states:
            return

        self.set_state(state, state_info)

        if states.is_completed(self.task_ex.state):
            self.register_workflow_completion_check()

        # Publish event.
        self.notify(old_task_state, self.task_ex.state)
コード例 #16
0
ファイル: tasks.py プロジェクト: openstack/mistral
    def update(self, state, state_info=None):
        """Update task and set specified state.

        Method sets specified task state.

        :param state: New task state.
        :param state_info: New state information (i.e. error message).
        """

        assert self.task_ex

        # Record the current task state.
        old_task_state = self.task_ex.state

        # Ignore if task already completed.
        if states.is_completed(self.task_ex.state):
            # Publish task event again so subscribers know
            # task completed state is being processed again.
            self.notify(old_task_state, self.task_ex.state)

            return

        # Update only if state transition is valid.
        if not states.is_valid_transition(self.task_ex.state, state):
            return

        # We can't set the task state to RUNNING if some other
        # child executions are paused.
        child_states = [a_ex.state for a_ex in self.task_ex.executions]

        if state == states.RUNNING and states.PAUSED in child_states:
            return

        self.set_state(state, state_info)

        if states.is_completed(self.task_ex.state):
            self.register_workflow_completion_check()

        # Publish event.
        self.notify(old_task_state, self.task_ex.state)
コード例 #17
0
def set_execution_state(wf_ex, state, state_info=None, set_upstream=False):
    cur_state = wf_ex.state

    if states.is_valid_transition(cur_state, state):
        wf_ex.state = state
        wf_ex.state_info = state_info

        wf_trace.info(
            wf_ex, "Execution of workflow '%s' [%s -> %s]" %
            (wf_ex.workflow_name, cur_state, state))
    else:
        msg = ("Can't change workflow execution state from %s to %s. "
               "[workflow=%s, execution_id=%s]" %
               (cur_state, state, wf_ex.name, wf_ex.id))
        raise exc.WorkflowException(msg)

    # Workflow result should be accepted by parent workflows (if any)
    # only if it completed successfully.
    wf_ex.accepted = wf_ex.state == states.SUCCESS

    # If specified, then recursively set the state of the parent workflow
    # executions to the same state. Only changing state to RUNNING is
    # supported.
    if set_upstream and state == states.RUNNING and wf_ex.task_execution_id:
        task_ex = db_api.get_task_execution(wf_ex.task_execution_id)

        parent_wf_ex = lock_workflow_execution(task_ex.workflow_execution_id)

        set_execution_state(parent_wf_ex,
                            state,
                            state_info=state_info,
                            set_upstream=set_upstream)

        task_handler.set_task_state(task_ex,
                                    state,
                                    state_info=None,
                                    processed=False)
コード例 #18
0
    def test_is_valid_transition(self):
        # From IDLE
        self.assertTrue(s.is_valid_transition(s.IDLE, s.IDLE))
        self.assertTrue(s.is_valid_transition(s.IDLE, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.IDLE, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.IDLE, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.IDLE, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.IDLE, s.SUCCESS))

        # From RUNNING
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.ERROR))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.PAUSED))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.RUNNING_DELAYED))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.RUNNING, s.IDLE))

        # From PAUSED
        self.assertTrue(s.is_valid_transition(s.PAUSED, s.PAUSED))
        self.assertTrue(s.is_valid_transition(s.PAUSED, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.PAUSED, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.PAUSED, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.PAUSED, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.PAUSED, s.IDLE))

        # From DELAYED
        self.assertTrue(
            s.is_valid_transition(s.RUNNING_DELAYED, s.RUNNING_DELAYED))
        self.assertTrue(s.is_valid_transition(s.RUNNING_DELAYED, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.RUNNING_DELAYED, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.RUNNING_DELAYED, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.RUNNING_DELAYED, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.RUNNING_DELAYED, s.IDLE))

        # From SUCCESS
        self.assertTrue(s.is_valid_transition(s.SUCCESS, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.RUNNING))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.IDLE))

        # From ERROR
        self.assertTrue(s.is_valid_transition(s.ERROR, s.ERROR))
        self.assertTrue(s.is_valid_transition(s.ERROR, s.RUNNING))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.IDLE))

        # From WAITING
        self.assertTrue(s.is_valid_transition(s.WAITING, s.RUNNING))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.IDLE))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.ERROR))
コード例 #19
0
ファイル: test_states.py プロジェクト: ISCAS-VDI/mistral-base
    def test_is_valid_transition(self):
        # From IDLE
        self.assertTrue(s.is_valid_transition(s.IDLE, s.IDLE))
        self.assertTrue(s.is_valid_transition(s.IDLE, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.IDLE, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.IDLE, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.IDLE, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.IDLE, s.SUCCESS))

        # From RUNNING
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.ERROR))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.PAUSED))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.RUNNING_DELAYED))
        self.assertTrue(s.is_valid_transition(s.RUNNING, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.RUNNING, s.IDLE))

        # From PAUSED
        self.assertTrue(s.is_valid_transition(s.PAUSED, s.PAUSED))
        self.assertTrue(s.is_valid_transition(s.PAUSED, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.PAUSED, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.PAUSED, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.PAUSED, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.PAUSED, s.IDLE))

        # From DELAYED
        self.assertTrue(
            s.is_valid_transition(s.RUNNING_DELAYED, s.RUNNING_DELAYED)
        )
        self.assertTrue(s.is_valid_transition(s.RUNNING_DELAYED, s.RUNNING))
        self.assertTrue(s.is_valid_transition(s.RUNNING_DELAYED, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.RUNNING_DELAYED, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.RUNNING_DELAYED, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.RUNNING_DELAYED, s.IDLE))

        # From SUCCESS
        self.assertTrue(s.is_valid_transition(s.SUCCESS, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.RUNNING))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.ERROR))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.SUCCESS, s.IDLE))

        # From ERROR
        self.assertTrue(s.is_valid_transition(s.ERROR, s.ERROR))
        self.assertTrue(s.is_valid_transition(s.ERROR, s.RUNNING))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.ERROR, s.IDLE))

        # From WAITING
        self.assertTrue(s.is_valid_transition(s.WAITING, s.RUNNING))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.SUCCESS))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.PAUSED))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.RUNNING_DELAYED))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.IDLE))
        self.assertFalse(s.is_valid_transition(s.WAITING, s.ERROR))