def test_do_not_retry_on_exceptions(self): instance = wf_db_models.WorkflowExecutionDB() exc = db_exc.StackStormDBObjectConflictError('foobar', '1234', instance) self.assertFalse(wf_exc.retry_on_exceptions(exc)) self.assertFalse(wf_exc.retry_on_exceptions(NotImplementedError())) self.assertFalse(wf_exc.retry_on_exceptions(Exception()))
def request(wf_def, ac_ex_db, st2_ctx): wf_ac_ex_id = str(ac_ex_db.id) LOG.info('[%s] Processing action execution request for workflow.', wf_ac_ex_id) # Load workflow definition into workflow spec model. spec_module = specs_loader.get_spec_module('native') wf_spec = spec_module.instantiate(wf_def) # Inspect the workflow spec. inspect(wf_spec, st2_ctx, raise_exception=True) # Identify the action to execute. action_db = action_utils.get_action_by_ref(ref=ac_ex_db.action['ref']) if not action_db: error = 'Unable to find action "%s".' % ac_ex_db.action['ref'] raise ac_exc.InvalidActionReferencedException(error) # Identify the runner for the action. runner_type_db = action_utils.get_runnertype_by_name( action_db.runner_type['name']) # Render action execution parameters. runner_params, action_params = param_utils.render_final_params( runner_type_db.runner_parameters, action_db.parameters, ac_ex_db.parameters, ac_ex_db.context) # Instantiate the workflow conductor. conductor_params = {'inputs': action_params, 'context': st2_ctx} conductor = conducting.WorkflowConductor(wf_spec, **conductor_params) # Set the initial workflow state to requested. conductor.request_workflow_state(states.REQUESTED) # Serialize the conductor which initializes some internal values. data = conductor.serialize() # Create a record for workflow execution. wf_ex_db = wf_db_models.WorkflowExecutionDB(action_execution=str( ac_ex_db.id), spec=data['spec'], graph=data['graph'], flow=data['flow'], context=data['context'], input=data['input'], output=data['output'], errors=data['errors'], status=data['state']) # Insert new record into the database and publish to the message bus. wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=True) LOG.info('[%s] Workflow execution "%s" created.', wf_ac_ex_id, str(wf_ex_db.id)) return wf_ex_db
def test_workflow_execution_crud(self): initial = wf_db_models.WorkflowExecutionDB() initial.action_execution = uuid.uuid4().hex initial.graph = {"var1": "foobar"} initial.status = "requested" # Test create created = wf_db_access.WorkflowExecution.add_or_update(initial) self.assertEqual(initial.rev, 1) doc_id = created.id # Test read retrieved = wf_db_access.WorkflowExecution.get_by_id(doc_id) self.assertEqual(created.action_execution, retrieved.action_execution) self.assertDictEqual(created.graph, retrieved.graph) self.assertEqual(created.status, retrieved.status) # Test update graph = {"var1": "fubar"} status = "running" retrieved = wf_db_access.WorkflowExecution.update( retrieved, graph=graph, status=status ) updated = wf_db_access.WorkflowExecution.get_by_id(doc_id) self.assertNotEqual(created.rev, updated.rev) self.assertEqual(retrieved.rev, updated.rev) self.assertEqual(retrieved.action_execution, updated.action_execution) self.assertDictEqual(retrieved.graph, updated.graph) self.assertEqual(retrieved.status, updated.status) # Test add or update retrieved.graph = {"var2": "fubar"} retrieved = wf_db_access.WorkflowExecution.add_or_update(retrieved) updated = wf_db_access.WorkflowExecution.get_by_id(doc_id) self.assertNotEqual(created.rev, updated.rev) self.assertEqual(retrieved.rev, updated.rev) self.assertEqual(retrieved.action_execution, updated.action_execution) self.assertDictEqual(retrieved.graph, updated.graph) self.assertEqual(retrieved.status, updated.status) # Test delete created.delete() self.assertRaises( db_exc.StackStormDBObjectNotFoundError, wf_db_access.WorkflowExecution.get_by_id, doc_id, )
def test_workflow_execution_write_conflict(self): initial = wf_db_models.WorkflowExecutionDB() initial.action_execution = uuid.uuid4().hex initial.graph = {"var1": "foobar"} initial.status = "requested" # Prep record created = wf_db_access.WorkflowExecution.add_or_update(initial) self.assertEqual(initial.rev, 1) doc_id = created.id # Get two separate instances of the document. retrieved1 = wf_db_access.WorkflowExecution.get_by_id(doc_id) retrieved2 = wf_db_access.WorkflowExecution.get_by_id(doc_id) # Test update on instance 1, expect success graph = {"var1": "fubar"} status = "running" retrieved1 = wf_db_access.WorkflowExecution.update( retrieved1, graph=graph, status=status ) updated = wf_db_access.WorkflowExecution.get_by_id(doc_id) self.assertNotEqual(created.rev, updated.rev) self.assertEqual(retrieved1.rev, updated.rev) self.assertEqual(retrieved1.action_execution, updated.action_execution) self.assertDictEqual(retrieved1.graph, updated.graph) self.assertEqual(retrieved1.status, updated.status) # Test update on instance 2, expect race error self.assertRaises( db_exc.StackStormDBObjectWriteConflictError, wf_db_access.WorkflowExecution.update, retrieved2, graph={"var2": "fubar"}, ) # Test delete created.delete() self.assertRaises( db_exc.StackStormDBObjectNotFoundError, wf_db_access.WorkflowExecution.get_by_id, doc_id, )
def request(wf_def, ac_ex_db, st2_ctx, notify_cfg=None): wf_ac_ex_id = str(ac_ex_db.id) LOG.info('[%s] Processing action execution request for workflow.', wf_ac_ex_id) # Load workflow definition into workflow spec model. spec_module = specs_loader.get_spec_module('native') wf_spec = spec_module.instantiate(wf_def) # Inspect the workflow spec. inspect(wf_spec, st2_ctx, raise_exception=True) # Identify the action to execute. action_db = action_utils.get_action_by_ref(ref=ac_ex_db.action['ref']) if not action_db: error = 'Unable to find action "%s".' % ac_ex_db.action['ref'] raise ac_exc.InvalidActionReferencedException(error) # Identify the runner for the action. runner_type_db = action_utils.get_runnertype_by_name( action_db.runner_type['name']) # Render action execution parameters. runner_params, action_params = param_utils.render_final_params( runner_type_db.runner_parameters, action_db.parameters, ac_ex_db.parameters, ac_ex_db.context) # Instantiate the workflow conductor. conductor_params = {'inputs': action_params, 'context': st2_ctx} conductor = conducting.WorkflowConductor(wf_spec, **conductor_params) # Serialize the conductor which initializes some internal values. data = conductor.serialize() # Create a record for workflow execution. wf_ex_db = wf_db_models.WorkflowExecutionDB(action_execution=str( ac_ex_db.id), spec=data['spec'], graph=data['graph'], input=data['input'], context=data['context'], state=data['state'], status=data['state']['status'], output=data['output'], errors=data['errors']) # Inspect that the list of tasks in the notify parameter exist in the workflow spec. if runner_params.get('notify'): invalid_tasks = list( set(runner_params.get('notify')) - set(wf_spec.tasks.keys())) if invalid_tasks: raise wf_exc.WorkflowExecutionException( 'The following tasks in the notify parameter do not exist ' 'in the workflow definition: %s.' % ', '.join(invalid_tasks)) # Write notify instruction to record. if notify_cfg: # Set up the notify instruction in the workflow execution record. wf_ex_db.notify = { 'config': notify_cfg, 'tasks': runner_params.get('notify') } # Insert new record into the database and do not publish to the message bus yet. wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=False) LOG.info('[%s] Workflow execution "%s" is created.', wf_ac_ex_id, str(wf_ex_db.id)) # Update the context with the workflow execution id created on database insert. # Publish the workflow execution requested status to the message bus. if wf_ex_db.status not in statuses.COMPLETED_STATUSES: # Set the initial workflow status to requested. conductor.request_workflow_status(statuses.REQUESTED) data = conductor.serialize() wf_ex_db.state = data['state'] wf_ex_db.status = data['state']['status'] # Put the ID of the workflow execution record in the context. wf_ex_db.context['st2']['workflow_execution_id'] = str(wf_ex_db.id) wf_ex_db.state['contexts'][0]['st2']['workflow_execution_id'] = str( wf_ex_db.id) # Update the workflow execution record. wf_ex_db = wf_db_access.WorkflowExecution.update(wf_ex_db, publish=False) wf_db_access.WorkflowExecution.publish_status(wf_ex_db) msg = '[%s] Workflow execution "%s" is published.' LOG.info(msg, wf_ac_ex_id, str(wf_ex_db.id)) else: msg = '[%s] Unable to request workflow execution. It is already in completed status "%s".' LOG.info(msg, wf_ac_ex_id, wf_ex_db.status) return wf_ex_db
def test_retry_on_transient_db_errors(self): instance = wf_db_models.WorkflowExecutionDB() exc = db_exc.StackStormDBObjectWriteConflictError(instance) self.assertTrue(wf_exc.retry_on_transient_db_errors(exc))
def mock_workflow_records(self, completed=False, expired=True, log=True): status = (ac_const.LIVEACTION_STATUS_SUCCEEDED if completed else ac_const.LIVEACTION_STATUS_RUNNING) # Identify start and end timestamp gc_max_idle = cfg.CONF.workflow_engine.gc_max_idle_sec utc_now_dt = date_utils.get_datetime_utc_now() expiry_dt = utc_now_dt - datetime.timedelta(seconds=gc_max_idle + 30) start_timestamp = expiry_dt if expired else utc_now_dt end_timestamp = utc_now_dt if completed else None # Assign metadata. action_ref = "orquesta_tests.sequential" runner = "orquesta" user = "******" # Create the WorkflowExecutionDB record first since the ID needs to be # included in the LiveActionDB and ActionExecutionDB records. st2_ctx = { "st2": { "action_execution_id": "123", "action": "foobar", "runner": "orquesta", } } wf_ex_db = wf_db_models.WorkflowExecutionDB( context=st2_ctx, status=status, start_timestamp=start_timestamp, end_timestamp=end_timestamp, ) wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=False) # Create the LiveActionDB record. lv_ac_db = lv_db_models.LiveActionDB( workflow_execution=str(wf_ex_db.id), action=action_ref, action_is_workflow=True, context={ "user": user, "workflow_execution": str(wf_ex_db.id) }, status=status, start_timestamp=start_timestamp, end_timestamp=end_timestamp, ) lv_ac_db = lv_db_access.LiveAction.insert(lv_ac_db, publish=False) # Create the ActionExecutionDB record. ac_ex_db = ex_db_models.ActionExecutionDB( workflow_execution=str(wf_ex_db.id), action={ "runner_type": runner, "ref": action_ref }, runner={"name": runner}, liveaction={"id": str(lv_ac_db.id)}, context={ "user": user, "workflow_execution": str(wf_ex_db.id) }, status=status, start_timestamp=start_timestamp, end_timestamp=end_timestamp, ) if log: ac_ex_db.log = [{ "status": "running", "timestamp": start_timestamp }] if log and status in ac_const.LIVEACTION_COMPLETED_STATES: ac_ex_db.log.append({"status": status, "timestamp": end_timestamp}) ac_ex_db = ex_db_access.ActionExecution.insert(ac_ex_db, publish=False) # Update the WorkflowExecutionDB record with cross reference to the ActionExecutionDB. wf_ex_db.action_execution = str(ac_ex_db.id) wf_ex_db = wf_db_access.WorkflowExecution.update(wf_ex_db, publish=False) return wf_ex_db, lv_ac_db, ac_ex_db
def mock_workflow_records(self, completed=False, expired=True, log=True): status = (ac_const.LIVEACTION_STATUS_SUCCEEDED if completed else ac_const.LIVEACTION_STATUS_RUNNING) # Identify start and end timestamp gc_max_idle = cfg.CONF.workflow_engine.gc_max_idle_sec utc_now_dt = date_utils.get_datetime_utc_now() expiry_dt = utc_now_dt - datetime.timedelta(seconds=gc_max_idle + 30) start_timestamp = expiry_dt if expired else utc_now_dt end_timestamp = utc_now_dt if completed else None # Assign metadata. action_ref = 'orquesta_tests.sequential' runner = 'orquesta' user = '******' # Create the WorkflowExecutionDB record first since the ID needs to be # included in the LiveActionDB and ActionExecutionDB records. st2_ctx = { 'st2': { 'action_execution_id': '123', 'action': 'foobar', 'runner': 'orquesta' } } wf_ex_db = wf_db_models.WorkflowExecutionDB( context=st2_ctx, status=status, start_timestamp=start_timestamp, end_timestamp=end_timestamp) wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=False) # Create the LiveActionDB record. lv_ac_db = lv_db_models.LiveActionDB(workflow_execution=str( wf_ex_db.id), action=action_ref, action_is_workflow=True, context={ 'user': user, 'workflow_execution': str(wf_ex_db.id) }, status=status, start_timestamp=start_timestamp, end_timestamp=end_timestamp) lv_ac_db = lv_db_access.LiveAction.insert(lv_ac_db, publish=False) # Create the ActionExecutionDB record. ac_ex_db = ex_db_models.ActionExecutionDB( workflow_execution=str(wf_ex_db.id), action={ 'runner_type': runner, 'ref': action_ref }, runner={'name': runner}, liveaction={'id': str(lv_ac_db.id)}, context={ 'user': user, 'workflow_execution': str(wf_ex_db.id) }, status=status, start_timestamp=start_timestamp, end_timestamp=end_timestamp) if log: ac_ex_db.log = [{ 'status': 'running', 'timestamp': start_timestamp }] if log and status in ac_const.LIVEACTION_COMPLETED_STATES: ac_ex_db.log.append({'status': status, 'timestamp': end_timestamp}) ac_ex_db = ex_db_access.ActionExecution.insert(ac_ex_db, publish=False) # Update the WorkflowExecutionDB record with cross reference to the ActionExecutionDB. wf_ex_db.action_execution = str(ac_ex_db.id) wf_ex_db = wf_db_access.WorkflowExecution.update(wf_ex_db, publish=False) return wf_ex_db, lv_ac_db, ac_ex_db