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()
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
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
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
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
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)
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 )
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
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
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
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
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
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
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)
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)
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))
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))