def test_cancellation(self): # Manually create the liveaction and action execution objects without publishing. wf_meta = self.get_wf_fixture_meta_data(TEST_PACK_PATH, TEST_FIXTURES['workflows'][0]) lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta['name']) lv_ac_db, ac_ex_db = ac_svc.create_request(lv_ac_db) # Request and pre-process the workflow execution. wf_def = self.get_wf_def(TEST_PACK_PATH, wf_meta) st2_ctx = self.mock_st2_context(ac_ex_db) wf_ex_db = wf_svc.request(wf_def, ac_ex_db, st2_ctx) wf_ex_db = self.prep_wf_ex(wf_ex_db) # Manually request task executions. task_route = 0 self.run_workflow_step(wf_ex_db, 'task1', task_route) self.assert_task_running('task2', task_route) # Cancel the workflow when there is still active task(s). wf_ex_db = wf_svc.request_cancellation(ac_ex_db) conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.CANCELING) self.assertEqual(wf_ex_db.status, wf_statuses.CANCELING) # Manually complete the task and ensure workflow is canceled. self.run_workflow_step(wf_ex_db, 'task2', task_route) self.assert_task_not_started('task3', task_route) conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.CANCELED) self.assertEqual(wf_ex_db.status, wf_statuses.CANCELED)
def test_cancellation(self): # Manually create the liveaction and action execution objects without publishing. wf_meta = self.get_wf_fixture_meta_data(TEST_PACK_PATH, TEST_FIXTURES["workflows"][0]) lv_ac_db = lv_db_models.LiveActionDB(action=wf_meta["name"]) lv_ac_db, ac_ex_db = ac_svc.create_request(lv_ac_db) # Request and pre-process the workflow execution. wf_def = self.get_wf_def(TEST_PACK_PATH, wf_meta) st2_ctx = self.mock_st2_context(ac_ex_db) wf_ex_db = wf_svc.request(wf_def, ac_ex_db, st2_ctx) wf_ex_db = self.prep_wf_ex(wf_ex_db) # Manually request task executions. task_route = 0 self.run_workflow_step(wf_ex_db, "task1", task_route) self.assert_task_running("task2", task_route) # Cancel the workflow when there is still active task(s). wf_ex_db = wf_svc.request_cancellation(ac_ex_db) conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.CANCELING) self.assertEqual(wf_ex_db.status, wf_statuses.CANCELING) # Manually complete the task and ensure workflow is canceled. self.run_workflow_step(wf_ex_db, "task2", task_route) self.assert_task_not_started("task3", task_route) conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.CANCELED) self.assertEqual(wf_ex_db.status, wf_statuses.CANCELED)
def test_request_rerun_again_while_prev_rerun_is_still_running(self): # Create and return a failed workflow execution. wf_meta, lv_ac_db1, ac_ex_db1, wf_ex_db = self.prep_wf_ex_for_rerun() # Manually create the liveaction and action execution objects for the rerun. lv_ac_db2 = lv_db_models.LiveActionDB(action=wf_meta["name"]) lv_ac_db2, ac_ex_db2 = action_service.create_request(lv_ac_db2) # Request workflow execution rerun. st2_ctx = self.mock_st2_context(ac_ex_db2, ac_ex_db1.context) st2_ctx["workflow_execution_id"] = str(wf_ex_db.id) rerun_options = {"ref": str(ac_ex_db1.id), "tasks": ["task1"]} wf_ex_db = workflow_service.request_rerun(ac_ex_db2, st2_ctx, rerun_options) wf_ex_db = self.prep_wf_ex(wf_ex_db) # Check workflow status. conductor, wf_ex_db = workflow_service.refresh_conductor( str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.RUNNING) self.assertEqual(wf_ex_db.status, wf_statuses.RUNNING) # Complete task1. self.run_workflow_step(wf_ex_db, "task1", 0) # Check workflow status and make sure it is still running. conductor, wf_ex_db = workflow_service.refresh_conductor( str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.RUNNING) self.assertEqual(wf_ex_db.status, wf_statuses.RUNNING) lv_ac_db2 = lv_db_access.LiveAction.get_by_id(str(lv_ac_db2.id)) self.assertEqual(lv_ac_db2.status, action_constants.LIVEACTION_STATUS_RUNNING) ac_ex_db2 = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db2.id)) self.assertEqual(ac_ex_db2.status, action_constants.LIVEACTION_STATUS_RUNNING) # Manually create the liveaction and action execution objects for the rerun. lv_ac_db3 = lv_db_models.LiveActionDB(action=wf_meta["name"]) lv_ac_db3, ac_ex_db3 = action_service.create_request(lv_ac_db3) # Request workflow execution rerun again. st2_ctx = self.mock_st2_context(ac_ex_db3, ac_ex_db1.context) st2_ctx["workflow_execution_id"] = str(wf_ex_db.id) rerun_options = {"ref": str(ac_ex_db1.id), "tasks": ["task1"]} expected_error = ('^Unable to rerun workflow execution ".*" ' "because it is not in a completed state.$") self.assertRaisesRegexp( wf_exc.WorkflowExecutionRerunException, expected_error, workflow_service.request_rerun, ac_ex_db3, st2_ctx, rerun_options, )
def process(self, wf_ex_db): # Refresh record from the database in case the request is in the queue for too long. conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) # Continue if workflow is still active. if conductor.get_workflow_state() not in states.COMPLETED_STATES: # Set workflow to running state. conductor.set_workflow_state(states.RUNNING) # Identify the next set of tasks to execute. next_tasks = conductor.get_next_tasks() # If there is no new tasks, update execution records to handle possible completion. if not next_tasks: # Update workflow execution and related liveaction and action execution. wf_svc.update_execution_records(wf_ex_db, conductor) # If workflow execution is no longer active, then stop processing here. if wf_ex_db.status in states.COMPLETED_STATES: return # Iterate while there are next tasks identified for processing. In the case for # task with no action execution defined, the task execution will complete # immediately with a new set of tasks available. while next_tasks: # Mark the tasks as running in the task flow before actual task execution. for task in next_tasks: conductor.update_task_flow(task['id'], states.RUNNING) # Update workflow execution and related liveaction and action execution. wf_svc.update_execution_records(wf_ex_db, conductor) # If workflow execution is no longer active, then stop processing here. if wf_ex_db.status in states.COMPLETED_STATES: break # Request task execution for the tasks. for task in next_tasks: try: task_id, task_spec, task_ctx = task['id'], task[ 'spec'], task['ctx'] st2_ctx = {'execution_id': wf_ex_db.action_execution} wf_svc.request_task_execution(wf_ex_db, task_id, task_spec, task_ctx, st2_ctx) except Exception as e: wf_svc.fail_workflow_execution(str(wf_ex_db.id), e, task_id=task['id']) return # Identify the next set of tasks to execute. conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) next_tasks = conductor.get_next_tasks()
def process(self, wf_ex_db): # Refresh record from the database in case the request is in the queue for too long. conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) # Continue if workflow is still active. if conductor.get_workflow_state() not in states.COMPLETED_STATES: # Set workflow to running state. conductor.set_workflow_state(states.RUNNING) # Identify the next set of tasks to execute. next_tasks = conductor.get_next_tasks() # If there is no new tasks, update execution records to handle possible completion. if not next_tasks: # Update workflow execution and related liveaction and action execution. wf_svc.update_execution_records(wf_ex_db, conductor) # If workflow execution is no longer active, then stop processing here. if wf_ex_db.status in states.COMPLETED_STATES: return # Iterate while there are next tasks identified for processing. In the case for # task with no action execution defined, the task execution will complete # immediately with a new set of tasks available. while next_tasks: # Mark the tasks as running in the task flow before actual task execution. for task in next_tasks: conductor.update_task_flow(task['id'], states.RUNNING) # Update workflow execution and related liveaction and action execution. wf_svc.update_execution_records(wf_ex_db, conductor) # If workflow execution is no longer active, then stop processing here. if wf_ex_db.status in states.COMPLETED_STATES: break # Request task execution for the tasks. for task in next_tasks: try: task_id, task_spec, task_ctx = task['id'], task['spec'], task['ctx'] st2_ctx = {'execution_id': wf_ex_db.action_execution} wf_svc.request_task_execution(wf_ex_db, task_id, task_spec, task_ctx, st2_ctx) except Exception as e: wf_svc.fail_workflow_execution(str(wf_ex_db.id), e, task_id=task['id']) return # Identify the next set of tasks to execute. conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) next_tasks = conductor.get_next_tasks()
def prep_wf_ex_for_rerun(self): wf_meta = self.get_wf_fixture_meta_data(TEST_PACK_PATH, 'sequential.yaml') # Manually create the liveaction and action execution objects without publishing. lv_ac_db1 = lv_db_models.LiveActionDB(action=wf_meta['name']) lv_ac_db1, ac_ex_db1 = action_service.create_request(lv_ac_db1) # Request the workflow execution. wf_def = self.get_wf_def(TEST_PACK_PATH, wf_meta) st2_ctx = self.mock_st2_context(ac_ex_db1) wf_ex_db = workflow_service.request(wf_def, ac_ex_db1, st2_ctx) wf_ex_db = self.prep_wf_ex(wf_ex_db) # Fail workflow execution. self.run_workflow_step( wf_ex_db, 'task1', 0, expected_ac_ex_db_status=action_constants.LIVEACTION_STATUS_FAILED, expected_tk_ex_db_status=wf_statuses.FAILED) # Check workflow status. conductor, wf_ex_db = workflow_service.refresh_conductor( str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.FAILED) self.assertEqual(wf_ex_db.status, wf_statuses.FAILED) lv_ac_db1 = lv_db_access.LiveAction.get_by_id(str(lv_ac_db1.id)) self.assertEqual(lv_ac_db1.status, action_constants.LIVEACTION_STATUS_FAILED) ac_ex_db1 = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db1.id)) self.assertEqual(ac_ex_db1.status, action_constants.LIVEACTION_STATUS_FAILED) return wf_meta, lv_ac_db1, ac_ex_db1, wf_ex_db
def test_request_rerun_while_original_is_still_running(self): wf_meta = self.get_wf_fixture_meta_data(TEST_PACK_PATH, 'sequential.yaml') # Manually create the liveaction and action execution objects without publishing. lv_ac_db1 = lv_db_models.LiveActionDB(action=wf_meta['name']) lv_ac_db1, ac_ex_db1 = action_service.create_request(lv_ac_db1) # Request the workflow execution. wf_def = self.get_wf_def(TEST_PACK_PATH, wf_meta) st2_ctx = self.mock_st2_context(ac_ex_db1) wf_ex_db = workflow_service.request(wf_def, ac_ex_db1, st2_ctx) wf_ex_db = self.prep_wf_ex(wf_ex_db) # Check workflow status. conductor, wf_ex_db = workflow_service.refresh_conductor( str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.RUNNING) self.assertEqual(wf_ex_db.status, wf_statuses.RUNNING) # Manually create the liveaction and action execution objects for the rerun. lv_ac_db2 = lv_db_models.LiveActionDB(action=wf_meta['name']) lv_ac_db2, ac_ex_db2 = action_service.create_request(lv_ac_db2) # Request workflow execution rerun. st2_ctx = self.mock_st2_context(ac_ex_db2, ac_ex_db1.context) st2_ctx['workflow_execution_id'] = str(wf_ex_db.id) rerun_options = {'ref': str(ac_ex_db1.id), 'tasks': ['task1']} expected_error = ('^Unable to rerun workflow execution \".*\" ' 'because it is not in a completed state.$') self.assertRaisesRegexp(wf_exc.WorkflowExecutionRerunException, expected_error, workflow_service.request_rerun, ac_ex_db2, st2_ctx, rerun_options)
def test_request_rerun(self): # Create and return a failed workflow execution. wf_meta, lv_ac_db1, ac_ex_db1, wf_ex_db = self.prep_wf_ex_for_rerun() # Manually create the liveaction and action execution objects for the rerun. lv_ac_db2 = lv_db_models.LiveActionDB(action=wf_meta['name']) lv_ac_db2, ac_ex_db2 = action_service.create_request(lv_ac_db2) # Request workflow execution rerun. st2_ctx = self.mock_st2_context(ac_ex_db2, ac_ex_db1.context) st2_ctx['workflow_execution_id'] = str(wf_ex_db.id) rerun_options = {'ref': str(ac_ex_db1.id), 'tasks': ['task1']} wf_ex_db = workflow_service.request_rerun(ac_ex_db2, st2_ctx, rerun_options) wf_ex_db = self.prep_wf_ex(wf_ex_db) # Check workflow status. conductor, wf_ex_db = workflow_service.refresh_conductor( str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.RUNNING) self.assertEqual(wf_ex_db.status, wf_statuses.RUNNING) # Complete task1. self.run_workflow_step(wf_ex_db, 'task1', 0) # Check workflow status and make sure it is still running. conductor, wf_ex_db = workflow_service.refresh_conductor( str(wf_ex_db.id)) self.assertEqual(conductor.get_workflow_status(), wf_statuses.RUNNING) self.assertEqual(wf_ex_db.status, wf_statuses.RUNNING) lv_ac_db2 = lv_db_access.LiveAction.get_by_id(str(lv_ac_db2.id)) self.assertEqual(lv_ac_db2.status, action_constants.LIVEACTION_STATUS_RUNNING) ac_ex_db2 = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db2.id)) self.assertEqual(ac_ex_db2.status, action_constants.LIVEACTION_STATUS_RUNNING)
def handle_workflow_execution(self, wf_ex_db): iteration = 0 wf_ac_ex_id = wf_ex_db.action_execution LOG.info('[%s] Processing request for workflow execution.', wf_ac_ex_id) # Refresh record from the database in case the request is in the queue for too long. conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) # Continue if workflow is still active and set workflow to running state. if conductor.get_workflow_state() not in states.COMPLETED_STATES: LOG.info( '[%s] Requesting conductor to start running workflow execution.', wf_ac_ex_id) conductor.request_workflow_state(states.RUNNING) # Identify the next set of tasks to execute. msg = '[%s] Identifying next set (%s) of tasks for workflow execution in state "%s".' LOG.info(msg, wf_ac_ex_id, str(iteration), conductor.get_workflow_state()) LOG.debug('[%s] %s', wf_ac_ex_id, conductor.serialize()) next_tasks = conductor.get_next_tasks() # If there is no new tasks, update execution records to handle possible completion. if not next_tasks: # Update workflow execution and related liveaction and action execution. LOG.info('[%s] No tasks identified to execute next.', wf_ac_ex_id) wf_svc.update_execution_records(wf_ex_db, conductor) # If workflow execution is no longer active, then stop processing here. if wf_ex_db.status in states.COMPLETED_STATES: msg = '[%s] Workflow execution is in completed state "%s".' LOG.info(msg, wf_ac_ex_id, wf_ex_db.status) return # Iterate while there are next tasks identified for processing. In the case for # task with no action execution defined, the task execution will complete # immediately with a new set of tasks available. while next_tasks: msg = '[%s] Identified the following set of tasks to execute next: %s' LOG.info(msg, wf_ac_ex_id, ', '.join([task['id'] for task in next_tasks])) # Mark the tasks as running in the task flow before actual task execution. for task in next_tasks: msg = '[%s] Mark task "%s" in conductor as running.' LOG.info(msg, wf_ac_ex_id, task['id']) ac_ex_event = events.ActionExecutionEvent(states.RUNNING) conductor.update_task_flow(task['id'], ac_ex_event) # Update workflow execution and related liveaction and action execution. wf_svc.update_execution_records(wf_ex_db, conductor) # If workflow execution is no longer active, then stop processing here. if wf_ex_db.status in states.COMPLETED_STATES: msg = '[%s] Workflow execution is in completed state "%s".' LOG.info(msg, wf_ac_ex_id, wf_ex_db.status) break # Request task execution for the tasks. for task in next_tasks: try: LOG.info('[%s] Requesting execution for task "%s".', wf_ac_ex_id, task['id']) task_id, task_spec, task_ctx = task['id'], task[ 'spec'], task['ctx'] st2_ctx = {'execution_id': wf_ex_db.action_execution} wf_svc.request_task_execution(wf_ex_db, task_id, task_spec, task_ctx, st2_ctx) except Exception as e: LOG.exception('[%s] Failed task execution for "%s".', wf_ac_ex_id, task['id']) wf_svc.fail_workflow_execution(str(wf_ex_db.id), e, task_id=task['id']) return # Identify the next set of tasks to execute. iteration += 1 conductor, wf_ex_db = wf_svc.refresh_conductor(str(wf_ex_db.id)) msg = '[%s] Identifying next set (%s) of tasks for workflow execution in state "%s".' LOG.info(msg, wf_ac_ex_id, str(iteration), conductor.get_workflow_state()) LOG.debug('[%s] %s', wf_ac_ex_id, conductor.serialize()) next_tasks = conductor.get_next_tasks() if not next_tasks: LOG.info('[%s] No tasks identified to execute next.', wf_ac_ex_id)