
Remove line containing comment - # vim: tabstop=4 shiftwidth=4 softtabstop=4 Change-Id: I7581cc88b8de433d5609ed06c6570b0b45c13573 Closes-Bug:#1229324
192 lines
6.5 KiB
Python
192 lines
6.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (C) 2012-2013 Yahoo! Inc. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from taskflow import exceptions as exc
|
|
|
|
# Job states.
|
|
CLAIMED = 'CLAIMED'
|
|
FAILURE = 'FAILURE'
|
|
PENDING = 'PENDING'
|
|
RUNNING = 'RUNNING'
|
|
SUCCESS = 'SUCCESS'
|
|
UNCLAIMED = 'UNCLAIMED'
|
|
|
|
# Flow states.
|
|
FAILURE = FAILURE
|
|
PENDING = PENDING
|
|
REVERTING = 'REVERTING'
|
|
REVERTED = 'REVERTED'
|
|
RUNNING = RUNNING
|
|
SUCCESS = SUCCESS
|
|
SUSPENDING = 'SUSPENDING'
|
|
SUSPENDED = 'SUSPENDED'
|
|
RESUMING = 'RESUMING'
|
|
|
|
# Task states.
|
|
FAILURE = FAILURE
|
|
PENDING = PENDING
|
|
REVERTED = REVERTED
|
|
REVERTING = REVERTING
|
|
SUCCESS = SUCCESS
|
|
|
|
# TODO(harlowja): use when we can timeout tasks??
|
|
TIMED_OUT = 'TIMED_OUT'
|
|
|
|
|
|
## Flow state transitions
|
|
# https://wiki.openstack.org/wiki/TaskFlow/States_of_Task_and_Flow#Flow_States
|
|
|
|
_ALLOWED_FLOW_TRANSITIONS = frozenset((
|
|
(PENDING, RUNNING), # run it!
|
|
|
|
(RUNNING, SUCCESS), # all tasks finished successfully
|
|
(RUNNING, FAILURE), # some of task failed
|
|
(RUNNING, SUSPENDING), # engine.suspend was called
|
|
(RUNNING, RESUMING), # resuming from a previous running
|
|
|
|
(SUCCESS, RUNNING), # see note below
|
|
|
|
(FAILURE, RUNNING), # see note below
|
|
(FAILURE, REVERTING), # flow failed, do cleanup now
|
|
|
|
(REVERTING, REVERTED), # revert done
|
|
(REVERTING, FAILURE), # revert failed
|
|
(REVERTING, SUSPENDING), # engine.suspend was called
|
|
(REVERTING, RESUMING), # resuming from a previous reverting
|
|
|
|
(REVERTED, PENDING), # try again
|
|
|
|
(SUSPENDING, SUSPENDED), # suspend finished
|
|
(SUSPENDING, SUCCESS), # all tasks finished while we were waiting
|
|
(SUSPENDING, FAILURE), # some tasks failed while we were waiting
|
|
(SUSPENDING, REVERTED), # all tasks were reverted while we were waiting
|
|
(SUSPENDING, RESUMING), # resuming from a previous suspending
|
|
|
|
(SUSPENDED, RUNNING), # restart from suspended
|
|
(SUSPENDED, REVERTING), # revert from suspended
|
|
|
|
(RESUMING, SUSPENDED), # after flow resumed, it is suspended
|
|
))
|
|
|
|
|
|
# NOTE(imelnikov) SUCCESS->RUNNING and FAILURE->RUNNING transitions are
|
|
# useful when flow or flowdetails backing it were altered after the flow
|
|
# was finished; then, client code may want to run through flow again
|
|
# to ensure all tasks from updated flow had a chance to run.
|
|
|
|
|
|
# NOTE(imelnikov): Engine cannot transition flow from SUSPENDING to
|
|
# SUSPENDED while some tasks from the flow are running and some results
|
|
# from them are not retrieved and saved properly, so while flow is
|
|
# in SUSPENDING state it may wait for some of the tasks to stop. Then,
|
|
# flow can go to SUSPENDED, SUCCESS, FAILURE or REVERTED state depending
|
|
# of actual state of the tasks -- e.g. if all tasks were finished
|
|
# successfully while we were waiting, flow can be transitioned from
|
|
# SUSPENDING to SUCCESS state.
|
|
|
|
_IGNORED_FLOW_TRANSITIONS = frozenset(
|
|
(a, b)
|
|
for a in (PENDING, FAILURE, SUCCESS, SUSPENDED, REVERTED)
|
|
for b in (SUSPENDING, SUSPENDED, RESUMING)
|
|
if a != b
|
|
)
|
|
|
|
|
|
def check_flow_transition(old_state, new_state):
|
|
"""Check that flow can transition from old_state to new_state.
|
|
|
|
If transition can be performed, it returns True. If transition
|
|
should be ignored, it returns False. If transition is not
|
|
valid, it raises InvalidState exception.
|
|
"""
|
|
if old_state == new_state:
|
|
return False
|
|
pair = (old_state, new_state)
|
|
if pair in _ALLOWED_FLOW_TRANSITIONS:
|
|
return True
|
|
if pair in _IGNORED_FLOW_TRANSITIONS:
|
|
return False
|
|
raise exc.InvalidState("Flow transition from %s to %s is not allowed"
|
|
% pair)
|
|
|
|
|
|
## Task state transitions
|
|
# https://wiki.openstack.org/wiki/TaskFlow/States_of_Task_and_Flow#Task_States
|
|
|
|
_ALLOWED_TASK_TRANSITIONS = frozenset((
|
|
(PENDING, RUNNING), # run it!
|
|
|
|
(RUNNING, SUCCESS), # the task finished successfully
|
|
(RUNNING, FAILURE), # the task failed
|
|
|
|
(FAILURE, REVERTING), # task failed, do cleanup now
|
|
(SUCCESS, REVERTING), # some other task failed, do cleanup now
|
|
|
|
(REVERTING, REVERTED), # revert done
|
|
(REVERTING, FAILURE), # revert failed
|
|
|
|
(REVERTED, PENDING), # try again
|
|
|
|
# NOTE(harlowja): allow the tasks to restart if in the same state
|
|
# as a they were in before as a task may be 'killed' while in one of the
|
|
# below states and it is permissible to let the task to re-enter that
|
|
# same state to try to finish.
|
|
(REVERTING, REVERTING),
|
|
(RUNNING, RUNNING),
|
|
|
|
# NOTE(harlowja): the task was 'killed' while in one of the starting/ending
|
|
# states and it is permissible to let the task to start running or
|
|
# reverting again (if it really wants too).
|
|
(REVERTING, RUNNING),
|
|
(RUNNING, REVERTING),
|
|
))
|
|
|
|
_IGNORED_TASK_TRANSITIONS = [
|
|
(SUCCESS, RUNNING), # already finished
|
|
(PENDING, REVERTING), # never ran in the first place
|
|
(REVERTED, REVERTING), # the task already reverted
|
|
]
|
|
|
|
# NOTE(harlowja): ignore transitions to the same state (in these cases).
|
|
#
|
|
# NOTE(harlowja): the above ALLOWED_TASK_TRANSITIONS does allow
|
|
# transitions to certain equivalent states (but only for a few special
|
|
# cases).
|
|
_IGNORED_TASK_TRANSITIONS.extend(
|
|
(a, a) for a in (PENDING, FAILURE, SUCCESS, REVERTED)
|
|
)
|
|
|
|
_IGNORED_TASK_TRANSITIONS = frozenset(_IGNORED_TASK_TRANSITIONS)
|
|
|
|
|
|
def check_task_transition(old_state, new_state):
|
|
"""Check that task can transition from old_state to new_state.
|
|
|
|
If transition can be performed, it returns True. If transition
|
|
should be ignored, it returns False. If transition is not
|
|
valid, it raises InvalidState exception.
|
|
"""
|
|
pair = (old_state, new_state)
|
|
if pair in _ALLOWED_TASK_TRANSITIONS:
|
|
return True
|
|
if pair in _IGNORED_TASK_TRANSITIONS:
|
|
return False
|
|
# TODO(harlowja): Should we check/allow for 3rd party states to be
|
|
# triggered during RUNNING by having a concept of a sub-state that we also
|
|
# verify against??
|
|
raise exc.InvalidState("Task transition from %s to %s is not allowed"
|
|
% pair)
|