OSMENG-987: Implement PrepareVnfWorkflow 06/13206/5
authorDario Faccin <dario.faccin@canonical.com>
Mon, 17 Apr 2023 14:56:37 +0000 (16:56 +0200)
committerbeierlm <mark.beierl@canonical.com>
Tue, 18 Apr 2023 17:04:09 +0000 (19:04 +0200)
Change-Id: Icf3ccfd44160fbc3f2f2d9ec64e360c07fc9bbfa
Signed-off-by: Dario Faccin <dario.faccin@canonical.com>
osm_lcm/nglcm.py
osm_lcm/temporal/ns_activities.py
osm_lcm/temporal/vnf_activities.py
osm_lcm/temporal/vnf_workflows.py
osm_lcm/tests/test_vnf_activities.py [new file with mode: 0644]
osm_lcm/tests/test_vnf_workflows.py [new file with mode: 0644]

index 06f803e..c62719f 100644 (file)
@@ -40,7 +40,7 @@ from osm_lcm.temporal.vim_workflows import (
     VimUpdateWorkflow,
 )
 from osm_lcm.temporal.vdu_workflows import VduInstantiateWorkflow
-from osm_lcm.temporal.vnf_workflows import VnfInstantiateWorkflow
+from osm_lcm.temporal.vnf_workflows import VnfInstantiateWorkflow, VnfPrepareWorkflow
 from osm_lcm.temporal.vnf_activities import (
     VnfDbActivity,
     VnfOperations,
@@ -156,10 +156,10 @@ class NGLcm:
             VimUpdateWorkflow,
             VduInstantiateWorkflow,
             VnfInstantiateWorkflow,
+            VnfPrepareWorkflow,
         ]
         activities = [
             ns_data_activity_instance.get_model_info,
-            ns_data_activity_instance.prepare_vnf_records,
             ns_data_activity_instance.update_ns_state,
             ns_operation_instance.check_ns_instantiate_finished,
             ns_operation_instance.deploy_ns,
@@ -176,6 +176,7 @@ class NGLcm:
             vnf_data_activity_instance.change_vnf_state,
             vnf_data_activity_instance.change_vnf_instantiation_state,
             vnf_send_notifications_instance.send_notification_for_vnf,
+            vnf_data_activity_instance.set_vnf_model,
         ]
 
         # Check if we are running under a debugger
index 6834eec..eee4325 100644 (file)
@@ -21,7 +21,6 @@ from osm_common.temporal_constants import (
     ACTIVITY_CHECK_NS_INSTANTIATION_FINISHED,
     ACTIVITY_DEPLOY_NS,
     ACTIVITY_GET_MODEL_INFO,
-    ACTIVITY_PREPARE_VNF_RECORDS,
     ACTIVITY_UPDATE_NS_STATE,
 )
 from osm_common.dataclasses.temporal_dataclasses import (
@@ -84,30 +83,6 @@ class NsDbActivity:
         self.db = db
         self.logger = logging.getLogger(f"lcm.act.{self.__class__.__name__}")
 
-    @activity.defn(name=ACTIVITY_PREPARE_VNF_RECORDS)
-    async def prepare_vnf_records(
-        self, ns_instantiate_input: NsInstantiateInput
-    ) -> None:
-        """Prepare VNFs to be deployed: Add namespace to the VNFr.
-
-        Collaborators:
-            DB Write:           vnfrs
-
-        Raises  (Retryable):
-            DbException         If the target DB record does not exist or DB is not reachable.
-
-        Activity Lifecycle:
-            This activity will not report a heartbeat due to its
-            short-running nature.
-
-            As this is a direct DB update, it is not recommended to have
-            any specific retry policy
-
-        """
-        vnfrs = self.db.get_list("vnfrs", {"nsr-id-ref": ns_instantiate_input.ns_uuid})
-        for vnfr in vnfrs:
-            self._prepare_vnf_record(vnfr)
-
     @activity.defn(name=ACTIVITY_GET_MODEL_INFO)
     async def get_model_info(
         self, ns_instantiate_input: NsInstantiateInput
@@ -134,18 +109,11 @@ class NsDbActivity:
         model_name = self._get_namespace(ns_uuid, vim_uuid)
         return ModelInfo(vim_uuid, model_name)
 
-    def _get_namespace(self, ns_id: str, vim_id: str) -> str:
+    @staticmethod
+    def _get_namespace(ns_id: str, vim_id: str) -> str:
         """The NS namespace is the combination if the NS ID and the VIM ID."""
         return ns_id[-12:] + "-" + vim_id[-12:]
 
-    def _prepare_vnf_record(self, vnfr: dict) -> None:
-        """Add namespace to the VNFr."""
-        ns_id = vnfr["nsr-id-ref"]
-        vim_id = vnfr["vim-account-id"]
-        namespace = self._get_namespace(ns_id, vim_id)
-        update_namespace = {"namespace": namespace}
-        self.db.set_one("vnfrs", {"_id": vnfr["_id"]}, update_namespace)
-
     @activity.defn(name=ACTIVITY_UPDATE_NS_STATE)
     async def update_ns_state(self, data: UpdateNsStateInput) -> None:
         """
index 3e2155b..6e5abcb 100644 (file)
@@ -22,12 +22,14 @@ from osm_common.temporal_constants import (
     ACTIVITY_SEND_NOTIFICATION_FOR_VNF,
     ACTIVITY_GET_TASK_QUEUE,
     VIM_TYPE_TASK_QUEUE_MAPPINGS,
+    ACTIVITY_SET_VNF_MODEL,
 )
 from osm_common.dataclasses.temporal_dataclasses import (
     ChangeVnfInstantiationStateInput,
     ChangeVnfStateInput,
     GetTaskQueueInput,
     GetTaskQueueOutput,
+    VnfInstantiateInput,
 )
 
 
@@ -143,6 +145,36 @@ class VnfDbActivity:
             f"VNF {vnf_instantiation_state_input.vnfr_uuid} state is updated to {vnf_instantiation_state_input.state}."
         )
 
+    @activity.defn(name=ACTIVITY_SET_VNF_MODEL)
+    async def set_vnf_model(self, set_vnf_model_input: VnfInstantiateInput) -> None:
+        """Updates the model name of VNF in VNFR.
+
+        Collaborators:
+            DB Access Object
+
+        Raises (retryable):
+            DbException: If DB access or update fails, the collection or DB record ID does not exist.
+
+        Activity Lifecycle:
+            This activity should complete relatively quickly (less than a
+            second). However, it would be reasonable to wait up to 10
+            seconds.
+
+            This activity will not report a heartbeat due to its
+            short-running nature.
+
+            It is not necessary to implement a back-off strategy for this
+            activity, the operation is idempotent.
+
+        """
+        update_namespace = {"namespace": set_vnf_model_input.model_name}
+        self.db.set_one(
+            "vnfrs", {"_id": set_vnf_model_input.vnfr_uuid}, update_namespace
+        )
+        self.logger.debug(
+            f"VNF {set_vnf_model_input.vnfr_uuid} model name is updated to {set_vnf_model_input.model_name}."
+        )
+
 
 class VnfSendNotifications:
     """Perform Notification operations."""
index 4521889..6d16d26 100644 (file)
@@ -27,7 +27,6 @@ from osm_common.dataclasses.temporal_dataclasses import (
     ChangeVnfInstantiationStateInput,
     ChangeVnfStateInput,
     GetTaskQueueInput,
-    PrepareVnfInput,
 )
 
 from osm_common.temporal_constants import (
@@ -35,6 +34,7 @@ from osm_common.temporal_constants import (
     ACTIVITY_SEND_NOTIFICATION_FOR_VNF,
     ACTIVITY_CHANGE_VNF_STATE,
     ACTIVITY_GET_TASK_QUEUE,
+    ACTIVITY_SET_VNF_MODEL,
     LCM_TASK_QUEUE,
     WORKFLOW_VDU_INSTANTIATE,
     WORKFLOW_VNF_INSTANTIATE,
@@ -82,7 +82,7 @@ class VnfInstantiateWorkflow:
 
             await workflow.execute_child_workflow(
                 workflow=WORKFLOW_VNF_PREPARE,
-                arg=PrepareVnfInput(input.vnfr_uuid),
+                arg=input,
                 task_queue=vnf_task_queue,
                 id=f"{WORKFLOW_VNF_PREPARE}-{input.vnfr_uuid}",
             )
@@ -168,7 +168,7 @@ class VnfInstantiateWorkflow:
 
 
 @workflow.defn(name=WORKFLOW_VNF_PREPARE, sandboxed=_SANDBOXED)
-class PrepareVnfWorkflow:
+class VnfPrepareWorkflow:
     """Prepare a VNF.
 
     Workflow Identifier:
@@ -176,7 +176,20 @@ class PrepareVnfWorkflow:
         ID when invoking this workflow.
     """
 
+    def __init__(self):
+        self.logger = logging.getLogger(f"lcm.wfl.{self.__class__.__name__}")
+
     @workflow.run
-    async def run(self, input: PrepareVnfInput) -> None:
-        # TODO: Set the model here OSM-991
-        pass
+    async def run(self, wf_input: VnfInstantiateInput) -> None:
+        try:
+            await workflow.execute_activity(
+                activity=ACTIVITY_SET_VNF_MODEL,
+                arg=wf_input,
+                activity_id=f"{ACTIVITY_SET_VNF_MODEL}-{wf_input.vnfr_uuid}",
+                task_queue=LCM_TASK_QUEUE,
+                schedule_to_close_timeout=default_schedule_to_close_timeout,
+                retry_policy=retry_policy,
+            )
+        except Exception as e:
+            self.logger.error(f"{WORKFLOW_VNF_PREPARE} failed with {str(e)}")
+            raise e
diff --git a/osm_lcm/tests/test_vnf_activities.py b/osm_lcm/tests/test_vnf_activities.py
new file mode 100644 (file)
index 0000000..989a647
--- /dev/null
@@ -0,0 +1,48 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import asynctest
+from osm_common.dataclasses.temporal_dataclasses import VnfInstantiateInput
+from osm_common.dbbase import DbException
+from osm_lcm.temporal.vnf_activities import VnfDbActivity
+from temporalio.testing import ActivityEnvironment
+from unittest.mock import Mock
+
+
+vnfr_uuid = "d08d2da5-2120-476c-8538-deaeb4e88b3e"
+model_name = "a-model-name"
+vnf_instantiate_input = VnfInstantiateInput(vnfr_uuid=vnfr_uuid, model_name=model_name)
+
+
+class TestVnfDbActivity(asynctest.TestCase):
+    def setUp(self):
+        self.db = Mock()
+        self.env = ActivityEnvironment()
+        self.vnf_db_activity = VnfDbActivity(self.db)
+
+    async def test_set_vnf_model(self):
+        await self.env.run(self.vnf_db_activity.set_vnf_model, vnf_instantiate_input)
+        self.db.set_one.assert_called_with(
+            "vnfrs", {"_id": vnfr_uuid}, {"namespace": model_name}
+        )
+
+    async def test_db_raises_exception(self):
+        self.db.set_one.side_effect = DbException("not found")
+        with self.assertRaises(DbException):
+            await self.env.run(
+                self.vnf_db_activity.set_vnf_model, vnf_instantiate_input
+            )
diff --git a/osm_lcm/tests/test_vnf_workflows.py b/osm_lcm/tests/test_vnf_workflows.py
new file mode 100644 (file)
index 0000000..c5be1e7
--- /dev/null
@@ -0,0 +1,62 @@
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import asynctest
+import logging
+from osm_common.dataclasses.temporal_dataclasses import VnfInstantiateInput
+from osm_common.temporal_constants import (
+    LCM_TASK_QUEUE,
+    ACTIVITY_SET_VNF_MODEL,
+    WORKFLOW_VNF_PREPARE,
+)
+from osm_lcm.temporal.vnf_workflows import VnfPrepareWorkflow
+from temporalio import activity
+from temporalio.testing import WorkflowEnvironment
+from temporalio.worker import Worker
+
+wf_input = VnfInstantiateInput(
+    vnfr_uuid="86b53d92-4f5a-402e-8ac2-585ec6b64698",
+    model_name="a-model-name",
+)
+
+
+@activity.defn(name=ACTIVITY_SET_VNF_MODEL)
+async def set_vnf_model_mocked(set_vnf_model_input: VnfInstantiateInput) -> None:
+    logging.debug(
+        f"VNF {set_vnf_model_input.vnfr_uuid} model name is updated to {set_vnf_model_input.model_name}."
+    )
+
+
+class TestVnfPrepareWorkflow(asynctest.TestCase):
+    async def setUp(self):
+        self.env = await WorkflowEnvironment.start_time_skipping()
+        self.client = self.env.client
+
+    async def test_vnf_prepare_workflow(self):
+        async with self.env:
+            async with Worker(
+                self.client,
+                task_queue=LCM_TASK_QUEUE,
+                workflows=[VnfPrepareWorkflow],
+                activities=[set_vnf_model_mocked],
+            ):
+                await self.client.execute_workflow(
+                    VnfPrepareWorkflow.run,
+                    arg=wf_input,
+                    id=f"{WORKFLOW_VNF_PREPARE}-{wf_input.vnfr_uuid}",
+                    task_queue=LCM_TASK_QUEUE,
+                )