Delete VNF workflow 71/13671/5
authorPatricia Reinoso <patricia.reinoso@canonical.com>
Thu, 13 Jul 2023 14:01:17 +0000 (14:01 +0000)
committerPatricia Reinoso <patricia.reinoso@canonical.com>
Tue, 25 Jul 2023 07:16:20 +0000 (07:16 +0000)
Change-Id: I1bfa161d70ed1d889f5e6e34174a926e6d17b19f
Signed-off-by: Patricia Reinoso <patricia.reinoso@canonical.com>
osm_lcm/nglcm.py
osm_lcm/temporal/vnf_workflows.py
osm_lcm/tests/test_vnf_workflows.py

index f48f381..38ec183 100644 (file)
@@ -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,
index e5957dd..80d3a5f 100644 (file)
@@ -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
index 4904429..838c3ee 100644 (file)
@@ -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()