def dispatch_workflow_commands(wf_ex, wf_cmds): # TODO(rakhmerov): I don't like these imports but otherwise we have # import cycles. from mistral.engine import task_handler from mistral.engine import workflow_handler as wf_handler if not wf_cmds: return for cmd in wf_cmds: if isinstance(cmd, (commands.RunTask, commands.RunExistingTask)): task_handler.run_task(cmd) elif isinstance(cmd, commands.SetWorkflowState): # TODO(rakhmerov): Make just a single call to workflow_handler if states.is_completed(cmd.new_state): wf_handler.stop_workflow(cmd.wf_ex, cmd.new_state, cmd.msg) else: wf_handler.set_workflow_state(wf_ex, cmd.new_state, cmd.msg) elif isinstance(cmd, commands.Noop): # Do nothing. pass else: raise exc.MistralError('Unsupported workflow command: %s' % cmd) if wf_ex.state != states.RUNNING: break
def pause_workflow(self, wf_ex_id): with db_api.transaction(): wf_ex = wf_handler.lock_workflow_execution(wf_ex_id) wf_handler.set_workflow_state(wf_ex, states.PAUSED) return wf_ex
def _continue_workflow(wf_ex, task_ex=None, reset=True, env=None): wf_ex = wf_service.update_workflow_execution_env(wf_ex, env) wf_handler.set_workflow_state( wf_ex, states.RUNNING, set_upstream=True ) wf_ctrl = wf_base.get_controller(wf_ex) # TODO(rakhmerov): Add error handling. # Calculate commands to process next. cmds = wf_ctrl.continue_workflow(task_ex=task_ex, reset=reset, env=env) # When resuming a workflow we need to ignore all 'pause' # commands because workflow controller takes tasks that # completed within the period when the workflow was paused. # TODO(rakhmerov): This all should be in workflow handler, it's too # specific for engine level. cmds = list( filter( lambda c: not isinstance(c, commands.PauseWorkflow), cmds ) ) # Since there's no explicit task causing the operation # we need to mark all not processed tasks as processed # because workflow controller takes only completed tasks # with flag 'processed' equal to False. for t_ex in wf_ex.task_executions: if states.is_completed(t_ex.state) and not t_ex.processed: t_ex.processed = True dispatcher.dispatch_workflow_commands(wf_ex, cmds) if not cmds: wf_handler.check_workflow_completion(wf_ex) return wf_ex.get_clone()
def start_workflow(self, wf_identifier, wf_input, description='', **params): with db_api.transaction(): # TODO(rakhmerov): It needs to be hidden in workflow_handler and # Workflow abstraction. # The new workflow execution will be in an IDLE # state on initial record creation. wf_ex, wf_spec = wf_ex_service.create_workflow_execution( wf_identifier, wf_input, description, params ) wf_handler.set_workflow_state(wf_ex, states.RUNNING) wf_ctrl = wf_base.get_controller(wf_ex, wf_spec) cmds = wf_ctrl.continue_workflow() dispatcher.dispatch_workflow_commands(wf_ex, cmds) return wf_ex.get_clone()
def _process_commands(wf_ex, cmds): if not cmds: return from mistral.engine import task_handler from mistral.engine import workflow_handler as wf_handler for cmd in _rearrange_commands(cmds): if states.is_completed(wf_ex.state): break if wf_ex.state == states.PAUSED: # Save all commands after 'pause' to the backlog so that # they can be processed after the workflow is resumed. _save_command_to_backlog(wf_ex, cmd) continue if isinstance(cmd, (commands.RunTask, commands.RunExistingTask)): task_handler.run_task(cmd) elif isinstance(cmd, commands.SetWorkflowState): wf_handler.set_workflow_state(wf_ex, cmd.new_state, cmd.msg) else: raise exc.MistralError('Unsupported workflow command: %s' % cmd)