From fc387baa70e5a81d80c083f0aa0b96c5f03852f0 Mon Sep 17 00:00:00 2001 From: Patricia Reinoso Date: Thu, 13 Jul 2023 14:01:17 +0000 Subject: [PATCH] Delete VNF workflow Change-Id: I1bfa161d70ed1d889f5e6e34174a926e6d17b19f Signed-off-by: Patricia Reinoso --- osm_lcm/nglcm.py | 2 + osm_lcm/temporal/vnf_workflows.py | 58 +++++++- osm_lcm/tests/test_vnf_workflows.py | 206 ++++++++++++++++++++++++---- 3 files changed, 239 insertions(+), 27 deletions(-) diff --git a/osm_lcm/nglcm.py b/osm_lcm/nglcm.py index f48f381..38ec183 100644 --- a/osm_lcm/nglcm.py +++ b/osm_lcm/nglcm.py @@ -72,6 +72,7 @@ from osm_lcm.temporal.vnf_activities import ( SendNotificationForVnfImpl, ) from osm_lcm.temporal.vnf_workflows import ( + VnfDeleteWorkflowImpl, VnfInstantiateWorkflowImpl, VnfPrepareWorkflowImpl, VnfTerminateWorkflowImpl, @@ -176,6 +177,7 @@ class NGLcm: VimDeleteWorkflowImpl, VimUpdateWorkflowImpl, VduInstantiateWorkflowImpl, + VnfDeleteWorkflowImpl, VnfInstantiateWorkflowImpl, VnfPrepareWorkflowImpl, VnfTerminateWorkflowImpl, diff --git a/osm_lcm/temporal/vnf_workflows.py b/osm_lcm/temporal/vnf_workflows.py index e5957dd..80d3a5f 100644 --- a/osm_lcm/temporal/vnf_workflows.py +++ b/osm_lcm/temporal/vnf_workflows.py @@ -21,6 +21,7 @@ from typing import Tuple from osm_common.temporal.states import VnfInstantiationState, VnfState from osm_common.temporal.activities.vnf import ( + DeleteVnfRecord, ChangeVnfInstantiationState, ChangeVnfState, GetTaskQueue, @@ -32,6 +33,7 @@ from osm_common.temporal.activities.vnf import ( ) from osm_common.temporal.workflows.vnf import ( + VnfDeleteWorkflow, VnfInstantiateWorkflow, VnfPrepareWorkflow, VnfTerminateWorkflow, @@ -49,9 +51,9 @@ from temporalio.common import RetryPolicy from temporalio.converter import value_to_type from temporalio.exceptions import ( ActivityError, + ApplicationError, ChildWorkflowError, FailureError, - ApplicationError, ) _SANDBOXED = False @@ -455,3 +457,57 @@ class VnfTerminateWorkflowImpl(VnfTerminateWorkflow): f"{VnfTerminateWorkflow.__name__} failed with {err_details}" ) raise e + + +@workflow.defn(name=VnfDeleteWorkflow.__name__, sandboxed=_SANDBOXED) +class VnfDeleteWorkflowImpl(VnfDeleteWorkflow): + @workflow.run + async def run(self, workflow_input: VnfDeleteWorkflow.Input) -> None: + try: + vnfr = value_to_type( + GetVnfRecord.Output, + await workflow.execute_activity( + activity=GetVnfRecord.__name__, + arg=GetVnfRecord.Input(workflow_input.vnfr_uuid), + activity_id=f"{GetVnfRecord.__name__}-{workflow_input.vnfr_uuid}", + task_queue=LCM_TASK_QUEUE, + schedule_to_close_timeout=default_schedule_to_close_timeout, + retry_policy=retry_policy, + ), + ).vnfr + instantiation_state = vnfr.get("instantiationState") + if instantiation_state != VnfInstantiationState.NOT_INSTANTIATED.name: + raise ApplicationError( + f"VNF must be in instantiation state '{VnfInstantiationState.NOT_INSTANTIATED.name}'", + non_retryable=True, + ) + + except ActivityError as e: + err_details = str(e.cause.with_traceback(e.__traceback__)) + self.logger.error(f"{VnfDeleteWorkflow.__name__} failed with {err_details}") + raise e + + except Exception as e: + err_details = str(traceback.format_exc()) + self.logger.error(f"{VnfDeleteWorkflow.__name__} failed with {err_details}") + raise e + + try: + await workflow.execute_activity( + activity=DeleteVnfRecord.__name__, + arg=DeleteVnfRecord.Input(workflow_input.vnfr_uuid), + activity_id=f"{DeleteVnfRecord.__name__}-{workflow_input.vnfr_uuid}", + task_queue=LCM_TASK_QUEUE, + schedule_to_close_timeout=default_schedule_to_close_timeout, + retry_policy=retry_policy, + ) + + except ActivityError as e: + err_details = str(e.cause.with_traceback(e.__traceback__)) + self.logger.error(f"{VnfDeleteWorkflow.__name__} failed with {err_details}") + raise e + + except Exception as e: + err_details = str(traceback.format_exc()) + self.logger.error(f"{VnfDeleteWorkflow.__name__} failed with {err_details}") + raise e diff --git a/osm_lcm/tests/test_vnf_workflows.py b/osm_lcm/tests/test_vnf_workflows.py index 4904429..838c3ee 100644 --- a/osm_lcm/tests/test_vnf_workflows.py +++ b/osm_lcm/tests/test_vnf_workflows.py @@ -26,6 +26,7 @@ from temporalio import activity, workflow from temporalio.client import WorkflowFailureError from temporalio.exceptions import ( ActivityError, + ApplicationError, ChildWorkflowError, RetryState, ) @@ -36,6 +37,7 @@ from typing import Any from osm_common.temporal.activities.vnf import ( ChangeVnfInstantiationState, ChangeVnfState, + DeleteVnfRecord, GetTaskQueue, GetVimCloud, GetVnfDescriptor, @@ -61,6 +63,7 @@ from osm_common.temporal.workflows.vnf import ( VnfTerminateWorkflow, ) from osm_lcm.temporal.vnf_workflows import ( + VnfDeleteWorkflowImpl, VnfInstantiateWorkflowImpl, VnfPrepareWorkflowImpl, VnfTerminateWorkflowImpl, @@ -107,11 +110,6 @@ config = [ {"key": "track", "value": "latest"}, {"key": "channel", "value": "stable"}, ] -wf_input = VnfInstantiateWorkflowImpl.Input( - vnfr_uuid=vnfr_uuid, - model_name=model_name, - instantiation_config={}, -) vdu = { "id": vdu_id, "name": "hackfest_basic-VM", @@ -269,6 +267,24 @@ class MockGetVnfRecord(MockActivity): self.tracker.return_value = GetVnfRecord.Output(vnfr=sample_vnfr) +@activity.defn(name=GetVnfRecord.__name__) +async def mock_get_vnf_record_not_instantiated_state( + get_vnf_record_input: GetVnfRecord.Input, +) -> GetVnfRecord.Output: + vnfr = deepcopy(sample_vnfr) + vnfr["instantiationState"] = VnfInstantiationState.NOT_INSTANTIATED.name + return GetVnfRecord.Output(vnfr=vnfr) + + +@activity.defn(name=GetVnfRecord.__name__) +async def mock_get_vnf_record_instantiated_state( + get_vnf_record_input: GetVnfRecord.Input, +) -> GetVnfRecord.Output: + vnfr = deepcopy(sample_vnfr) + vnfr["instantiationState"] = VnfInstantiationState.INSTANTIATED.name + return GetVnfRecord.Output(vnfr=vnfr) + + @activity.defn(name=GetVnfRecord.__name__) async def mock_get_vnf_record_raise_exception( get_vnf_record_input: GetVnfRecord.Input, @@ -363,6 +379,9 @@ class TestVnfPrepareWorkflow(asynctest.TestCase): self.env = await WorkflowEnvironment.start_time_skipping() self.client = self.env.client self.task_queue = LCM_TASK_QUEUE + self.wf_input = VnfPrepareWorkflowImpl.Input( + vnfr_uuid=vnfr_uuid, model_name=model_name + ) async def test_vnf_prepare_workflow__successful(self): async with self.env: @@ -375,8 +394,8 @@ class TestVnfPrepareWorkflow(asynctest.TestCase): ): await self.client.execute_workflow( VnfPrepareWorkflowImpl.run, - arg=wf_input, - id=f"{VnfPrepareWorkflow.__name__}-{wf_input.vnfr_uuid}", + arg=self.wf_input, + id=f"{VnfPrepareWorkflow.__name__}-{self.wf_input.vnfr_uuid}", task_queue=self.task_queue, task_timeout=TASK_TIMEOUT, execution_timeout=EXECUTION_TIMEOUT, @@ -396,8 +415,8 @@ class TestVnfPrepareWorkflow(asynctest.TestCase): with self.assertRaises(WorkflowFailureError) as err: await self.client.execute_workflow( VnfPrepareWorkflowImpl.run, - arg=wf_input, - id=f"{VnfPrepareWorkflow.__name__}-{wf_input.vnfr_uuid}", + arg=self.wf_input, + id=f"{VnfPrepareWorkflow.__name__}-{self.wf_input.vnfr_uuid}", task_queue=self.task_queue, task_timeout=TASK_TIMEOUT, execution_timeout=EXECUTION_TIMEOUT, @@ -418,8 +437,8 @@ class TestVnfPrepareWorkflow(asynctest.TestCase): with self.assertRaises(WorkflowFailureError) as err: await self.client.execute_workflow( VnfPrepareWorkflowImpl.run, - arg=wf_input, - id=f"{VnfPrepareWorkflow.__name__}-{wf_input.vnfr_uuid}", + arg=self.wf_input, + id=f"{VnfPrepareWorkflow.__name__}-{self.wf_input.vnfr_uuid}", task_queue=self.task_queue, task_timeout=TASK_TIMEOUT, execution_timeout=EXECUTION_TIMEOUT, @@ -443,6 +462,11 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): MockVduInstantiateWorkflow, ] self.task_queue = LCM_TASK_QUEUE + self.wf_input = VnfInstantiateWorkflowImpl.Input( + vnfr_uuid=vnfr_uuid, + model_name=model_name, + instantiation_config={}, + ) def get_worker(self, task_queue: str, workflows: list, activities: list) -> Worker: return Worker( @@ -453,7 +477,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): debug_mode=DEBUG_MODE, ) - async def execute_workflow(self, wf_input=wf_input): + async def execute_workflow(self, wf_input): return await self.client.execute_workflow( VnfInstantiateWorkflowImpl.run, arg=wf_input, @@ -539,7 +563,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): async with self.env, self.get_worker( self.task_queue, workflows, activities ), self.get_worker(self.task_queue, workflows, activities): - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.validate_vnf_instantiation_state_is_updated( VnfInstantiationState.INSTANTIATED ) @@ -572,7 +596,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): async with self.env, self.get_worker( self.task_queue, workflows, activities ), self.get_worker(self.task_queue, workflows, activities): - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.validate_vnf_instantiation_state_is_updated(VnfState.STARTED) @patch( @@ -607,7 +631,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): sample_vdu_instantiate_wf_id_1, ) async with self.env, self.get_worker(self.task_queue, workflows, activities): - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(mock_execute_child_workflow.called) self.assertEquals(mock_execute_child_workflow.call_count, 2) # Check that PrepareVnfWorkflow is executed @@ -662,7 +686,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): for _ in range(num_child_workflows) ] async with self.env, self.get_worker(self.task_queue, workflows, activities): - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertEquals( mock_execute_child_workflow.call_count, num_child_workflows + 1 @@ -692,7 +716,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): self.task_queue, self.workflows, activities ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause, ActivityError)) async def test_vnf_instantiate_workflow__activity_change_vnf_instantiation_state_failed__vnf_state_not_updated( @@ -712,7 +736,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): self.task_queue, self.workflows, activities ): with self.assertRaises(WorkflowFailureError): - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.mock_change_vnf_state_tracker.assert_not_called() async def test_vnf_instantiate_workflow__change_vnf_state_failed__raise_activity_error( @@ -732,7 +756,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): self.task_queue, self.workflows, activities ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause, ActivityError)) async def test_vnf_instantiate_workflow__get_task_queue_failed__raises_activity_error( @@ -752,7 +776,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): self.task_queue, self.workflows, activities ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause, ActivityError)) async def test_vnf_instantiate_workflow__wf_vnf_prepare_failed__raises_child_wf_error( @@ -778,7 +802,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): activities, ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause.cause, ChildWorkflowError)) async def test_vnf_instantiate_workflow__wf_vdu_instantiate_failed__raises_child_wf_error( @@ -804,7 +828,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): activities, ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause, ChildWorkflowError)) async def test_vnf_instantiate_workflow__get_vnf_descriptor_failed__raises_activity_error( @@ -824,7 +848,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): self.task_queue, self.workflows, activities ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause, ActivityError)) async def test_vnf_instantiate_workflow__get_vnf_record_failed__raises_activity_error( @@ -844,7 +868,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): self.task_queue, self.workflows, activities ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause, ActivityError)) async def test_vnf_instantiate_workflow__get_vim_cloud_failed__raises_activity_error( @@ -864,7 +888,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): self.task_queue, self.workflows, activities ): with self.assertRaises(WorkflowFailureError) as err: - await self.execute_workflow() + await self.execute_workflow(self.wf_input) self.assertTrue(isinstance(err.exception.cause, ActivityError)) @patch("osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflowImpl.instantiate_vdus") @@ -885,7 +909,7 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase): async with self.env, self.get_worker( self.task_queue, workflows, activities ), self.get_worker(juju_task_queue, workflows, activities): - await self.execute_workflow() + await self.execute_workflow(self.wf_input) mock_instantiate_vdus.assert_called_once_with( vnfr=sample_vnfr, vnfd=sample_vnfd, @@ -1520,5 +1544,135 @@ class TestVnfTerminateWorkflow(asynctest.TestCase): ) +class TestVnfDeleteWorkflow(asynctest.TestCase): + async def setUp(self): + self.env = await WorkflowEnvironment.start_time_skipping() + self.client = self.env.client + self.task_queue = LCM_TASK_QUEUE + self.wf_input = VnfDeleteWorkflowImpl.Input(vnfr_uuid=vnfr_uuid) + self.mock_delete_vnf_record_tracker = Mock() + + def assert_vnf_record_is_called(self): + self.assertEqual( + self.mock_delete_vnf_record_tracker.call_args_list[0].args[0], + DeleteVnfRecord.Input(vnfr_uuid=vnfr_uuid), + ) + + @activity.defn(name=DeleteVnfRecord.__name__) + async def mock_delete_vnf_record( + self, + delete_vnf_record_input: DeleteVnfRecord.Input, + ) -> None: + self.mock_delete_vnf_record_tracker(delete_vnf_record_input) + + @activity.defn(name=DeleteVnfRecord.__name__) + async def mock_delete_vnf_record_raises_exception( + self, + delete_vnf_record_input: DeleteVnfRecord.Input, + ) -> None: + self.mock_delete_vnf_record_tracker(delete_vnf_record_input) + raise TestException(f"{DeleteVnfRecord.__name__} failed.") + + async def test_vnf_delete_workflow__successful(self): + async with self.env: + async with Worker( + self.client, + debug_mode=DEBUG_MODE, + task_queue=self.task_queue, + workflows=[VnfDeleteWorkflowImpl], + activities=[ + mock_get_vnf_record_not_instantiated_state, + self.mock_delete_vnf_record, + ], + ): + await self.client.execute_workflow( + VnfDeleteWorkflowImpl.run, + arg=self.wf_input, + id=f"{VnfDeleteWorkflowImpl.__name__}-{self.wf_input.vnfr_uuid}", + task_queue=self.task_queue, + task_timeout=TASK_TIMEOUT, + execution_timeout=EXECUTION_TIMEOUT, + ) + self.assert_vnf_record_is_called() + + async def test_vnf_delete_workflow__instantiated_state__fails(self): + async with self.env: + async with Worker( + self.client, + debug_mode=DEBUG_MODE, + task_queue=self.task_queue, + workflows=[VnfDeleteWorkflowImpl], + activities=[mock_get_vnf_record_instantiated_state], + ): + with self.assertRaises(WorkflowFailureError) as err: + await self.client.execute_workflow( + VnfDeleteWorkflowImpl.run, + arg=self.wf_input, + id=f"{VnfDeleteWorkflowImpl.__name__}-{self.wf_input.vnfr_uuid}", + task_queue=self.task_queue, + task_timeout=TASK_TIMEOUT, + execution_timeout=EXECUTION_TIMEOUT, + ) + self.assertIsInstance(err.exception.cause, ApplicationError) + self.assertEqual( + str(err.exception.cause), + "VNF must be in instantiation state 'NOT_INSTANTIATED'", + ) + self.mock_delete_vnf_record_tracker.assert_not_called() + + async def test_vnf_delete_workflow__get_vnf_record_fails__workflow_fails(self): + async with self.env: + async with Worker( + self.client, + debug_mode=DEBUG_MODE, + task_queue=self.task_queue, + workflows=[VnfDeleteWorkflowImpl], + activities=[mock_get_vnf_record_raise_exception], + ): + with self.assertRaises(WorkflowFailureError) as err: + await self.client.execute_workflow( + VnfDeleteWorkflowImpl.run, + arg=self.wf_input, + id=f"{VnfDeleteWorkflowImpl.__name__}-{self.wf_input.vnfr_uuid}", + task_queue=self.task_queue, + task_timeout=TASK_TIMEOUT, + execution_timeout=EXECUTION_TIMEOUT, + ) + self.assertIsInstance(err.exception.cause, ActivityError) + self.assertEqual( + str(err.exception.cause.cause), + f"TestException: {GetVnfRecord.__name__} failed.", + ) + self.mock_delete_vnf_record_tracker.assert_not_called() + + async def test_vnf_delete_workflow__delete_vnf_record_fails__workflow_fails(self): + async with self.env: + async with Worker( + self.client, + debug_mode=DEBUG_MODE, + task_queue=self.task_queue, + workflows=[VnfDeleteWorkflowImpl], + activities=[ + mock_get_vnf_record_not_instantiated_state, + self.mock_delete_vnf_record_raises_exception, + ], + ): + with self.assertRaises(WorkflowFailureError) as err: + await self.client.execute_workflow( + VnfDeleteWorkflowImpl.run, + arg=self.wf_input, + id=f"{VnfDeleteWorkflowImpl.__name__}-{self.wf_input.vnfr_uuid}", + task_queue=self.task_queue, + task_timeout=TASK_TIMEOUT, + execution_timeout=EXECUTION_TIMEOUT, + ) + self.assertIsInstance(err.exception.cause, ActivityError) + self.assertEqual( + str(err.exception.cause.cause), + f"TestException: {DeleteVnfRecord.__name__} failed.", + ) + self.assert_vnf_record_is_called() + + if __name__ == "__main__": asynctest.main() -- 2.25.1