def get_controller(wf_ex, wf_spec=None): """Gets a workflow controller instance by given workflow execution object. :param wf_ex: Workflow execution object. :param wf_spec: Workflow specification object. If passed, the method works faster. :returns: Workflow controller class. """ if not wf_spec: wf_spec = spec_parser.get_workflow_spec_by_execution_id(wf_ex.id) wf_type = wf_spec.get_type() ctrl_cls = None for cls in u.iter_subclasses(WorkflowController): if cls.__workflow_type__ == wf_type: ctrl_cls = cls break if not ctrl_cls: raise exc.MistralError( 'Failed to find a workflow controller [type=%s]' % wf_type) return ctrl_cls(wf_ex, wf_spec)
def _build_task_from_command(cmd): if isinstance(cmd, wf_cmds.RunExistingTask): task = _create_task(cmd.wf_ex, cmd.wf_spec, spec_parser.get_task_spec(cmd.task_ex.spec), cmd.ctx, task_ex=cmd.task_ex, unique_key=cmd.task_ex.unique_key, waiting=cmd.task_ex.state == states.WAITING, triggered_by=cmd.triggered_by) if cmd.reset: task.reset() return task if isinstance(cmd, wf_cmds.RunTask): task = _create_task(cmd.wf_ex, cmd.wf_spec, cmd.task_spec, cmd.ctx, unique_key=cmd.unique_key, waiting=cmd.is_waiting(), triggered_by=cmd.triggered_by) return task raise exc.MistralError('Unsupported workflow command: %s' % cmd)
def _run_existing(self): if self.waiting: return # Explicitly change task state to RUNNING. # Throw exception if the existing task already succeeded. if self.task_ex.state == states.SUCCESS: raise exc.MistralError( 'Rerunning succeeded tasks is not supported.') # Record the current task state. old_task_state = self.task_ex.state self.set_state(states.RUNNING, None, processed=False) # Publish event. self.notify(old_task_state, self.task_ex.state) if self.rerun: self.save_started_time() self.save_finished_time(value=None) self._before_task_start() # Policies could possibly change task state. if self.task_ex.state != states.RUNNING: return self._update_inbound_context() self._update_triggered_by() self._reset_actions() self._schedule_actions()
def __init__(self): config_file = CONF.event_engine.event_definitions_cfg_file definition_cfg = [] if os.path.exists(config_file): with open(config_file) as cf: config = cf.read() try: definition_cfg = safe_yaml.load(config) except safe_yaml.YAMLError as err: if hasattr(err, 'problem_mark'): mark = err.problem_mark errmsg = ( "Invalid YAML syntax in Definitions file " "%(file)s at line: %(line)s, column: %(column)s." % dict(file=config_file, line=mark.line + 1, column=mark.column + 1)) else: errmsg = ("YAML error reading Definitions file %s" % CONF.event_engine.event_definitions_cfg_file) LOG.error(errmsg) raise exceptions.MistralError( 'Invalid event definition configuration file. %s' % config_file) self.definitions = [ EventDefinition(event_def) for event_def in reversed(definition_cfg) ]
def _run_existing(self): if self.waiting: return # Explicitly change task state to RUNNING. # Throw exception if the existing task already succeeded. if self.task_ex.state == states.SUCCESS: raise exc.MistralError( 'Rerunning succeeded tasks is not supported.' ) self.set_state(states.RUNNING, None, processed=False) # Get the manual input data when action is rerun, otherwise # the manual output data when action is used for pass. self._task_action = getattr(self.task_ex, "_manual_action", None) self._task_input = getattr(self.task_ex, "_manual_input", None) self._task_output = getattr(self.task_ex, "_manual_output", None) # Add Kafka log trace if self._task_action: etype = kfk_etypes.TASK_REDO if self._task_action == 'pass': etype = kfk_etypes.TASK_PASS kfk_trace.log(etype, None, states.RUNNING, self.wf_ex.workflow_id, self.wf_ex.id, self.task_ex.id, self.task_ex.name, self._task_input, self._task_output, self.triggered_by) self._update_inbound_context() self._update_triggered_by() self._reset_actions() self._schedule_actions()
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 set_workflow_state(wf_ex, state, msg=None): if states.is_completed(state): stop_workflow(wf_ex, state, msg) elif states.is_paused(state): pause_workflow(wf_ex, msg) else: raise exc.MistralError('Invalid workflow state [wf_ex=%s, state=%s]' % (wf_ex, state))
def set_workflow_state(wf_ex, state, msg=None): if states.is_completed(state): stop_workflow(wf_ex, state, msg) elif states.is_paused(state): pause_workflow(wf_ex, msg) else: raise exc.MistralError( 'Invalid workflow execution state [wf_ex_id=%s, wf_name=%s, ' 'state=%s]' % (wf_ex.id, wf_ex.name, state))
def _run_existing(self): if self.waiting: return # Explicitly change task state to RUNNING. # Throw exception if the existing task already succeeded. if self.task_ex.state == states.SUCCESS: raise exc.MistralError( 'Rerunning succeeded tasks is not supported.') self.set_state(states.RUNNING, None, processed=False) self._update_inbound_context() self._reset_actions() self._schedule_actions()
def test_resume_fails(self): # Start and pause workflow. wb_service.create_workbook_v2(WORKBOOK_DIFFERENT_TASK_STATES) wf_ex = self.engine.start_workflow('wb.wf1', {}) self.await_workflow_paused(wf_ex.id) wf_ex = db_api.get_workflow_execution(wf_ex.id) self.assertEqual(states.PAUSED, wf_ex.state) # Simulate failure and check if it is handled. err = exc.MistralError('foo') with mock.patch.object(db_api, 'acquire_lock', side_effect=err): self.assertRaises(exc.MistralError, self.engine.resume_workflow, wf_ex.id)
def _build_task_from_command(cmd): if isinstance(cmd, wf_cmds.RunExistingTask): task = _create_task(cmd.wf_ex, spec_parser.get_task_spec(cmd.task_ex.spec), cmd.ctx, cmd.task_ex) if cmd.reset: task.reset() return task if isinstance(cmd, wf_cmds.RunTask): task = _create_task(cmd.wf_ex, cmd.task_spec, cmd.ctx) if cmd.is_waiting(): task.defer() return task raise exc.MistralError('Unsupported workflow command: %s' % cmd)
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)
def _raise_immutable_error(): raise exc.MistralError('Context view is immutable.')
def call(self, fn, *args, **kwargs): try: return super(MistralRetrying, self).call(fn, *args, **kwargs) except tenacity.RetryError: raise exc.MistralError("The service is temporarily unavailable")
def _validate_config(): if not CONF.yaql.convert_output_data and CONF.yaql.convert_input_data: raise exc.MistralError( "The config property 'yaql.convert_output_data' is set to False " "so 'yaql.convert_input_data' must also be set to False.")