Files
deb-mistral/mistral/workflow/commands.py
LingxianKong 77cf6eb26a Fix invalid workflow completion in case of "join"
When we use 'join' clause in workflow execution, the execution should be
in running status, until all possible upstream tasks are completed and
corresponding conditions have triggered.

Do not remove the tasks with unsatisfied join when continuing execution,
or the workflow will succeed erroneously. So, a new attribute
'wait_flag' is added to RunTask class, to indicate if the task should
run immediately.

The difference between 'waiting' and 'delayed' status is, 'waiting'
indicates a task needs to wait for its conditions to be satisfied,
'delayed' means the task has to wait because of policy(wait-before,
wait-after or retry).

Change-Id: Idcd162b9c000b85acf2adabc158f848d0824ac57
Closes-Bug: #1455038
2015-06-30 17:11:42 +08:00

148 lines
4.0 KiB
Python

# Copyright 2015 - Mirantis, Inc.
#
# 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 mistral.workbook import parser as spec_parser
from mistral.workbook.v2 import tasks
from mistral.workflow import states
class WorkflowCommand(object):
"""Workflow command.
A set of workflow commands form a communication protocol between workflow
handler and its clients. When workflow handler makes a decision about
how to continue a workflow it returns a set of commands so that a caller
knows what to do next.
"""
def __init__(self, wf_ex, task_spec, ctx):
self.wf_ex = wf_ex
self.task_spec = task_spec
self.ctx = ctx or {}
class Noop(WorkflowCommand):
"""No-operation command."""
def __repr__(self):
return "NOOP [workflow=%s]" % self.wf_ex.name
class RunTask(WorkflowCommand):
"""Instruction to run a workflow task."""
def __init__(self, wf_ex, task_spec, ctx):
super(RunTask, self).__init__(wf_ex, task_spec, ctx)
self.wait_flag = False
def is_waiting(self):
return (self.wait_flag and
isinstance(self.task_spec, tasks.DirectWorkflowTaskSpec) and
self.task_spec.get_join())
def __repr__(self):
return (
"Run task [workflow=%s, task=%s, waif_flag=%s]"
% (self.wf_ex.name, self.task_spec.get_name(), self.wait_flag)
)
class RunExistingTask(WorkflowCommand):
"""Command for running already existent task."""
def __init__(self, task_ex):
wf_ex = task_ex.workflow_execution
task_spec = spec_parser.get_task_spec(task_ex.spec)
self.task_ex = task_ex
super(RunExistingTask, self).__init__(
wf_ex, task_spec, task_ex.in_context
)
class SetWorkflowState(WorkflowCommand):
"""Instruction to change a workflow state."""
def __init__(self, wf_ex, task_spec, ctx, new_state, msg):
super(SetWorkflowState, self).__init__(wf_ex, task_spec, ctx)
self.new_state = new_state
self.msg = msg
class FailWorkflow(SetWorkflowState):
"""Instruction to fail a workflow."""
def __init__(self, wf_ex, task_spec, ctx, msg=None):
super(FailWorkflow, self).__init__(
wf_ex,
task_spec,
ctx,
states.ERROR,
msg
)
def __repr__(self):
return "Fail [workflow=%s]" % self.wf_ex.name
class SucceedWorkflow(SetWorkflowState):
"""Instruction to succeed a workflow."""
def __init__(self, wf_ex, task_spec, ctx, msg=None):
super(SucceedWorkflow, self).__init__(
wf_ex,
task_spec,
ctx,
states.SUCCESS,
msg
)
def __repr__(self):
return "Succeed [workflow=%s]" % self.wf_ex.name
class PauseWorkflow(SetWorkflowState):
"""Instruction to pause a workflow."""
def __init__(self, wf_ex, task_spec, ctx, msg=None):
super(PauseWorkflow, self).__init__(
wf_ex,
task_spec,
ctx,
states.PAUSED,
msg
)
def __repr__(self):
return "Pause [workflow=%s]" % self.wf_ex.name
RESERVED_CMDS = {
'noop': Noop,
'fail': FailWorkflow,
'succeed': SucceedWorkflow,
'pause': PauseWorkflow
}
def get_command_class(cmd_name):
return RESERVED_CMDS[cmd_name] if cmd_name in RESERVED_CMDS else None
def create_command(cmd_name, wf_ex, task_spec, ctx):
cmd_cls = get_command_class(cmd_name) or RunTask
return cmd_cls(wf_ex, task_spec, ctx)