OSMENG-1048 Implement day1 configuration for VDU 10/13310/25
authorGulsum Atici <gulsum.atici@canonical.com>
Thu, 27 Apr 2023 13:41:43 +0000 (16:41 +0300)
committeraticig <gulsum.atici@canonical.com>
Thu, 8 Jun 2023 07:43:02 +0000 (09:43 +0200)
Day1 config implementation, arranging existing unit tests and imports
adding task and execution timeout policy for workflows in tests.

Change-Id: Ie5a2626eec01176723d8130576facbf4934d5285
Signed-off-by: Gulsum Atici <gulsum.atici@canonical.com>
19 files changed:
osm_lcm/nglcm.py
osm_lcm/temporal/juju_paas_activities.py
osm_lcm/temporal/lcm_activities.py
osm_lcm/temporal/lcm_workflows.py
osm_lcm/temporal/ns_activities.py
osm_lcm/temporal/ns_workflows.py
osm_lcm/temporal/vdu_workflows.py
osm_lcm/temporal/vim_activities.py
osm_lcm/temporal/vim_workflows.py
osm_lcm/temporal/vnf_activities.py
osm_lcm/temporal/vnf_workflows.py
osm_lcm/tests/test_juju_paas_activities.py
osm_lcm/tests/test_ns_activities.py
osm_lcm/tests/test_ns_workflows.py
osm_lcm/tests/test_vdu_workflow.py
osm_lcm/tests/test_vim_sdn.py
osm_lcm/tests/test_vim_workflows.py
osm_lcm/tests/test_vnf_activities.py
osm_lcm/tests/test_vnf_workflows.py

index 729b775..bad37f7 100644 (file)
@@ -27,9 +27,6 @@ from os import path
 import yaml
 from osm_common.dbbase import DbException
 from osm_common.temporal_constants import LCM_TASK_QUEUE
-from temporalio.client import Client
-from temporalio.worker import Worker
-
 from osm_lcm.data_utils.database.database import Database
 from osm_lcm.data_utils.lcm_config import LcmCfg
 from osm_lcm.lcm_utils import LcmException
@@ -51,6 +48,8 @@ from osm_lcm.temporal.vnf_activities import (
     VnfSendNotifications,
 )
 from osm_lcm.temporal.vnf_workflows import VnfInstantiateWorkflow, VnfPrepareWorkflow
+from temporalio.client import Client
+from temporalio.worker import Worker
 
 
 class NGLcm:
@@ -161,7 +160,8 @@ class NGLcm:
         ]
         activities = [
             ns_data_activity_instance.update_ns_state,
-            ns_operation_instance.get_vnf_record_ids,
+            ns_operation_instance.get_vnf_details,
+            ns_operation_instance.get_ns_record,
             nslcm_activity_instance.update_ns_lcm_operation_state,
             nslcm_activity_instance.no_op,
             paas_connector_instance.create_model,
@@ -175,7 +175,8 @@ class NGLcm:
             vnf_data_activity_instance.change_vnf_instantiation_state,
             vnf_operation_instance.get_task_queue,
             vnf_operation_instance.get_vim_cloud,
-            vnf_operation_instance.get_vnf_details,
+            vnf_operation_instance.get_vnf_descriptor,
+            vnf_operation_instance.get_vnf_record,
             vnf_send_notifications_instance.send_notification_for_vnf,
             vnf_data_activity_instance.set_vnf_model,
         ]
index 3182317..5082254 100644 (file)
@@ -17,6 +17,7 @@ import asyncio
 import base64
 import logging
 from dataclasses import dataclass
+
 from juju.application import Application
 from juju.controller import Controller
 from n2vc.config import EnvironConfig
@@ -245,6 +246,7 @@ class JujuPaasConnector:
             application_name=application_name,
             channel=charm_info.channel,
             constraints=constraints if constraints else None,
+            config=deploy_charm_input.config,
         )
 
     @activity.defn(name=ACTIVITY_CHECK_CHARM_STATUS)
index 5f360f1..6ddb0f3 100644 (file)
@@ -14,8 +14,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import logging
-from temporalio import activity
 import time
+
 from osm_common.dataclasses.temporal_dataclasses import (
     NsLcmOperationInput,
     UpdateLcmOperationStateInput,
@@ -25,6 +25,7 @@ from osm_common.temporal_constants import (
     ACTIVITY_NSLCM_NO_OP,
 )
 from osm_lcm.data_utils.database.database import Database
+from temporalio import activity
 
 
 class NsLcmActivity:
index 2dc7096..6c8c28f 100644 (file)
@@ -17,9 +17,6 @@
 import logging
 from abc import ABC, abstractmethod
 from datetime import timedelta
-from temporalio import workflow
-from temporalio.common import RetryPolicy
-from temporalio.exceptions import ActivityError, ChildWorkflowError
 
 from osm_common.dataclasses.temporal_dataclasses import (
     LcmOperationState,
@@ -31,6 +28,9 @@ from osm_common.temporal_constants import (
     ACTIVITY_UPDATE_LCM_OPERATION_STATE,
     WORKFLOW_NSLCM_NO_OP,
 )
+from temporalio import workflow
+from temporalio.common import RetryPolicy
+from temporalio.exceptions import ActivityError, ChildWorkflowError
 
 
 class LcmOperationWorkflow(ABC):
@@ -84,11 +84,11 @@ class LcmOperationWorkflow(ABC):
             raise e
 
         except ChildWorkflowError as e:
-            err_details = str(e.cause.cause.cause.with_traceback(e.cause.__traceback__))
+            err_details = str(e.cause.with_traceback(e.cause.__traceback__))
             self.logger.error(err_details)
             await self.update_operation_state(
                 LcmOperationState.FAILED,
-                error_message=str(e.cause.cause.message),
+                error_message=str(e.cause.message),
                 detailed_status=err_details,
             )
             raise e
@@ -98,7 +98,7 @@ class LcmOperationWorkflow(ABC):
             await self.update_operation_state(
                 LcmOperationState.FAILED,
                 error_message=str(e),
-                detailed_status=err_details,
+                detailed_status=str(e),
             )
             raise e
 
index b937717..8c7c182 100644 (file)
@@ -18,17 +18,19 @@ import logging
 from time import time
 
 from osm_common.dataclasses.temporal_dataclasses import (
-    GetVnfRecordIdsInput,
-    GetVnfRecordIdsOutput,
+    GetNsRecordInput,
+    GetNsRecordOutput,
+    GetVnfDetailsInput,
+    GetVnfDetailsOutput,
     UpdateNsStateInput,
 )
 from osm_common.temporal_constants import (
-    ACTIVITY_GET_VNF_RECORD_IDS,
+    ACTIVITY_GET_NS_RECORD,
+    ACTIVITY_GET_VNF_DETAILS,
     ACTIVITY_UPDATE_NS_STATE,
 )
-from temporalio import activity
-
 from osm_lcm.data_utils.database.database import Database
+from temporalio import activity
 
 
 class NsOperations:
@@ -36,12 +38,12 @@ class NsOperations:
         self.db: Database = db
         self.logger = logging.getLogger(f"lcm.act.{self.__class__.__name__}")
 
-    @activity.defn(name=ACTIVITY_GET_VNF_RECORD_IDS)
-    async def get_vnf_record_ids(
-        self, get_vnf_record_ids_input: GetVnfRecordIdsInput
-    ) -> GetVnfRecordIdsOutput:
+    @activity.defn(name=ACTIVITY_GET_VNF_DETAILS)
+    async def get_vnf_details(
+        self, get_vnf_details_input: GetVnfDetailsInput
+    ) -> GetVnfDetailsOutput:
         """
-        Gets the list of VNF record IDs for a given NS record ID.
+        Gets the list of VNF record IDs, VNF member-index-refs for a given NS record ID.
 
         Collaborators:
             DB Read:            vnfrs
@@ -55,10 +57,36 @@ class NsOperations:
             Since this activity only reads from the DB, it is safe to retry, although
             you may wish to have some back-off policy.
         """
-        vnfrs = self.db.get_list(
-            "vnfrs", {"nsr-id-ref": get_vnf_record_ids_input.ns_uuid}
+        vnfrs = self.db.get_list("vnfrs", {"nsr-id-ref": get_vnf_details_input.ns_uuid})
+        return GetVnfDetailsOutput(
+            vnf_details=[(vnfr["id"], vnfr["member-vnf-index-ref"]) for vnfr in vnfrs]
         )
-        return GetVnfRecordIdsOutput(vnfr_ids=[vnfr["id"] for vnfr in vnfrs])
+
+    @activity.defn(name=ACTIVITY_GET_NS_RECORD)
+    async def get_ns_record(
+        self, get_ns_record_input: GetNsRecordInput
+    ) -> GetNsRecordOutput:
+        """Gets the NS record from Database.
+
+        Collaborators:
+            DB Read:     nsrs
+
+        Raises (retryable):
+            DbException: If DB read operations fail, the collection or DB record ID does not exist.
+
+        Activity Lifecycle:
+            This activity should complete relatively quickly (less than 10
+            second).
+
+            This activity will not report a heartbeat due to its
+            short-running nature.
+
+            This is an idempotent activity.
+
+        """
+        nsr = self.db.get_one("nsrs", {"_id": get_ns_record_input.nsr_uuid})
+        self.logger.debug("Got the nsr from Database for VNF operations.")
+        return GetNsRecordOutput(nsr=nsr)
 
 
 class NsDbActivity:
index 343a9c9..6281ee6 100644 (file)
 # limitations under the License.
 
 import asyncio
-from temporalio import workflow
-from temporalio.converter import value_to_type
-from temporalio.exceptions import ActivityError, ChildWorkflowError
 import traceback
 
 from osm_common.dataclasses.temporal_dataclasses import (
-    GetVnfRecordIdsInput,
-    GetVnfRecordIdsOutput,
+    GetNsRecordInput,
+    GetNsRecordOutput,
+    GetVnfDetailsInput,
+    GetVnfDetailsOutput,
     ModelInfo,
     NsLcmOperationInput,
     NsState,
@@ -31,12 +30,16 @@ from osm_common.dataclasses.temporal_dataclasses import (
 )
 from osm_common.temporal_constants import (
     ACTIVITY_CREATE_MODEL,
-    ACTIVITY_GET_VNF_RECORD_IDS,
+    ACTIVITY_GET_NS_RECORD,
+    ACTIVITY_GET_VNF_DETAILS,
     ACTIVITY_UPDATE_NS_STATE,
     WORKFLOW_NS_INSTANTIATE,
     WORKFLOW_VNF_INSTANTIATE,
 )
 from osm_lcm.temporal.lcm_workflows import LcmOperationWorkflow
+from temporalio import workflow
+from temporalio.converter import value_to_type
+from temporalio.exceptions import ActivityError, ChildWorkflowError
 
 
 @workflow.defn(name=WORKFLOW_NS_INSTANTIATE, sandboxed=LcmOperationWorkflow._SANDBOXED)
@@ -50,8 +53,7 @@ class NsInstantiateWorkflow(LcmOperationWorkflow):
     async def workflow(self, input: NsLcmOperationInput) -> None:
         self.logger.info(f"Executing {WORKFLOW_NS_INSTANTIATE} with {input}")
 
-        # TODO: Can we clean up the input? Perhaps this workflow could receive NsInstantiateInput
-        # directly.
+        # TODO: Can we clean up the input? Perhaps this workflow could receive NsInstantiateInput directly.
         ns_uuid = input.nslcmop["nsInstanceId"]
         vim_uuid = input.nslcmop["operationParams"]["vimAccountId"]
         model_name = self._get_namespace(ns_uuid, vim_uuid)
@@ -63,27 +65,40 @@ class NsInstantiateWorkflow(LcmOperationWorkflow):
                 schedule_to_close_timeout=LcmOperationWorkflow.default_schedule_to_close_timeout,
                 retry_policy=LcmOperationWorkflow.no_retry_policy,
             )
-
-            vnf_record_ids_output: GetVnfRecordIdsOutput = value_to_type(
-                GetVnfRecordIdsOutput,
-                await workflow.execute_activity(
-                    activity=ACTIVITY_GET_VNF_RECORD_IDS,
-                    arg=GetVnfRecordIdsInput(ns_uuid=ns_uuid),
-                    activity_id=f"{ACTIVITY_GET_VNF_RECORD_IDS}-{ns_uuid}",
+            activities_results = await asyncio.gather(
+                workflow.execute_activity(
+                    activity=ACTIVITY_GET_VNF_DETAILS,
+                    arg=GetVnfDetailsInput(ns_uuid=ns_uuid),
+                    activity_id=f"{ACTIVITY_GET_VNF_DETAILS}-{ns_uuid}",
+                    schedule_to_close_timeout=LcmOperationWorkflow.default_schedule_to_close_timeout,
+                    retry_policy=LcmOperationWorkflow.no_retry_policy,
+                ),
+                workflow.execute_activity(
+                    activity=ACTIVITY_GET_NS_RECORD,
+                    arg=GetNsRecordInput(nsr_uuid=ns_uuid),
+                    activity_id=f"{ACTIVITY_GET_NS_RECORD}-{ns_uuid}",
                     schedule_to_close_timeout=LcmOperationWorkflow.default_schedule_to_close_timeout,
                     retry_policy=LcmOperationWorkflow.no_retry_policy,
                 ),
             )
+            get_vnf_details, get_ns_record = value_to_type(
+                GetVnfDetailsOutput, activities_results[0]
+            ), value_to_type(GetNsRecordOutput, activities_results[1])
+
             await asyncio.gather(
                 *(
                     workflow.execute_child_workflow(
                         workflow=WORKFLOW_VNF_INSTANTIATE,
                         arg=VnfInstantiateInput(
-                            vnfr_uuid=vnfr_uuid, model_name=model_name
+                            vnfr_uuid=vnfr_uuid,
+                            model_name=model_name,
+                            instantiation_config=NsInstantiateWorkflow.get_vnf_config(
+                                vnf_member_index_ref, get_ns_record.nsr
+                            ),
                         ),
                         id=f"{WORKFLOW_VNF_INSTANTIATE}-{vnfr_uuid}",
                     )
-                    for vnfr_uuid in vnf_record_ids_output.vnfr_ids
+                    for vnfr_uuid, vnf_member_index_ref in get_vnf_details.vnf_details
                 )
             )
 
@@ -94,7 +109,7 @@ class NsInstantiateWorkflow(LcmOperationWorkflow):
             raise e
 
         except ChildWorkflowError as e:
-            err_details = str(e.cause.cause.cause.with_traceback(e.cause.__traceback__))
+            err_details = str(e.cause.with_traceback(e.cause.__traceback__))
             await self.update_ns_state(ns_uuid, NsState.INSTANTIATED, err_details)
             self.logger.error(f"{WORKFLOW_NS_INSTANTIATE} failed with {err_details}")
             raise e
@@ -125,3 +140,19 @@ class NsInstantiateWorkflow(LcmOperationWorkflow):
     def _get_namespace(self, 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:]
+
+    @staticmethod
+    def get_vnf_config(vnf_member_index_ref: str, nsr: dict) -> dict:
+        """Get the VNF instantiation config
+        Args:
+            vnf_member_index_ref    (str):     VNF member-index-ref
+            nsr                     (dict):     NS record
+
+        Returns:
+            vnf_config  (dict)  VNF instantiation config
+
+        """
+        for vnf_config in nsr.get("instantiate_params", {}).get("vnf", {}):
+            if vnf_config.get("member-vnf-index") == vnf_member_index_ref:
+                return vnf_config
+        return {}
index 0c91e99..7349535 100644 (file)
@@ -16,9 +16,6 @@
 
 from datetime import timedelta
 import logging
-from temporalio import workflow
-from temporalio.common import RetryPolicy
-from temporalio.exceptions import ActivityError
 import traceback
 
 from osm_common.dataclasses.temporal_dataclasses import (
@@ -32,6 +29,9 @@ from osm_common.temporal_constants import (
     LCM_TASK_QUEUE,
     WORKFLOW_VDU_INSTANTIATE,
 )
+from temporalio import workflow
+from temporalio.common import RetryPolicy
+from temporalio.exceptions import ActivityError
 
 _SANDBOXED = False
 retry_policy = RetryPolicy(maximum_attempts=3)
index 4bd5514..e9db668 100644 (file)
@@ -15,8 +15,8 @@
 # limitations under the License.
 
 import logging
-from temporalio import activity
 from time import time
+
 from osm_common.temporal_constants import (
     ACTIVITY_DELETE_VIM,
     ACTIVITY_UPDATE_VIM_OPERATION_STATE,
@@ -28,6 +28,7 @@ from osm_common.dataclasses.temporal_dataclasses import (
     UpdateVimStateInput,
 )
 from osm_lcm.data_utils.database.database import Database
+from temporalio import activity
 
 
 class VimDbActivity:
index cb7d636..5d6b08c 100644 (file)
@@ -16,9 +16,6 @@
 
 from datetime import timedelta
 import logging
-from temporalio import workflow
-from temporalio.common import RetryPolicy
-from temporalio.exceptions import ActivityError
 import traceback
 
 from osm_common.dataclasses.temporal_dataclasses import (
@@ -40,6 +37,9 @@ from osm_common.temporal_constants import (
     WORKFLOW_VIM_UPDATE,
     WORKFLOW_VIM_DELETE,
 )
+from temporalio import workflow
+from temporalio.common import RetryPolicy
+from temporalio.exceptions import ActivityError
 
 _SANDBOXED = False
 retry_policy = RetryPolicy(maximum_attempts=3)
index 2e8ab1c..9581db0 100644 (file)
 # limitations under the License.
 
 import logging
+from typing import List, Any
+
 from osm_common.temporal_constants import (
     ACTIVITY_CHANGE_VNF_STATE,
     ACTIVITY_CHANGE_VNF_INSTANTIATION_STATE,
     ACTIVITY_GET_TASK_QUEUE,
     ACTIVITY_GET_VIM_CLOUD,
-    ACTIVITY_GET_VNF_DETAILS,
+    ACTIVITY_GET_VNF_DESCRIPTOR,
+    ACTIVITY_GET_VNF_RECORD,
     ACTIVITY_SEND_NOTIFICATION_FOR_VNF,
     ACTIVITY_SET_VNF_MODEL,
     VIM_TYPE_TASK_QUEUE_MAPPINGS,
@@ -31,14 +34,20 @@ from osm_common.dataclasses.temporal_dataclasses import (
     GetTaskQueueOutput,
     GetVimCloudInput,
     GetVimCloudOutput,
-    GetVnfDetailsInput,
-    GetVnfDetailsOutput,
-    VnfInstantiateInput,
+    GetVnfDescriptorInput,
+    GetVnfDescriptorOutput,
+    GetVnfRecordInput,
+    GetVnfRecordOutput,
+    SetVnfModelInput,
+    VduComputeConstraints,
 )
 from osm_lcm.data_utils.database.database import Database
 from temporalio import activity
 
 
+CONFIG_IDENTIFIER = "config::"
+
+
 class VnfOperations:
     def __init__(self, db: Database):
         self.db: Database = db
@@ -81,7 +90,7 @@ class VnfOperations:
         """Finds the cloud by checking the VIM account of VNF.
 
         Collaborators:
-            DB Access Object
+            DB Read:  vnfrs, vim_accounts
 
         Raises (retryable):
             DbException: If DB read operations fail, the collection or DB record ID does not exist.
@@ -104,14 +113,40 @@ class VnfOperations:
         self.logger.debug(f"Got the cloud type {cloud} for VNF operations.")
         return GetVimCloudOutput(cloud=cloud)
 
-    @activity.defn(name=ACTIVITY_GET_VNF_DETAILS)
-    async def get_vnf_details(
-        self, get_vnf_details_input: GetVnfDetailsInput
-    ) -> GetVnfDetailsOutput:
+    @activity.defn(name=ACTIVITY_GET_VNF_RECORD)
+    async def get_vnf_record(
+        self, get_vnf_record_input: GetVnfRecordInput
+    ) -> GetVnfRecordOutput:
+        """Gets the VNF record and VNF descriptor from Database.
+
+        Collaborators:
+            DB read:           vnfrs
+
+        Raises (retryable):
+            DbException: If DB read operations fail, the collection or DB record ID does not exist.
+
+        Activity Lifecycle:
+            This activity should complete relatively quickly (less than 10
+            second).
+
+            This activity will not report a heartbeat due to its
+            short-running nature.
+
+            This is an idempotent activity.
+
+        """
+        vnfr = self.db.get_one("vnfrs", {"_id": get_vnf_record_input.vnfr_uuid})
+        self.logger.debug("Got the vnfr from Database for VNF operations.")
+        return GetVnfRecordOutput(vnfr=vnfr)
+
+    @activity.defn(name=ACTIVITY_GET_VNF_DESCRIPTOR)
+    async def get_vnf_descriptor(
+        self, get_vnf_descriptor_input: GetVnfDescriptorInput
+    ) -> GetVnfDescriptorOutput:
         """Gets the VNF record and VNF descriptor from Database.
 
         Collaborators:
-            DB read:           vnfrs, vnfds
+            DB read:           vnfds
 
         Raises (retryable):
             DbException: If DB read operations fail, the collection or DB record ID does not exist.
@@ -126,10 +161,67 @@ class VnfOperations:
             This is an idempotent activity.
 
         """
-        vnfr = self.db.get_one("vnfrs", {"_id": get_vnf_details_input.vnfr_uuid})
-        vnfd = self.db.get_one("vnfds", {"_id": vnfr["vnfd-id"]})
+        vnfd = self.db.get_one("vnfds", {"_id": get_vnf_descriptor_input.vnfd_uuid})
         self.logger.debug("Got the vnfr and vnfd from Database for VNF operations.")
-        return GetVnfDetailsOutput(vnfr=vnfr, vnfd=vnfd)
+        return GetVnfDescriptorOutput(vnfd=vnfd)
+
+    @staticmethod
+    def get_vdu_instantiation_params(
+        vdu_id: str, vnf_instantiation_config: dict
+    ) -> dict:
+        for vdu in vnf_instantiation_config.get("vdu", []):
+            if vdu.get("id") == vdu_id:
+                return vdu.get("configurable-properties", {})
+        return {}
+
+    @staticmethod
+    def get_compute_constraints(vdu: dict, vnfd: dict) -> VduComputeConstraints:
+        compute_desc_id = vdu.get("virtual-compute-desc")
+        if not compute_desc_id:
+            return VduComputeConstraints(cores=0, mem=0)
+        flavor_details = VnfOperations._get_flavor_details(compute_desc_id, vnfd)
+        if not flavor_details:
+            return VduComputeConstraints(cores=0, mem=0)
+
+        cpu_cores = flavor_details.get("virtual-cpu", {}).get("num-virtual-cpu", 0)
+        memory_gb = flavor_details.get("virtual-memory", {}).get("size", 0)
+        return VduComputeConstraints(cores=cpu_cores, mem=int(memory_gb))
+
+    @staticmethod
+    def _get_flavor_details(compute_desc_id: str, vnfd: dict) -> Any:
+        for flavor in vnfd.get("virtual-compute-desc", []):
+            if flavor.get("id") == compute_desc_id:
+                return flavor
+        return None
+
+    @staticmethod
+    def get_application_config(vdu: dict, vdu_instantiation_config: dict) -> dict:
+        configurable_properties = vdu.get("configurable-properties", [])
+
+        config_from_descriptor = VnfOperations._get_only_config_items(
+            VnfOperations._list_to_dict(configurable_properties)
+        )
+
+        config_from_instantiation = VnfOperations._get_only_config_items(
+            vdu_instantiation_config
+        )
+        return {**config_from_descriptor, **config_from_instantiation}
+
+    @staticmethod
+    def _get_only_config_items(config: dict) -> dict:
+        return {
+            key[len(CONFIG_IDENTIFIER) :]: value
+            for key, value in config.items()
+            if key.startswith(CONFIG_IDENTIFIER)
+        }
+
+    @staticmethod
+    def _list_to_dict(indata: List[dict]) -> dict:
+        return {
+            item["key"]: item["value"]
+            for item in indata
+            if item.get("key") and item.get("value")
+        }
 
 
 class VnfDbActivity:
@@ -208,7 +300,7 @@ class VnfDbActivity:
         )
 
     @activity.defn(name=ACTIVITY_SET_VNF_MODEL)
-    async def set_vnf_model(self, set_vnf_model_input: VnfInstantiateInput) -> None:
+    async def set_vnf_model(self, set_vnf_model_input: SetVnfModelInput) -> None:
         """Updates the model name of VNF in VNFR.
 
         Collaborators:
index c97f908..c121b05 100644 (file)
 # implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+import asyncio
 from datetime import timedelta
 import logging
-from temporalio import workflow
-from temporalio.converter import value_to_type
-from temporalio.common import RetryPolicy
-from temporalio.exceptions import ActivityError, ChildWorkflowError
 import traceback
+from typing import Tuple
 
 from osm_common.dataclasses.temporal_dataclasses import (
     ChangeVnfInstantiationStateInput,
@@ -29,22 +26,25 @@ from osm_common.dataclasses.temporal_dataclasses import (
     GetTaskQueueOutput,
     GetVimCloudInput,
     GetVimCloudOutput,
-    GetVnfDetailsInput,
-    GetVnfDetailsOutput,
-    VduComputeConstraints,
+    GetVnfDescriptorInput,
+    GetVnfDescriptorOutput,
+    GetVnfRecordInput,
+    GetVnfRecordOutput,
+    SetVnfModelInput,
     VduInstantiateInput,
     VnfInstantiateInput,
     VnfInstantiationState,
+    VnfPrepareInput,
     VnfState,
 )
-
 from osm_common.temporal_constants import (
     ACTIVITY_CHANGE_VNF_INSTANTIATION_STATE,
     ACTIVITY_SEND_NOTIFICATION_FOR_VNF,
     ACTIVITY_CHANGE_VNF_STATE,
     ACTIVITY_GET_TASK_QUEUE,
     ACTIVITY_GET_VIM_CLOUD,
-    ACTIVITY_GET_VNF_DETAILS,
+    ACTIVITY_GET_VNF_RECORD,
+    ACTIVITY_GET_VNF_DESCRIPTOR,
     ACTIVITY_SET_VNF_MODEL,
     LCM_TASK_QUEUE,
     WORKFLOW_VDU_INSTANTIATE,
@@ -52,6 +52,11 @@ from osm_common.temporal_constants import (
     WORKFLOW_VNF_PREPARE,
 )
 from osm_lcm.temporal.juju_paas_activities import CharmInfoUtils
+from osm_lcm.temporal.vnf_activities import VnfOperations
+from temporalio import workflow
+from temporalio.converter import value_to_type
+from temporalio.common import RetryPolicy
+from temporalio.exceptions import ActivityError, ChildWorkflowError
 
 _SANDBOXED = False
 retry_policy = RetryPolicy(maximum_attempts=3)
@@ -107,21 +112,27 @@ class VnfInstantiateWorkflow:
                 id=f"{WORKFLOW_VNF_PREPARE}-{input.vnfr_uuid}",
             )
 
-            get_vnf_details = value_to_type(
-                GetVnfDetailsOutput,
+            get_vnf_record = value_to_type(
+                GetVnfRecordOutput,
                 await workflow.execute_activity(
-                    activity=ACTIVITY_GET_VNF_DETAILS,
-                    arg=GetVnfDetailsInput(input.vnfr_uuid),
-                    activity_id=f"{ACTIVITY_GET_VNF_DETAILS}-{input.vnfr_uuid}",
+                    activity=ACTIVITY_GET_VNF_RECORD,
+                    arg=GetVnfRecordInput(input.vnfr_uuid),
+                    activity_id=f"{ACTIVITY_GET_VNF_RECORD}-{input.vnfr_uuid}",
                     task_queue=vnf_task_queue.task_queue,
                     schedule_to_close_timeout=default_schedule_to_close_timeout,
                     retry_policy=retry_policy,
                 ),
             )
-
-            get_cloud = value_to_type(
-                GetVimCloudOutput,
-                await workflow.execute_activity(
+            activities_results = await asyncio.gather(
+                workflow.execute_activity(
+                    activity=ACTIVITY_GET_VNF_DESCRIPTOR,
+                    arg=GetVnfDescriptorInput(get_vnf_record.vnfr["vnfd-id"]),
+                    activity_id=f"{ACTIVITY_GET_VNF_DESCRIPTOR}-{get_vnf_record.vnfr['vnfd-id']}",
+                    task_queue=vnf_task_queue.task_queue,
+                    schedule_to_close_timeout=default_schedule_to_close_timeout,
+                    retry_policy=retry_policy,
+                ),
+                workflow.execute_activity(
                     activity=ACTIVITY_GET_VIM_CLOUD,
                     arg=GetVimCloudInput(input.vnfr_uuid),
                     activity_id=f"{ACTIVITY_GET_VIM_CLOUD}-{input.vnfr_uuid}",
@@ -130,12 +141,16 @@ class VnfInstantiateWorkflow:
                     retry_policy=retry_policy,
                 ),
             )
+            get_vnf_descriptor, get_cloud = value_to_type(
+                GetVnfDescriptorOutput, activities_results[0]
+            ), value_to_type(GetVimCloudOutput, activities_results[1])
 
             await self.instantiate_vdus(
-                vnfr=get_vnf_details.vnfr,
-                vnfd=get_vnf_details.vnfd,
+                vnfr=get_vnf_record.vnfr,
+                vnfd=get_vnf_descriptor.vnfd,
                 task_queue=vnf_task_queue.task_queue,
                 cloud=get_cloud.cloud,
+                vnf_instantiation_config=input.instantiation_config,
             )
             await self.update_vnf_instantiation_state(
                 ChangeVnfInstantiationStateInput(
@@ -170,7 +185,7 @@ class VnfInstantiateWorkflow:
             raise e
 
         except ChildWorkflowError as e:
-            err_details = str(e.cause.cause.with_traceback(e.cause.__traceback__))
+            err_details = str(e.cause.with_traceback(e.cause.__traceback__))
             await self.update_vnf_instantiation_state(
                 ChangeVnfInstantiationStateInput(
                     vnfr_uuid=input.vnfr_uuid, state=VnfInstantiationState.INSTANTIATED
@@ -241,13 +256,23 @@ class VnfInstantiateWorkflow:
         )
 
     @staticmethod
-    async def instantiate_vdus(vnfr: dict, vnfd: dict, task_queue: str, cloud: str):
+    async def instantiate_vdus(
+        vnfr: dict,
+        vnfd: dict,
+        task_queue: str,
+        cloud: str,
+        vnf_instantiation_config: dict,
+    ):
         for vdu in vnfd.get("vdu"):
             (
                 vdu_instantiate_input,
                 vdu_instantiate_workflow_id,
-            ) = VnfInstantiateWorkflow.get_vdu_instantiate_input(
-                vnfr=vnfr, vnfd=vnfd, vdu=vdu, cloud=cloud
+            ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=vnfr,
+                vnfd=vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config=vnf_instantiation_config,
             )
             await workflow.execute_child_workflow(
                 workflow=WORKFLOW_VDU_INSTANTIATE,
@@ -257,53 +282,26 @@ class VnfInstantiateWorkflow:
             )
 
     @staticmethod
-    def get_flavor_details(compute_desc_id: str, vnfd: dict):
-        if not compute_desc_id:
-            return {}
-        flavor_details = next(
-            filter(
-                lambda flavor: flavor.get("id") == compute_desc_id,
-                vnfd.get("virtual-compute-desc", {}),
-            ),
-            {},
-        )
-        return flavor_details
-
-    @staticmethod
-    def get_compute_constraints(vdu: dict, vnfd: dict) -> VduComputeConstraints:
-        compute_desc_id = vdu.get("virtual-compute-desc", "")
-        flavor_details = VnfInstantiateWorkflow.get_flavor_details(
-            compute_desc_id, vnfd
-        )
-        if not flavor_details:
-            return VduComputeConstraints(cores=0, mem=0)
-
-        cpu_cores = (
-            flavor_details["virtual-cpu"].get("num-virtual-cpu", 0)
-            if flavor_details.get("virtual-cpu")
-            else 0
-        )
-        memory_gb = (
-            flavor_details["virtual-memory"].get("size", 0)
-            if flavor_details.get("virtual-memory")
-            else 0
-        )
-        return VduComputeConstraints(cores=cpu_cores, mem=int(memory_gb))
-
-    @staticmethod
-    def get_vdu_instantiate_input(vnfr: dict, vnfd: dict, vdu: dict, cloud: str):
+    def _get_vdu_instantiate_info(
+        vnfr, vnfd, vdu, cloud, vnf_instantiation_config
+    ) -> Tuple[VduInstantiateInput, str]:
+        """Calculates the VDU instantiate input data without reaching Database."""
         model_name = vnfr.get("namespace")
         vim_id = vnfr.get("vim-account-id")
         sw_image_descs = vnfd.get("sw-image-desc")
         vdu_info = CharmInfoUtils.get_charm_info(vdu, sw_image_descs)
-        compute_constraints = VnfInstantiateWorkflow.get_compute_constraints(vdu, vnfd)
+        vdu_instantiation_config = VnfOperations.get_vdu_instantiation_params(
+            vdu["id"], vnf_instantiation_config
+        )
+        compute_constraints = VnfOperations.get_compute_constraints(vdu, vnfd)
+        config = VnfOperations.get_application_config(vdu, vdu_instantiation_config)
         vdu_instantiate_input = VduInstantiateInput(
             vim_uuid=vim_id,
             model_name=model_name,
             charm_info=vdu_info,
             constraints=compute_constraints,
             cloud=cloud,
-            config={},
+            config=config,
         )
         vdu_instantiate_workflow_id = (
             vdu_instantiate_input.model_name
@@ -326,11 +324,11 @@ class VnfPrepareWorkflow:
         self.logger = logging.getLogger(f"lcm.wfl.{self.__class__.__name__}")
 
     @workflow.run
-    async def run(self, wf_input: VnfInstantiateInput) -> None:
+    async def run(self, wf_input: VnfPrepareInput) -> None:
         try:
             await workflow.execute_activity(
                 activity=ACTIVITY_SET_VNF_MODEL,
-                arg=wf_input,
+                arg=SetVnfModelInput(wf_input.vnfr_uuid, wf_input.model_name),
                 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,
index db1de7c..1aae706 100644 (file)
@@ -18,6 +18,7 @@ import asynctest
 import asyncio
 from unittest import TestCase
 import unittest.mock as mock
+from unittest.mock import ANY, AsyncMock, Mock
 
 from juju.application import Application
 from juju.controller import Controller
@@ -36,7 +37,6 @@ from osm_common.dbbase import DbException
 from osm_lcm.temporal.juju_paas_activities import JujuPaasConnector
 from parameterized import parameterized
 from temporalio.testing import ActivityEnvironment
-from unittest.mock import ANY, AsyncMock, Mock
 
 namespace = "some-namespace"
 vim_content = {
@@ -259,9 +259,17 @@ class TestDeployCharm(TestJujuPaasActivitiesBase):
     entity_url = "ch:my-charm"
     cloud_k8s = "microk8s"
     cloud_other = "other"
-    config = {}
+    config = {"domain_name1": "osm.org", "domain_name2": "osm.com"}
     charm_info = CharmInfo(app_name, channel, entity_url)
-    vdu_instantiate_input_with_constraints_k8s = VduInstantiateInput(
+    vdu_instantiate_input_with_constraints_k8s_without_config = VduInstantiateInput(
+        vim_content["_id"],
+        namespace,
+        charm_info,
+        VduComputeConstraints(mem=1, cores=1),
+        cloud_k8s,
+        {},
+    )
+    vdu_instantiate_input_with_constraints_k8s_with_config = VduInstantiateInput(
         vim_content["_id"],
         namespace,
         charm_info,
@@ -270,80 +278,82 @@ class TestDeployCharm(TestJujuPaasActivitiesBase):
         config,
     )
 
-    async def test_deploy_charm_with_constraints_k8s_cloud(self):
-        await self.env.run(
-            self.juju_paas.deploy_charm, self.vdu_instantiate_input_with_constraints_k8s
-        )
-        self.model.deploy.assert_called_once_with(
-            entity_url=self.entity_url,
-            application_name=self.app_name,
-            channel=self.channel,
-            constraints={"mem": 1024},
-        )
-
-    async def test_deploy_charm_with_constraints_other_cloud(self):
-        await self.env.run(
-            self.juju_paas.deploy_charm,
-            VduInstantiateInput(
-                vim_content["_id"],
-                namespace,
-                self.charm_info,
-                VduComputeConstraints(mem=1, cores=1),
-                self.cloud_other,
-                self.config,
+    @parameterized.expand(
+        [
+            (
+                "k8s_cloud_without_config_with_constraints",
+                vdu_instantiate_input_with_constraints_k8s_without_config,
+                {"mem": 1024},
+                {},
             ),
-        )
-        self.model.deploy.assert_called_once_with(
-            entity_url=self.entity_url,
-            application_name=self.app_name,
-            channel=self.channel,
-            constraints={"mem": 1024, "cores": 1},
-        )
-
-    async def test_deploy_charm_without_constraints_k8s_cloud(self):
-        await self.env.run(
-            self.juju_paas.deploy_charm,
-            VduInstantiateInput(
-                vim_content["_id"],
-                namespace,
-                self.charm_info,
-                VduComputeConstraints(mem=0, cores=0),
-                self.cloud_k8s,
-                self.config,
+            (
+                "k8s_cloud_with_config_with_constraints",
+                vdu_instantiate_input_with_constraints_k8s_with_config,
+                {"mem": 1024},
+                config,
             ),
-        )
-        self.model.deploy.assert_called_once_with(
-            entity_url=self.entity_url,
-            application_name=self.app_name,
-            channel=self.channel,
-            constraints=None,
-        )
-
-    async def test_deploy_charm_without_constraints_other_cloud(self):
+            (
+                "k8s_cloud_with_config_without_constraints",
+                VduInstantiateInput(
+                    vim_content["_id"],
+                    namespace,
+                    charm_info,
+                    VduComputeConstraints(mem=0, cores=0),
+                    cloud_k8s,
+                    config,
+                ),
+                None,
+                config,
+            ),
+            (
+                "other_cloud_without_config_with_constraints",
+                VduInstantiateInput(
+                    vim_content["_id"],
+                    namespace,
+                    charm_info,
+                    VduComputeConstraints(mem=1, cores=1),
+                    cloud_other,
+                    {},
+                ),
+                {"mem": 1024, "cores": 1},
+                {},
+            ),
+            (
+                "other_cloud_without_config_without_constraints",
+                VduInstantiateInput(
+                    vim_content["_id"],
+                    namespace,
+                    charm_info,
+                    VduComputeConstraints(mem=0, cores=0),
+                    cloud_other,
+                    {},
+                ),
+                None,
+                {},
+            ),
+        ]
+    )
+    async def test_deploy_charm__model_deployed_with_expected_constraints_and_config(
+        self, _, vdu_instantiate_input, expected_constraint, expected_config
+    ):
         await self.env.run(
             self.juju_paas.deploy_charm,
-            VduInstantiateInput(
-                vim_content["_id"],
-                namespace,
-                self.charm_info,
-                VduComputeConstraints(mem=0, cores=0),
-                self.cloud_other,
-                self.config,
-            ),
+            vdu_instantiate_input,
         )
         self.model.deploy.assert_called_once_with(
             entity_url=self.entity_url,
             application_name=self.app_name,
             channel=self.channel,
-            constraints=None,
+            constraints=expected_constraint,
+            config=expected_config,
         )
 
-    async def test_deploy_charm_app_already_exists(self):
+    async def test_deploy_charm__app_already_exists__get_expected_error_details(self):
         self.add_application(self.app_name)
         with self.assertRaises(Exception) as err:
             await self.env.run(
                 self.juju_paas.deploy_charm,
-                self.vdu_instantiate_input_with_constraints_k8s,
+                self.vdu_instantiate_input_with_constraints_k8s_without_config,
             )
         self.model.deploy.assert_not_called()
         self.assertEqual(
@@ -351,12 +361,12 @@ class TestDeployCharm(TestJujuPaasActivitiesBase):
             "Application {} already exists".format(self.app_name),
         )
 
-    async def test_deploy_charm_raises_exception(self):
+    async def test_deploy_charm__juju_error_occured__app_is_not_deployed(self):
         self.controller.get_model.side_effect = JujuError()
         with self.assertRaises(JujuError):
             await self.env.run(
                 self.juju_paas.deploy_charm,
-                self.vdu_instantiate_input_with_constraints_k8s,
+                self.vdu_instantiate_input_with_constraints_k8s_without_config,
             )
         self.model.deploy.assert_not_called()
 
@@ -365,31 +375,45 @@ class TestGetApplicationConstraints(TestCase):
     constraints = VduComputeConstraints(mem=1, cores=1)
     no_constraints = VduComputeConstraints(mem=0, cores=0)
 
-    def test_get_application_constraints_k8s_cloud(self):
-        result = JujuPaasConnector._get_application_constraints(
-            self.constraints, "kubernetes"
-        )
-        self.assertEqual(result, {"mem": 1024})
-
-    def test_get_application_constraints_aws_cloud(self):
-        result = JujuPaasConnector._get_application_constraints(self.constraints, "aws")
-        self.assertEqual(result, {"cores": 1, "mem": 1024})
-
-    def test_get_application_constraints_no_constraints_aws(self):
-        result = JujuPaasConnector._get_application_constraints(
-            self.no_constraints, "aws"
-        )
-        self.assertEqual(result, {})
-
-    def test_get_application_constraints_no_constraints_microk8s(self):
-        result = JujuPaasConnector._get_application_constraints(
-            self.no_constraints, "microk8s"
-        )
-        self.assertEqual(result, {})
-
-    def test_get_application_constraints_empty_cloud(self):
-        result = JujuPaasConnector._get_application_constraints(self.constraints, "")
-        self.assertEqual(result, {"cores": 1, "mem": 1024})
+    @parameterized.expand(
+        [
+            (
+                "k8s_cloud_with_constraints",
+                constraints,
+                "kubernetes",
+                {"mem": 1024},
+            ),
+            (
+                "aws_cloud_with_constraints",
+                constraints,
+                "aws",
+                {"cores": 1, "mem": 1024},
+            ),
+            (
+                "aws_cloud_without_constraints",
+                no_constraints,
+                "aws",
+                {},
+            ),
+            (
+                "microk8s_without_constraints",
+                no_constraints,
+                "microk8s",
+                {},
+            ),
+            (
+                "empty_cloud_with_constraints",
+                constraints,
+                "",
+                {"cores": 1, "mem": 1024},
+            ),
+        ]
+    )
+    def test_get_application_constraints__get_expected_constraints(
+        self, _, constraints, cloud, expected_result
+    ):
+        result = JujuPaasConnector._get_application_constraints(constraints, cloud)
+        self.assertEqual(result, expected_result)
 
 
 class TestTestVimConnectivity(TestJujuPaasActivitiesBase):
index 623f072..c3176ee 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
-from unittest.mock import Mock
-
 import asynctest
-from osm_common.dataclasses.temporal_dataclasses import GetVnfRecordIdsInput
+
+from osm_common.dataclasses.temporal_dataclasses import (
+    GetNsRecordInput,
+    GetVnfDetailsInput,
+)
 from osm_common.dbbase import DbException
 from temporalio.testing import ActivityEnvironment
-
 from osm_lcm.temporal.ns_activities import NsOperations
+from unittest.mock import Mock
 
 ns_uuid = "00000000-0000-0000-0000-000000000000"
-get_vnf_record_ids_input = GetVnfRecordIdsInput(ns_uuid=ns_uuid)
+get_vnf_details_input = GetVnfDetailsInput(ns_uuid=ns_uuid)
+sample_vnf_details = [
+    {
+        "id": "00000000-0000-0000-0000-000000000000",
+        "member-vnf-index-ref": "vnf1",
+    },
+    {
+        "id": "00000000-0000-0000-0000-000000000000",
+        "member-vnf-index-ref": "vnf2",
+    },
+]
+sample_nsr = {
+    "_id": ns_uuid,
+    "name": "sol006_juju24",
+    "name-ref": "sol006_juju24",
+    "short-name": "sol006_juju24",
+    "admin-status": "ENABLED",
+    "nsState": "NOT_INSTANTIATED",
+    "currentOperation": "IDLE",
+}
+
+
+class TestException(Exception):
+    pass
 
 
-class TestGetModelInfo(asynctest.TestCase):
+class TestGetVnfDetails(asynctest.TestCase):
     def setUp(self):
         self.db = Mock()
         self.env = ActivityEnvironment()
         self.ns_operations_activity = NsOperations(self.db)
 
-    async def test_get_vnfr_ids(self):
-        self.db.get_list.return_value = [
-            {"id": "00000000-0000-0000-0000-000000000000"},
-            {"id": "00000000-0000-0000-0000-000000000000"},
-        ]
+    async def test_activity__succeded__get_expected_result(self):
+        self.db.get_list.return_value = sample_vnf_details
         result = await self.env.run(
-            self.ns_operations_activity.get_vnf_record_ids, get_vnf_record_ids_input
+            self.ns_operations_activity.get_vnf_details, get_vnf_details_input
         )
 
-        assert result.vnfr_ids == [
-            "00000000-0000-0000-0000-000000000000",
-            "00000000-0000-0000-0000-000000000000",
-        ]
-
-        self.db.get_list.assert_called_with("vnfrs", {"nsr-id-ref": ns_uuid})
+        self.assertEqual(
+            result.vnf_details,
+            [
+                ("00000000-0000-0000-0000-000000000000", "vnf1"),
+                ("00000000-0000-0000-0000-000000000000", "vnf2"),
+            ],
+        )
 
-    async def test_activity_raises_db_exception(self):
-        self.db.get_list.side_effect = DbException("not found")
+    async def test_activity__failed__raise_db_exception(self):
+        self.db.get_list.side_effect = DbException("not found.")
         with self.assertRaises(DbException):
             await self.env.run(
-                self.ns_operations_activity.get_vnf_record_ids, get_vnf_record_ids_input
+                self.ns_operations_activity.get_vnf_details, get_vnf_details_input
+            )
+
+
+class TestGetNsRecord(asynctest.TestCase):
+    async def setUp(self):
+        self.db = Mock()
+        self.env = ActivityEnvironment()
+        self.ns_operations_activity = NsOperations(self.db)
+
+    async def test_activity__succeeded__get_expected_result(self):
+        self.db.get_one.return_value = sample_nsr
+        activity_result = await self.env.run(
+            self.ns_operations_activity.get_ns_record,
+            GetNsRecordInput(nsr_uuid=sample_nsr["_id"]),
+        )
+        self.assertEqual(activity_result.nsr, sample_nsr)
+
+    async def test_activity__failed__raise_test_exception(self):
+        self.db.get_one.side_effect = TestException("Can not connect to Database.")
+        with self.assertRaises(TestException):
+            await self.env.run(
+                self.ns_operations_activity.get_ns_record,
+                GetNsRecordInput(nsr_uuid=sample_nsr["_id"]),
             )
index 082b155..e5f38ef 100644 (file)
 # implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
-
-from unittest.mock import Mock
-
 import asynctest
-from mock import AsyncMock, patch
+import copy
+from datetime import timedelta
+from unittest.mock import Mock, patch
+
 from osm_common.dataclasses.temporal_dataclasses import (
-    GetVnfRecordIdsInput,
-    GetVnfRecordIdsOutput,
+    GetNsRecordInput,
+    GetNsRecordOutput,
+    GetVnfDetailsInput,
+    GetVnfDetailsOutput,
     LcmOperationState,
     ModelInfo,
     NsLcmOperationInput,
@@ -32,23 +33,77 @@ from osm_common.dataclasses.temporal_dataclasses import (
 )
 from osm_common.temporal_constants import (
     ACTIVITY_CREATE_MODEL,
-    ACTIVITY_GET_VNF_RECORD_IDS,
+    ACTIVITY_GET_NS_RECORD,
+    ACTIVITY_GET_VNF_DETAILS,
     ACTIVITY_UPDATE_LCM_OPERATION_STATE,
     ACTIVITY_UPDATE_NS_STATE,
     LCM_TASK_QUEUE,
     WORKFLOW_VNF_INSTANTIATE,
 )
+from osm_lcm.temporal.ns_workflows import NsInstantiateWorkflow
 from temporalio import activity, workflow
 from temporalio.client import WorkflowFailureError
+from temporalio.common import RetryPolicy
+from temporalio.exceptions import (
+    ActivityError,
+    ChildWorkflowError,
+    RetryState,
+    TimeoutError,
+)
 from temporalio.testing import WorkflowEnvironment
 from temporalio.worker import Worker
 
-from osm_lcm.temporal.ns_workflows import NsInstantiateWorkflow
 
-vnfr_ids = [
-    "828d91ee-fa04-43bb-8471-f66ea74597e7",
-    "c4bbeb41-df7e-4daa-863d-c8fd29fac96d",
+vnfr_uuid_1 = "828d91ee-fa04-43bb-8471-f66ea74597e7"
+vnfr_uuid_2 = "c4bbeb41-df7e-4daa-863d-c8fd29fac96d"
+vnf_member_index_ref_1 = "vnf-profile-id"
+vnf_member_index_ref_2 = "vnf-profile-id-2"
+vnf_details = [
+    (vnfr_uuid_1, vnf_member_index_ref_1),
+    (vnfr_uuid_2, vnf_member_index_ref_2),
 ]
+ns_uuid = "9c96e9c1-aae2-4c61-a85f-2ec8acb448fc"
+vim_uuid = "a64f7c6c-bc27-4ec8-b664-5500a3324eca"
+vdu_id = "test-vdu"
+model_name = "2ec8acb448fc-5500a3324eca"
+vnf_config = {
+    "member-vnf-index": vnf_member_index_ref_1,
+    "vdu": [
+        {
+            "id": vdu_id,
+            "configurable-properties": {
+                "config::redirect-map": "https://osm.instantiation.params"
+            },
+        }
+    ],
+}
+sample_nsr = {
+    "_id": ns_uuid,
+    "name": "sol006_juju23",
+    "name-ref": "sol006_juju23",
+    "short-name": "sol006_juju23",
+    "admin-status": "ENABLED",
+    "nsState": "NOT_INSTANTIATED",
+    "instantiate_params": {
+        "nsdId": ns_uuid,
+        "nsName": "sol006_juju23",
+        "nsDescription": "default description",
+        "vimAccountId": vim_uuid,
+        "vnf": [vnf_config],
+    },
+}
+mock_get_namespace = Mock()
+retry_policy = RetryPolicy(
+    initial_interval=timedelta(seconds=1),
+    backoff_coefficient=2.0,
+    maximum_interval=None,
+    maximum_attempts=1,
+    non_retryable_error_types=None,
+)
+SANDBOXED = False
+DEBUG_MODE = True
+TASK_TIMEOUT = timedelta(seconds=0.5)
+EXECUTION_TIMEOUT = timedelta(seconds=1)
 
 
 class TestException(Exception):
@@ -60,31 +115,72 @@ async def mock_create_model(create_model_input: ModelInfo) -> None:
     pass
 
 
-@activity.defn(name=ACTIVITY_GET_VNF_RECORD_IDS)
-async def mock_get_vnf_record_ids(
-    get_vnf_record_ids_input: GetVnfRecordIdsInput,
-) -> None:
-    return GetVnfRecordIdsOutput(vnfr_ids=vnfr_ids)
-
-
 @activity.defn(name=ACTIVITY_CREATE_MODEL)
 async def mock_create_model_raises(create_model_input: ModelInfo) -> None:
-    raise TestException("Test exception")
+    raise TestException(f"{ACTIVITY_CREATE_MODEL} failed.")
+
+
+@activity.defn(name=ACTIVITY_GET_VNF_DETAILS)
+async def mock_get_vnf_details(
+    get_vnf_details_input: GetVnfDetailsInput,
+) -> GetVnfDetailsOutput:
+    return GetVnfDetailsOutput(vnf_details=vnf_details)
 
 
-@workflow.defn(name=WORKFLOW_VNF_INSTANTIATE, sandboxed=False)
+@activity.defn(name=ACTIVITY_GET_VNF_DETAILS)
+async def mock_get_vnf_details_raises(
+    et_vnf_details_input: GetVnfDetailsInput,
+) -> GetVnfDetailsOutput:
+    raise TestException(f"{ACTIVITY_GET_VNF_DETAILS} failed.")
+
+
+@activity.defn(name=ACTIVITY_GET_NS_RECORD)
+async def mock_get_ns_record(
+    get_ns_record_input: GetNsRecordInput,
+) -> GetNsRecordOutput:
+    return GetNsRecordOutput(nsr=sample_nsr)
+
+
+@activity.defn(name=ACTIVITY_GET_NS_RECORD)
+async def mock_get_ns_record_raise_exception(
+    get_ns_record_input: GetNsRecordInput,
+) -> GetNsRecordOutput:
+    raise TestException(f"{ACTIVITY_GET_NS_RECORD} failed.")
+
+
+@workflow.defn(name=WORKFLOW_VNF_INSTANTIATE, sandboxed=SANDBOXED)
 class MockVnfInstantiateWorkflow:
     @workflow.run
     async def run(self, input: VnfInstantiateInput) -> None:
         pass
 
 
+@workflow.defn(name=WORKFLOW_VNF_INSTANTIATE, sandboxed=SANDBOXED)
+class MockVnfInstantiateWorkflowFailed:
+    @workflow.run
+    async def run(self, input: VnfInstantiateInput) -> None:
+        raise ChildWorkflowError(
+            message=f"{WORKFLOW_VNF_INSTANTIATE} child workflow failed.",
+            namespace="default",
+            workflow_id="123",
+            run_id="1",
+            workflow_type=WORKFLOW_VNF_INSTANTIATE,
+            initiated_event_id=0,
+            started_event_id=0,
+            retry_state=RetryState.NON_RETRYABLE_FAILURE,
+        )
+
+
+@patch(
+    "osm_lcm.temporal.ns_workflows.NsInstantiateWorkflow._get_namespace",
+    new=mock_get_namespace,
+)
 class TestNsInstantiateWorkflow(asynctest.TestCase):
     input = NsLcmOperationInput(
         nslcmop={
             "_id": "1234",
-            "nsInstanceId": "5678",
-            "operationParams": {"vimAccountId": "9876"},
+            "nsInstanceId": ns_uuid,
+            "operationParams": {"vimAccountId": vim_uuid},
         }
     )
 
@@ -101,111 +197,414 @@ class TestNsInstantiateWorkflow(asynctest.TestCase):
 
     async def setUp(self):
         self.env = await WorkflowEnvironment.start_time_skipping()
+        self.client = self.env.client
         self.mock_update_lcm_operation_state_tracker = Mock()
         self.mock_update_ns_state_tracker = Mock()
+        self.workflows = [NsInstantiateWorkflow, MockVnfInstantiateWorkflow]
+        self.task_queue = LCM_TASK_QUEUE
+        mock_get_namespace.return_value = model_name
 
-    @patch("temporalio.workflow.execute_child_workflow")
-    async def test_instantiate_workflow(self, mock_execute_child_workflow: AsyncMock):
-        async with self.env as env:
-            async with Worker(
-                env.client,
-                task_queue=LCM_TASK_QUEUE,
-                workflows=[NsInstantiateWorkflow, MockVnfInstantiateWorkflow],
-                activities=[
-                    mock_create_model,
-                    mock_get_vnf_record_ids,
-                    self.mock_update_ns_state,
-                    self.mock_update_lcm_operation_state,
-                ],
-                debug_mode=True,
-            ):
-                await env.client.execute_workflow(
-                    NsInstantiateWorkflow,
-                    arg=self.input,
-                    id=self.input.nslcmop["nsInstanceId"],
-                    task_queue=LCM_TASK_QUEUE,
-                )
+    def get_worker(self, task_queue: str, workflows: list, activities: list) -> Worker:
+        return Worker(
+            self.client,
+            task_queue=task_queue,
+            workflows=workflows,
+            activities=activities,
+            debug_mode=DEBUG_MODE,
+        )
 
-        self.assertEqual(
-            [
-                call.kwargs["workflow"] == WORKFLOW_VNF_INSTANTIATE
-                and call.kwargs["arg"].vnfr_uuid
-                for call in mock_execute_child_workflow.call_args_list
-            ],
-            vnfr_ids,
+    async def execute_workflow(self):
+        return await self.client.execute_workflow(
+            NsInstantiateWorkflow,
+            arg=self.input,
+            id=self.input.nslcmop["nsInstanceId"],
+            task_queue=self.task_queue,
+            task_timeout=TASK_TIMEOUT,
+            execution_timeout=EXECUTION_TIMEOUT,
         )
 
+    async def test_instantiate_workflow__successful__update_lcm_op_state(self):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        async with self.env, self.get_worker(
+            self.task_queue, self.workflows, activities
+        ):
+            await self.execute_workflow()
         self.assert_lcm_op_states(
             self.mock_update_lcm_operation_state_tracker.call_args_list,
             [LcmOperationState.PROCESSING, LcmOperationState.COMPLETED],
         )
 
+    async def test_instantiate_workflow__successful__update_ns_state(self):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        async with self.env, self.get_worker(
+            self.task_queue, self.workflows, activities
+        ):
+            await self.execute_workflow()
+        self.assert_ns_states(
+            self.mock_update_ns_state_tracker.call_args_list, [NsState.INSTANTIATED]
+        )
+
+    async def test_instantiate_workflow__activity_failed__update_ns_state(
+        self,
+    ):
+        activities = [
+            mock_create_model_raises,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
         self.assert_ns_states(
             self.mock_update_ns_state_tracker.call_args_list, [NsState.INSTANTIATED]
         )
 
-    async def test_instantiate_workflow_with_exception_updates_lcm_operation(self):
-        async with self.env as env:
-            async with Worker(
-                env.client,
-                task_queue=LCM_TASK_QUEUE,
-                workflows=[NsInstantiateWorkflow, MockVnfInstantiateWorkflow],
-                activities=[
-                    mock_create_model_raises,
-                    mock_get_vnf_record_ids,
-                    self.mock_update_ns_state,
-                    self.mock_update_lcm_operation_state,
-                ],
-                debug_mode=True,
+    async def test_instantiate_workflow__activity_failed__update_lcm_op_state(
+        self,
+    ):
+        activities = [
+            mock_create_model_raises,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assert_lcm_op_states(
+            self.mock_update_lcm_operation_state_tracker.call_args_list,
+            [LcmOperationState.PROCESSING, LcmOperationState.FAILED],
+        )
+
+    async def test_instantiate_workflow__create_model_activity_failed__raise_activity_error(
+        self,
+    ):
+        activities = [
+            mock_create_model_raises,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError) as err:
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assertTrue(isinstance(err.exception.cause, ActivityError))
+
+    async def test_instantiate_workflow__create_model_activity_failed__error_details_expected(
+        self,
+    ):
+        activities = [
+            mock_create_model_raises,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assert_ns_error_details(
+            self.mock_update_ns_state_tracker.call_args_list,
+            ["TestException: create-model failed."],
+        )
+
+    async def test_instantiate_workflow__get_ns_record_activity_failed__error_details_expected(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record_raise_exception,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
             ):
-                with self.assertRaises(WorkflowFailureError) as e_info:
-                    await env.client.execute_workflow(
-                        NsInstantiateWorkflow,
-                        arg=self.input,
-                        id=self.input.nslcmop["nsInstanceId"],
-                        task_queue=LCM_TASK_QUEUE,
-                    )
-                self.assertEqual(e_info.exception.cause.cause.type, "TestException")
+                await self.execute_workflow()
+        self.assert_ns_error_details(
+            self.mock_update_ns_state_tracker.call_args_list,
+            ["TestException: get-ns-record failed."],
+        )
 
+    async def test_instantiate_workflow__get_ns_record_activity_failed__raise_activity_error(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record_raise_exception,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError) as err:
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assertTrue(isinstance(err.exception.cause, ActivityError))
+
+    async def test_instantiate_workflow__get_vnf_details_activity_failed__error_details_expected(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details_raises,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assert_ns_error_details(
+            self.mock_update_ns_state_tracker.call_args_list,
+            ["TestException: get-vnf-details failed."],
+        )
+
+    async def test_instantiate_workflow__get_vnf_details_activity_failed__raise_activity_error(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details_raises,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError) as err:
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assertTrue(isinstance(err.exception.cause, ActivityError))
+
+    @patch("osm_lcm.temporal.ns_workflows.NsInstantiateWorkflow.get_vnf_config")
+    async def test_instantiate_workflow__get_vnf_config_failed__workflow_times_out(
+        self, mock_get_vnf_config
+    ):
+        # Because of workflow task timeout policy, workflow times out
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        mock_get_vnf_config.side_effect = TestException("get_vnf_config failed.")
+        with self.assertRaises(WorkflowFailureError) as err:
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assertTrue(isinstance(err.exception.cause, TimeoutError))
+
+    @patch("osm_lcm.temporal.ns_workflows.NsInstantiateWorkflow.get_vnf_config")
+    async def test_instantiate_workflow__get_vnf_config_failed__update_ns_state(
+        self, mock_get_vnf_config
+    ):
+        # Workflow task failure
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        mock_get_vnf_config.side_effect = TestException("get_vnf_config failed.")
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
+        self.assert_ns_states(
+            self.mock_update_ns_state_tracker.call_args_list, [NsState.INSTANTIATED]
+        )
+
+    @patch("osm_lcm.temporal.ns_workflows.NsInstantiateWorkflow.get_vnf_config")
+    async def test_instantiate_workflow__get_vnf_config_failed__update_lcm_op_state(
+        self, mock_get_vnf_config
+    ):
+        # Workflow task failure
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        mock_get_vnf_config.side_effect = TestException("get_vnf_config failed.")
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue, self.workflows, activities
+            ):
+                await self.execute_workflow()
         self.assert_lcm_op_states(
             self.mock_update_lcm_operation_state_tracker.call_args_list,
             [LcmOperationState.PROCESSING, LcmOperationState.FAILED],
         )
 
-    async def test_instantiate_workflow_with_exception_updates_ns_state(self):
-        async with self.env as env:
-            async with Worker(
-                env.client,
-                task_queue=LCM_TASK_QUEUE,
-                workflows=[NsInstantiateWorkflow, MockVnfInstantiateWorkflow],
-                activities=[
-                    mock_create_model_raises,
-                    mock_get_vnf_record_ids,
-                    self.mock_update_ns_state,
-                    self.mock_update_lcm_operation_state,
-                ],
-                debug_mode=True,
+    async def test_instantiate_workflow__child_wf_failed__raise_child_workflow_error(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError) as err:
+            async with self.env, self.get_worker(
+                self.task_queue,
+                [NsInstantiateWorkflow, MockVnfInstantiateWorkflowFailed],
+                activities,
+            ):
+                await self.execute_workflow()
+        self.assertTrue(isinstance(err.exception.cause, ChildWorkflowError))
+
+    async def test_instantiate_workflow__child_wf_failed__error_details_expected(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue,
+                [NsInstantiateWorkflow, MockVnfInstantiateWorkflowFailed],
+                activities,
             ):
-                with self.assertRaises(WorkflowFailureError) as e_info:
-                    await env.client.execute_workflow(
-                        NsInstantiateWorkflow,
-                        arg=self.input,
-                        id=self.input.nslcmop["nsInstanceId"],
-                        task_queue=LCM_TASK_QUEUE,
-                    )
-                self.assertEqual(e_info.exception.cause.cause.type, "TestException")
+                await self.execute_workflow()
+        self.assert_ns_error_details(
+            self.mock_update_ns_state_tracker.call_args_list,
+            ["vnf-instantiate child workflow failed."],
+        )
+
+    async def test_instantiate_workflow__child_wf_failed__update_ns_state(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue,
+                [NsInstantiateWorkflow, MockVnfInstantiateWorkflowFailed],
+                activities,
+            ):
+                await self.execute_workflow()
         self.assert_ns_states(
             self.mock_update_ns_state_tracker.call_args_list, [NsState.INSTANTIATED]
         )
 
+    async def test_instantiate_workflow__child_wf_failed__update_lcm_op_state(
+        self,
+    ):
+        activities = [
+            mock_create_model,
+            mock_get_vnf_details,
+            mock_get_ns_record,
+            self.mock_update_ns_state,
+            self.mock_update_lcm_operation_state,
+        ]
+
+        with self.assertRaises(WorkflowFailureError):
+            async with self.env, self.get_worker(
+                self.task_queue,
+                [NsInstantiateWorkflow, MockVnfInstantiateWorkflowFailed],
+                activities,
+            ):
+                await self.execute_workflow()
+        self.assert_lcm_op_states(
+            self.mock_update_lcm_operation_state_tracker.call_args_list,
+            [LcmOperationState.PROCESSING, LcmOperationState.FAILED],
+        )
+
     def assert_ns_states(self, call_args_list, expected_states):
         """Asserts that the NS state was set to each of the expected states (in order)."""
         self.assertEqual(
             [call.args[0].state for call in call_args_list], expected_states
         )
 
+    def assert_ns_error_details(self, call_args_list, expected_error):
+        """Asserts that the NS state was set to each of the expected states (in order)."""
+        self.assertEqual(
+            [call.args[0].message for call in call_args_list], expected_error
+        )
+
     def assert_lcm_op_states(self, call_args_list, expected_states):
-        """Asserts that the LCM operation was set to each of the exepcted states (in order)."""
+        """Asserts that the LCM operation was set to each of the expected states (in order)."""
         self.assertEqual(
             [call.args[0].op_state for call in call_args_list], expected_states
         )
+
+
+class TestGetVnfConfig(asynctest.TestCase):
+    def test_vnf_config__nsr_config_matches_with_vnf__return_expected_config(self):
+        expected_config = {
+            "member-vnf-index": "vnf-profile-id",
+            "vdu": [
+                {
+                    "configurable-properties": {
+                        "config::redirect-map": "https://osm.instantiation.params"
+                    },
+                    "id": "test-vdu",
+                }
+            ],
+        }
+        result = NsInstantiateWorkflow.get_vnf_config(
+            vnf_member_index_ref_1, sample_nsr
+        )
+        self.assertEqual(result, expected_config)
+
+    def test_vnf_config__nsr_does_not_have_config__expected_no_config(self):
+        nsr = copy.deepcopy(sample_nsr)
+        nsr["instantiate_params"] = {}
+        result = NsInstantiateWorkflow.get_vnf_config(vnf_member_index_ref_1, nsr)
+        self.assertEqual(result, {})
+
+    def test_vnf_config__empty_vnf_member_index_ref__expected_no_config(self):
+        result = NsInstantiateWorkflow.get_vnf_config("", sample_nsr)
+        self.assertEqual(result, {})
+
+    def test_vnf_config__nsr_config_does_not_match_with_vnf__expected_no_config(self):
+        result = NsInstantiateWorkflow.get_vnf_config(
+            vnf_member_index_ref_2, sample_nsr
+        )
+        self.assertEqual(result, {})
index 22def4e..333f1de 100644 (file)
@@ -15,6 +15,7 @@
 # limitations under the License.
 
 import asynctest
+from datetime import timedelta
 
 from osm_common.dataclasses.temporal_dataclasses import (
     CharmInfo,
@@ -29,9 +30,19 @@ from osm_common.temporal_constants import (
 from osm_lcm.temporal.vdu_workflows import CheckCharmStatusInput, VduInstantiateWorkflow
 from temporalio import activity
 from temporalio.client import WorkflowFailureError
+from temporalio.exceptions import (
+    ActivityError,
+)
 from temporalio.testing import WorkflowEnvironment
 from temporalio.worker import Worker
 
+TASK_TIMEOUT = timedelta(seconds=0.5)
+EXECUTION_TIMEOUT = timedelta(seconds=1)
+
+
+class TestException(Exception):
+    pass
+
 
 @activity.defn(name=ACTIVITY_DEPLOY_CHARM)
 async def deploy_charm_mocked(deploy_charm_input: VduInstantiateInput) -> None:
@@ -40,7 +51,7 @@ async def deploy_charm_mocked(deploy_charm_input: VduInstantiateInput) -> None:
 
 @activity.defn(name=ACTIVITY_DEPLOY_CHARM)
 async def deploy_charm_mocked_raises(deploy_charm_input: VduInstantiateInput) -> None:
-    raise Exception()
+    raise TestException(f"{ACTIVITY_DEPLOY_CHARM} failed.")
 
 
 @activity.defn(name=ACTIVITY_CHECK_CHARM_STATUS)
@@ -52,7 +63,7 @@ async def check_charm_status_mocked(check_charm_status: CheckCharmStatusInput) -
 async def check_charm_status_mocked_raises(
     check_charm_status: CheckCharmStatusInput,
 ) -> None:
-    raise Exception()
+    raise TestException(f"{ACTIVITY_CHECK_CHARM_STATUS} failed.")
 
 
 class TestVduWorkflows(asynctest.TestCase):
@@ -63,13 +74,16 @@ class TestVduWorkflows(asynctest.TestCase):
     channel = "latest"
     entity_url = "ch:my-charm"
     cloud = "microk8s"
-    config = {}
+    config = {"domain_name1": "osm.org", "domain_name2": "osm.com"}
     constraints = VduComputeConstraints(mem=1, cores=1)
     charm_info = CharmInfo(app_name, channel, entity_url)
-    vdu_instantiate_input = VduInstantiateInput(
+    vdu_instantiate_input_without_config = VduInstantiateInput(
+        vim_id, namespace, charm_info, constraints, cloud, {}
+    )
+    vdu_instantiate_input_with_config = VduInstantiateInput(
         vim_id, namespace, charm_info, constraints, cloud, config
     )
-    worflow_id = namespace + "-" + app_name
+    workflow_id = namespace + "-" + app_name
 
     async def setUp(self):
         self.env = await WorkflowEnvironment.start_time_skipping()
@@ -84,37 +98,87 @@ class TestVduWorkflows(asynctest.TestCase):
             debug_mode=True,
         )
 
-    async def test_vdu_instantiate(self):
+    async def test_vdu_instantiate__without_config__successful(self):
         activities = [deploy_charm_mocked, check_charm_status_mocked]
         async with self.env, self.get_worker(activities):
-            result = await self.client.execute_workflow(
+            await self.client.execute_workflow(
                 VduInstantiateWorkflow.run,
-                arg=self.vdu_instantiate_input,
-                id=self.worflow_id,
+                arg=self.vdu_instantiate_input_without_config,
+                id=self.workflow_id,
                 task_queue=self.task_queue_name,
+                task_timeout=TASK_TIMEOUT,
+                execution_timeout=EXECUTION_TIMEOUT,
             )
-            self.assertIsNone(result)
 
-    async def test_vdu_instantiate_deploy_raises(self):
+    async def test_vdu_instantiate__with_config__successful(self):
+        activities = [deploy_charm_mocked, check_charm_status_mocked]
+        async with self.env, self.get_worker(activities):
+            await self.client.execute_workflow(
+                VduInstantiateWorkflow.run,
+                arg=self.vdu_instantiate_input_with_config,
+                id=self.workflow_id,
+                task_queue=self.task_queue_name,
+                task_timeout=TASK_TIMEOUT,
+                execution_timeout=EXECUTION_TIMEOUT,
+            )
+
+    async def test_vdu_instantiate__deploy_charm_failed__raise_activity_error(self):
+        activities = [deploy_charm_mocked_raises, check_charm_status_mocked]
+        async with self.env, self.get_worker(activities):
+            with self.assertRaises(WorkflowFailureError) as err:
+                await self.client.execute_workflow(
+                    VduInstantiateWorkflow.run,
+                    arg=self.vdu_instantiate_input_without_config,
+                    id=self.workflow_id,
+                    task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
+                )
+            self.assertTrue(err.exception.cause, ActivityError)
+
+    async def test_vdu_instantiate__deploy_charm_failed__get_error_details(self):
         activities = [deploy_charm_mocked_raises, check_charm_status_mocked]
         async with self.env, self.get_worker(activities):
-            with self.assertRaises(WorkflowFailureError):
-                result = await self.client.execute_workflow(
+            with self.assertRaises(WorkflowFailureError) as err:
+                await self.client.execute_workflow(
                     VduInstantiateWorkflow.run,
-                    arg=self.vdu_instantiate_input,
-                    id=self.worflow_id,
+                    arg=self.vdu_instantiate_input_without_config,
+                    id=self.workflow_id,
                     task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
                 )
-                self.assertIsNone(result)
+            self.assertEqual(
+                str(err.exception.cause.cause), "TestException: deploy-charm failed."
+            )
 
-    async def test_vdu_instantiate_check_status_raises(self):
+    async def test_vdu_instantiate__check_status__raise_activity_error(self):
         activities = [deploy_charm_mocked, check_charm_status_mocked_raises]
         async with self.env, self.get_worker(activities):
-            with self.assertRaises(WorkflowFailureError):
-                result = await self.client.execute_workflow(
+            with self.assertRaises(WorkflowFailureError) as err:
+                await self.client.execute_workflow(
                     VduInstantiateWorkflow.run,
-                    arg=self.vdu_instantiate_input,
-                    id=self.worflow_id,
+                    arg=self.vdu_instantiate_input_without_config,
+                    id=self.workflow_id,
                     task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
                 )
-                self.assertIsNone(result)
+            self.assertTrue(err.exception.cause, ActivityError)
+
+    async def test_vdu_instantiate__check_status__get_error_details(self):
+        activities = [deploy_charm_mocked, check_charm_status_mocked_raises]
+        async with self.env, self.get_worker(activities):
+            with self.assertRaises(WorkflowFailureError) as err:
+                await self.client.execute_workflow(
+                    VduInstantiateWorkflow.run,
+                    arg=self.vdu_instantiate_input_without_config,
+                    id=self.workflow_id,
+                    task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
+                )
+            self.assertEqual(
+                str(err.exception.cause.cause),
+                "TestException: check-charm-status failed.",
+            )
index 7bd6c65..8d68597 100644 (file)
@@ -16,7 +16,6 @@ import asyncio
 from unittest import TestCase
 from unittest.mock import Mock, patch, MagicMock
 
-
 from osm_common import msgbase
 from osm_common.dbbase import DbException
 from osm_lcm.vim_sdn import K8sClusterLcm, VcaLcm
index e48b94c..9c43895 100644 (file)
@@ -15,6 +15,9 @@
 # limitations under the License.
 
 import asynctest
+from datetime import timedelta
+from unittest.mock import Mock
+
 from osm_common.dataclasses.temporal_dataclasses import (
     DeleteVimInput,
     TestVimConnectivityInput,
@@ -44,7 +47,9 @@ from temporalio import activity
 from temporalio.client import WorkflowFailureError
 from temporalio.testing import WorkflowEnvironment
 from temporalio.worker import Worker
-from unittest.mock import Mock
+
+TASK_TIMEOUT = timedelta(seconds=0.5)
+EXECUTION_TIMEOUT = timedelta(seconds=1)
 
 
 class TestException(Exception):
@@ -160,6 +165,8 @@ class TestVimWorkflow(TestVimWorkflowsBase):
                 arg=self.vim_operation_input,
                 id=self.worflow_id,
                 task_queue=self.task_queue_name,
+                task_timeout=TASK_TIMEOUT,
+                execution_timeout=EXECUTION_TIMEOUT,
             )
         self.check_vim_state_is_updated(expected_vim_state)
         self.check_vim_op_state_is_updated(expected_vim_op_state)
@@ -180,6 +187,8 @@ class TestVimWorkflow(TestVimWorkflowsBase):
                     arg=self.vim_operation_input,
                     id=self.worflow_id,
                     task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
                 )
         self.check_vim_state_is_updated(expected_vim_state)
         self.check_vim_op_state_is_updated(expected_vim_op_state)
@@ -200,6 +209,8 @@ class TestVimWorkflow(TestVimWorkflowsBase):
                     arg=self.vim_operation_input,
                     id=self.worflow_id,
                     task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
                 )
         self.check_vim_state_is_updated(expected_vim_state)
         self.check_vim_op_state_is_updated(expected_vim_op_state)
@@ -221,6 +232,8 @@ class TestVimWorkflow(TestVimWorkflowsBase):
                     arg=self.vim_operation_input,
                     id=self.worflow_id,
                     task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
                 )
         self.check_vim_state_is_updated(expected_vim_state)
         self.check_vim_op_state_is_updated(expected_vim_op_state)
@@ -295,6 +308,8 @@ class TestVimDeleteWorkflow(TestVimWorkflowsBase):
                 arg=self.vim_operation_input,
                 id=self.worflow_id,
                 task_queue=self.task_queue_name,
+                task_timeout=TASK_TIMEOUT,
+                execution_timeout=EXECUTION_TIMEOUT,
             )
             self.assertIsNone(result)
         self.mock_delete_vim_record_tracker.assert_called_once()
@@ -308,5 +323,7 @@ class TestVimDeleteWorkflow(TestVimWorkflowsBase):
                     arg=self.vim_operation_input,
                     id=self.worflow_id,
                     task_queue=self.task_queue_name,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
                 )
                 self.assertIsNone(result)
index 02fad62..0c346ee 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
 import asynctest
-from asyncio.exceptions import CancelledError
 from copy import deepcopy
+from unittest import TestCase
+from unittest.mock import Mock, patch
+
 from osm_common.dataclasses.temporal_dataclasses import (
     ChangeVnfInstantiationStateInput,
     ChangeVnfStateInput,
     GetTaskQueueInput,
     GetVimCloudInput,
-    GetVnfDetailsInput,
-    VnfInstantiateInput,
+    GetVnfDescriptorInput,
+    GetVnfRecordInput,
+    SetVnfModelInput,
     VnfInstantiationState,
     VnfState,
+    VduComputeConstraints,
 )
 from osm_common.dbbase import DbException
 from osm_common.temporal_constants import (
@@ -34,29 +37,17 @@ from osm_common.temporal_constants import (
 )
 from osm_lcm.temporal.vnf_activities import VnfDbActivity, VnfOperations
 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)
+
+vnfr_uuid = "9f472177-95c0-4335-b357-5cdc17a79965"
+vnfd_uuid = "97784f19-d254-4252-946c-cf92d85443ca"
+vim_uuid = "a64f7c6c-bc27-4ec8-b664-5500a3324eca"
+model_name = "my-model-name"
+set_vnf_model_input = SetVnfModelInput(vnfr_uuid=vnfr_uuid, model_name=model_name)
 cloud = "microk8s"
-sample_vnfr = {
-    "_id": "9f472177-95c0-4335-b357-5cdc17a79965",
-    "id": "9f472177-95c0-4335-b357-5cdc17a79965",
-    "nsr-id-ref": "dcf4c922-5a73-41bf-a6ca-870c22d6336c",
-    "vnfd-ref": "jar_vnfd_scalable",
-    "vnfd-id": "f1b38eac-190c-485d-9a74-c6e169c929d8",
-    "vim-account-id": "9b0bedc3-ea8e-42fd-acc9-dd03f4dee73c",
-}
-sample_vnfd = {
-    "_id": "97784f19-d254-4252-946c-cf92d85443ca",
-    "id": "sol006-vnf",
-    "provider": "Canonical",
-    "product-name": "test-vnf",
-    "software-version": "1.0",
-}
+nsr_uuid = "583726d4-957d-47f5-8df5-199456f7afd0"
 sample_vim_record = {
-    "_id": "a64f7c6c-bc27-4ec8-b664-5500a3324eca",
+    "_id": vim_uuid,
     "name": "juju",
     "vim_type": "paas",
     "vim_url": "192.168.1.100:17070",
@@ -70,6 +61,72 @@ sample_vim_record = {
         "ca_cert_content": "-----BEGIN-----",
     },
 }
+vim_account_id = "9b0bedc3-ea8e-42fd-acc9-dd03f4dee73c"
+vdu_id = "hackfest_basic-VM"
+vnf_index = "vnf-profile-id"
+sample_vnfr = {
+    "_id": vnfr_uuid,
+    "id": vnfr_uuid,
+    "nsr-id-ref": "dcf4c922-5a73-41bf-a6ca-870c22d6336c",
+    "vnfd-ref": "jar_vnfd_scalable",
+    "vnfd-id": "f1b38eac-190c-485d-9a74-c6e169c929d8",
+    "vim-account-id": vim_account_id,
+    "namespace": model_name,
+    "member-vnf-index-ref": vnf_index,
+}
+vnf_config = {
+    "member-vnf-index": vnf_index,
+    "vdu": [
+        {
+            "id": vdu_id,
+            "configurable-properties": {
+                "config::redirect-map": "https://osm.instantiation.params"
+            },
+        }
+    ],
+}
+app_config = {"domain_name1": "osm.org", "domain_name2": "osm.com"}
+config = [
+    {"key": "config::domain_name1", "value": "osm.org"},
+    {"key": "config::domain_name2", "value": "osm.com"},
+    {"key": "track", "value": "latest"},
+    {"key": "channel", "value": "stable"},
+]
+vdu = {
+    "id": vdu_id,
+    "name": "hackfest_basic-VM",
+    "sw-image-desc": "ubuntu18.04",
+    "virtual-compute-desc": "hackfest_basic-VM-compute",
+    "virtual-storage-desc": ["hackfest_basic-VM-storage"],
+    "configurable-properties": config,
+}
+vnfd_id = "97784f19-d254-4252-946c-cf92d85443ca"
+flavor_1 = {
+    "id": "compute-id-1",
+    "virtual-memory": {"size": "4"},
+    "virtual-cpu": {"cpu-architecture": "x86", "num-virtual-cpu": 2},
+}
+flavor_2 = {
+    "id": "compute-id-2",
+    "virtual-cpu": {"cpu-architecture": "x86", "num-virtual-cpu": 2},
+}
+flavor_3 = {
+    "id": "compute-id-2",
+    "virtual-memory": {"size": "4"},
+}
+sample_vnfd = {
+    "_id": vnfd_id,
+    "id": "sol006-vnf",
+    "provider": "Canonical",
+    "product-name": "test-vnf",
+    "software-version": "1.0",
+    "vdu": [vdu],
+    "virtual-compute-desc": [flavor_1, flavor_2],
+}
+
+
+class TestException(Exception):
+    pass
 
 
 class TestVnfDbActivity(asynctest.TestCase):
@@ -78,7 +135,7 @@ class TestVnfDbActivity(asynctest.TestCase):
         self.env = ActivityEnvironment()
         self.vnf_db_activity = VnfDbActivity(self.db)
 
-    async def test_change_vnf_state_nominal_case(self):
+    async def test_change_vnf_state__successful__db_updated_as_expected(self):
         vnf_state = VnfState.STOPPED
         change_vnf_state_input = ChangeVnfStateInput(vnfr_uuid, vnf_state)
         await self.env.run(
@@ -88,7 +145,7 @@ class TestVnfDbActivity(asynctest.TestCase):
             "vnfrs", {"_id": vnfr_uuid}, {"vnfState": vnf_state.name}
         )
 
-    async def test_change_vnf_state_db_raises_exception(self):
+    async def test_change_vnf_state__failed__raises_db_exception(self):
         change_vnf_state_input = ChangeVnfStateInput(vnfr_uuid, VnfState.STARTED)
         self.db.set_one.side_effect = DbException("not found")
         with self.assertRaises(DbException):
@@ -96,7 +153,9 @@ class TestVnfDbActivity(asynctest.TestCase):
                 self.vnf_db_activity.change_vnf_state, change_vnf_state_input
             )
 
-    async def test_change_vnf_instantiation_state_nominal_case(self):
+    async def test_change_vnf_instantiation_state__successful__db_updated_as_expected(
+        self,
+    ):
         instantiation_state = VnfInstantiationState.NOT_INSTANTIATED
         change_instantiation_input = ChangeVnfInstantiationStateInput(
             vnfr_uuid, instantiation_state
@@ -111,28 +170,27 @@ class TestVnfDbActivity(asynctest.TestCase):
             {"instantiationState": instantiation_state.name},
         )
 
-    async def test_change_vnf_instantiation_state_db_raises_exception(self):
+    async def test_change_vnf_instantiation_state__failed__raises_db_exception(self):
         change_instantiation_input = ChangeVnfInstantiationStateInput(
             vnfr_uuid, VnfInstantiationState.INSTANTIATED
         )
         self.db.set_one.side_effect = DbException("not found")
         with self.assertRaises(DbException):
             await self.env.run(
-                self.vnf_db_activity.change_vnf_state, change_instantiation_input
+                self.vnf_db_activity.change_vnf_instantiation_state,
+                change_instantiation_input,
             )
 
-    async def test_set_vnf_model_nominal_case(self):
-        await self.env.run(self.vnf_db_activity.set_vnf_model, vnf_instantiate_input)
+    async def test_set_vnf_model__successful__db_updated_as_expected(self):
+        await self.env.run(self.vnf_db_activity.set_vnf_model, set_vnf_model_input)
         self.db.set_one.assert_called_with(
             "vnfrs", {"_id": vnfr_uuid}, {"namespace": model_name}
         )
 
-    async def test_set_vnf_model_db_raises_exception(self):
+    async def test_set_vnf_model__failed__raises_db_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
-            )
+            await self.env.run(self.vnf_db_activity.set_vnf_model, set_vnf_model_input)
 
 
 class TestGetTaskQueue(asynctest.TestCase):
@@ -142,7 +200,7 @@ class TestGetTaskQueue(asynctest.TestCase):
         self.env = ActivityEnvironment()
         self.get_task_queue_input = GetTaskQueueInput(vnfr_uuid=sample_vnfr["id"])
 
-    async def test_activity_succeeded(self):
+    async def test_activity__succeeded__get_expected_task_queue(self):
         self.db.get_one.side_effect = [sample_vnfr, sample_vim_record]
         activity_result = await self.env.run(
             self.vnf_operations_instance.get_task_queue,
@@ -150,7 +208,7 @@ class TestGetTaskQueue(asynctest.TestCase):
         )
         self.assertEqual(activity_result.task_queue, LCM_TASK_QUEUE)
 
-    async def test_db_raises_exception(self):
+    async def test_activity__failed__raises_db_exception(self):
         self.db.get_one.side_effect = DbException("not found")
         with self.assertRaises(DbException):
             await self.env.run(
@@ -158,7 +216,7 @@ class TestGetTaskQueue(asynctest.TestCase):
                 self.get_task_queue_input,
             )
 
-    async def test_invalid_task_queue(self):
+    async def test_activity__invalid_task_queue__raises_key_error(self):
         vim_record = deepcopy(sample_vim_record)
         vim_record["vim_type"] = "some-vim-type"
         self.db.get_one.side_effect = [sample_vnfr, vim_record]
@@ -168,65 +226,51 @@ class TestGetTaskQueue(asynctest.TestCase):
                 self.get_task_queue_input,
             )
 
-    async def test_activity_cancelled(self):
-        self.env._cancelled = True
-        self.db.get_one.side_effect = [sample_vnfr, sample_vim_record]
-        with self.assertRaises(CancelledError):
-            activity_result = await self.env.run(
-                self.vnf_operations_instance.get_task_queue,
-                self.get_task_queue_input,
-            )
-            self.assertEqual(activity_result, None)
-
 
-class TestVnfDetails(asynctest.TestCase):
+class TestGetVnfDescriptor(asynctest.TestCase):
     async def setUp(self):
         self.db = Mock()
         self.vnf_operations_instance = VnfOperations(self.db)
         self.env = ActivityEnvironment()
 
-    async def test_activity_succeeded(self):
-        self.db.get_one.side_effect = [sample_vnfr, sample_vnfd]
+    async def test_activity__succeeded__get_expected_vnfd(self):
+        self.db.get_one.return_value = sample_vnfd
         activity_result = await self.env.run(
-            self.vnf_operations_instance.get_vnf_details,
-            GetVnfDetailsInput(vnfr_uuid=sample_vnfr["id"]),
+            self.vnf_operations_instance.get_vnf_descriptor,
+            GetVnfDescriptorInput(vnfd_uuid=vnfd_uuid),
         )
         self.assertEqual(activity_result.vnfd, sample_vnfd)
-        self.assertEqual(activity_result.vnfr, sample_vnfr)
 
-    async def test_activity_failed_db_exception(self):
+    async def test_activity__failed__raises_db_exception(self):
         self.db.get_one.side_effect = DbException("Can not connect to Database.")
-        with self.assertRaises(DbException) as err:
-            activity_result = await self.env.run(
-                self.vnf_operations_instance.get_vnf_details,
-                GetVnfDetailsInput(vnfr_uuid=sample_vnfr["id"]),
+        with self.assertRaises(DbException):
+            await self.env.run(
+                self.vnf_operations_instance.get_vnf_descriptor,
+                GetVnfDescriptorInput(vnfd_uuid=vnfd_uuid),
             )
-            self.assertEqual(activity_result, None)
-        self.assertEqual(
-            str(err.exception), "database exception Can not connect to Database."
+
+
+class TestGetVnfRecord(asynctest.TestCase):
+    async def setUp(self):
+        self.db = Mock()
+        self.vnf_operations_instance = VnfOperations(self.db)
+        self.env = ActivityEnvironment()
+
+    async def test_activity__succeeded__get_expected_vnfr(self):
+        self.db.get_one.return_value = sample_vnfr
+        activity_result = await self.env.run(
+            self.vnf_operations_instance.get_vnf_record,
+            GetVnfRecordInput(vnfr_uuid=vnfr_uuid),
         )
+        self.assertEqual(activity_result.vnfr, sample_vnfr)
 
-    async def test_activity_failed_key_error(self):
-        vnfr = deepcopy(sample_vnfr)
-        vnfr.pop("vnfd-id")
-        self.db.get_one.side_effect = [vnfr, sample_vnfd]
-        with self.assertRaises(KeyError) as err:
-            activity_result = await self.env.run(
-                self.vnf_operations_instance.get_vnf_details,
-                GetVnfDetailsInput(vnfr_uuid=sample_vnfr["id"]),
-            )
-            self.assertEqual(activity_result, None)
-        self.assertEqual(str(err.exception.args[0]), "vnfd-id")
-
-    async def test_activity_cancelled(self):
-        self.env._cancelled = True
-        self.db.get_one.side_effect = [sample_vnfr, sample_vnfd]
-        with self.assertRaises(CancelledError):
-            activity_result = await self.env.run(
-                self.vnf_operations_instance.get_vnf_details,
-                GetVnfDetailsInput(vnfr_uuid=sample_vnfr["id"]),
+    async def test_activity__failed__raise_db_exception(self):
+        self.db.get_one.side_effect = DbException("Can not connect to Database.")
+        with self.assertRaises(DbException):
+            await self.env.run(
+                self.vnf_operations_instance.get_vnf_record,
+                GetVnfRecordInput(vnfr_uuid=vnfr_uuid),
             )
-            self.assertEqual(activity_result, None)
 
 
 class TestGetVimCloud(asynctest.TestCase):
@@ -235,7 +279,7 @@ class TestGetVimCloud(asynctest.TestCase):
         self.vnf_operations_instance = VnfOperations(self.db)
         self.env = ActivityEnvironment()
 
-    async def test_activity_succeeded(self):
+    async def test_activity__succeeded__get_vim_cloud(self):
         self.db.get_one.side_effect = [sample_vnfr, sample_vim_record]
         activity_result = await self.env.run(
             self.vnf_operations_instance.get_vim_cloud,
@@ -243,7 +287,7 @@ class TestGetVimCloud(asynctest.TestCase):
         )
         self.assertEqual(activity_result.cloud, cloud)
 
-    async def test_activity_vim_record_without_cloud(self):
+    async def test_activity__vim_record_without_cloud__no_cloud_info(self):
         vim_record = deepcopy(sample_vim_record)
         vim_record["config"].pop("cloud")
         self.db.get_one.side_effect = [sample_vnfr, vim_record]
@@ -253,39 +297,205 @@ class TestGetVimCloud(asynctest.TestCase):
         )
         self.assertEqual(activity_result.cloud, "")
 
-    async def test_activity_failed_db_exception(self):
+    async def test_activity__failed__raise_db_exception(self):
         self.db.get_one.side_effect = DbException("Can not connect to Database.")
-        with self.assertRaises(DbException) as err:
-            activity_result = await self.env.run(
+        with self.assertRaises(DbException):
+            await self.env.run(
                 self.vnf_operations_instance.get_vim_cloud,
                 GetVimCloudInput(vnfr_uuid=sample_vnfr["id"]),
             )
-            self.assertEqual(activity_result, None)
-        self.assertEqual(
-            str(err.exception), "database exception Can not connect to Database."
-        )
 
-    async def test_activity_failed_key_error(self):
+    async def test_activity__wrong_vim_record__raise_key_error(self):
         vim_record = deepcopy(sample_vim_record)
         vim_record.pop("config")
         self.db.get_one.side_effect = [sample_vnfr, vim_record]
-        with self.assertRaises(KeyError) as err:
-            activity_result = await self.env.run(
+        with self.assertRaises(KeyError):
+            await self.env.run(
                 self.vnf_operations_instance.get_vim_cloud,
                 GetVimCloudInput(vnfr_uuid=sample_vnfr["id"]),
             )
-            self.assertEqual(activity_result, None)
-        self.assertEqual(str(err.exception.args[0]), "config")
 
-    async def test_activity_cancelled(self):
-        self.env._cancelled = True
-        self.db.get_one.side_effect = [sample_vnfr, sample_vim_record]
-        with self.assertRaises(CancelledError):
-            activity_result = await self.env.run(
-                self.vnf_operations_instance.get_vim_cloud,
-                GetVimCloudInput(vnfr_uuid=sample_vnfr["id"]),
-            )
-            self.assertEqual(activity_result, None)
+
+class TestGetVduInstantiateInfoMethods(TestCase):
+    def test_get_flavor_details__successful__get_flavor(self):
+        compute_desc_id = "compute-id-1"
+        result = VnfOperations._get_flavor_details(compute_desc_id, sample_vnfd)
+        self.assertEqual(result, flavor_1)
+
+    def test_get_flavor_details__empty_compute_desc__flavor_is_none(self):
+        compute_desc_id = ""
+        result = VnfOperations._get_flavor_details(compute_desc_id, sample_vnfd)
+        self.assertEqual(result, None)
+
+    def test_get_flavor_details__compute_desc_not_found__flavor_is_none(self):
+        compute_desc_id = "compute-id-5"
+        result = VnfOperations._get_flavor_details(compute_desc_id, sample_vnfd)
+        self.assertEqual(result, None)
+
+    def test_get_flavor_details__empty_vnfd__flavor_is_none(self):
+        compute_desc_id = "compute-id-5"
+        result = VnfOperations._get_flavor_details(compute_desc_id, {})
+        self.assertEqual(result, None)
+
+    def test_get_flavor_details__wrong_vnfd_format__flavor_is_none(self):
+        compute_desc_id = "compute-id-2"
+        sample_vnfd = {
+            "_id": vnfd_id,
+            "vdu": [vdu],
+            "virtual-compute-desc": [
+                {
+                    "virtual-memory": {"size": "4"},
+                }
+            ],
+        }
+        result = VnfOperations._get_flavor_details(compute_desc_id, sample_vnfd)
+        self.assertEqual(result, None)
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_flavor_details")
+    def test_get_compute_constraints__succeeded__get_constraints(
+        self, mock_get_flavor_details
+    ):
+        mock_get_flavor_details.return_value = flavor_1
+        result = VnfOperations.get_compute_constraints(vdu, sample_vnfd)
+        self.assertEqual(VduComputeConstraints(cores=2, mem=4), result)
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_flavor_details")
+    def test_get_compute_constraints__empty_flavor_details__no_constraints(
+        self, mock_get_flavor_details
+    ):
+        mock_get_flavor_details.return_value = {}
+        result = VnfOperations.get_compute_constraints(vdu, sample_vnfd)
+        self.assertEqual(VduComputeConstraints(cores=0, mem=0), result)
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_flavor_details")
+    def test_get_compute_constraints__flavor_details_is_none__no_constraints(
+        self, mock_get_flavor_details
+    ):
+        mock_get_flavor_details.return_value = None
+        result = VnfOperations.get_compute_constraints(vdu, sample_vnfd)
+        self.assertEqual(VduComputeConstraints(cores=0, mem=0), result)
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_flavor_details")
+    def test_get_compute_constraints__flavor_has_only_cpu__get_cpu_constraint(
+        self, mock_get_flavor_details
+    ):
+        mock_get_flavor_details.return_value = flavor_2
+        result = VnfOperations.get_compute_constraints(vdu, sample_vnfd)
+        self.assertEqual(VduComputeConstraints(cores=2, mem=0), result)
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_flavor_details")
+    def test_get_compute_constraints__flavor_has_only_memory__get_memory_constraint(
+        self, mock_get_flavor_details
+    ):
+        mock_get_flavor_details.return_value = flavor_3
+        result = VnfOperations.get_compute_constraints(vdu, sample_vnfd)
+        self.assertEqual(VduComputeConstraints(cores=0, mem=4), result)
+
+    def test_list_to_dict__config_has_several_items__get_expected_dict(self):
+        config = [
+            {"key": "config::domain_name1", "value": "osm.org"},
+            {"key": "domain_name2", "value": "osm.com"},
+        ]
+        result = VnfOperations._list_to_dict(config)
+        self.assertEqual(
+            result, {"config::domain_name1": "osm.org", "domain_name2": "osm.com"}
+        )
+
+    def test_list_to_dict__empty_input__get_empty_dict(self):
+        config = []
+        result = VnfOperations._list_to_dict(config)
+        self.assertEqual(result, {})
+
+    def test_get_only_config_items__with_identifier__get_expected_config(self):
+        config = {"config::redirect-map": "https://osm.instantiation.params"}
+        result = VnfOperations._get_only_config_items(config)
+        self.assertEqual(result, {"redirect-map": "https://osm.instantiation.params"})
+
+    def test_get_only_config_items__without_identifier__no_config(self):
+        config = {"key": "domain_name1", "value": "osm.org"}
+        result = VnfOperations._get_only_config_items(config)
+        self.assertEqual(result, {})
+
+    def test_get_only_config_items__empty_input__no_config(self):
+        config = {}
+        result = VnfOperations._get_only_config_items(config)
+        self.assertEqual(result, {})
+
+    def test_get_vdu_instantiation_params__empty_vnf_config__get_params(self):
+        result = VnfOperations.get_vdu_instantiation_params(vdu_id, vnf_config)
+        self.assertEqual(
+            result, {"config::redirect-map": "https://osm.instantiation.params"}
+        )
+
+    def test_get_vdu_instantiation_params__empty_vnf_config__no_params(self):
+        result = VnfOperations.get_vdu_instantiation_params(vdu_id, {})
+        self.assertEqual(result, {})
+
+    def test_get_vdu_instantiation_params__vdu_id_mismatch__no_params(self):
+        config = deepcopy(vnf_config)
+        config["vdu"][0]["id"] = "other-vdu-id"
+        result = VnfOperations.get_vdu_instantiation_params(vdu_id, config)
+        self.assertEqual(result, {})
+
+    def test_get_vdu_instantiation_params__empty_configurable_properties__no_params(
+        self,
+    ):
+        config = deepcopy(vnf_config)
+        config["vdu"][0]["configurable-properties"] = {}
+        result = VnfOperations.get_vdu_instantiation_params(vdu_id, config)
+        self.assertEqual(result, {})
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_only_config_items")
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._list_to_dict")
+    def test_get_application_config__instantiate_config_overrides_vdu_config__get_expected_application_config(
+        self,
+        mock_list_to_dict,
+        mock_get_only_config_items,
+    ):
+        vdu_instantiate_config = {"config::domain_name1": "osm.public"}
+        mock_get_only_config_items.side_effect = [
+            {
+                "domain_name1": "osm.org",
+                "domain_name2": "osm.com",
+            },
+            {"domain_name1": "osm.public"},
+        ]
+        result = VnfOperations.get_application_config(vdu, vdu_instantiate_config)
+        self.assertEqual(
+            result, {"domain_name1": "osm.public", "domain_name2": "osm.com"}
+        )
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_only_config_items")
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._list_to_dict")
+    def test_get_application_config__empty_instantiate_config__get_descriptor_config(
+        self,
+        mock_list_to_dict,
+        mock_get_only_config_items,
+    ):
+        vdu_instantiate_config = {}
+        mock_get_only_config_items.side_effect = [
+            {
+                "domain_name1": "osm.org",
+                "domain_name2": "osm.com",
+            },
+            {},
+        ]
+        result = VnfOperations.get_application_config(vdu, vdu_instantiate_config)
+        self.assertEqual(result, app_config)
+
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._get_only_config_items")
+    @patch("osm_lcm.temporal.vnf_activities.VnfOperations._list_to_dict")
+    def test_get_application_config__no_config__empty_application_config(
+        self,
+        mock_list_to_dict,
+        mock_get_only_config_items,
+    ):
+        vdu_instantiate_config = {}
+        vdu_copy = deepcopy(vdu)
+        vdu_copy["configurable-properties"] = []
+        mock_get_only_config_items.side_effect = [{}, {}]
+        result = VnfOperations.get_application_config(vdu_copy, vdu_instantiate_config)
+        self.assertEqual(result, {})
 
 
 if __name__ == "__main__":
index 073b767..8601142 100644 (file)
 # limitations under the License.
 
 import asynctest
+from asynctest.mock import patch
 from copy import deepcopy
-from temporalio import activity
-from temporalio import workflow
-from temporalio.client import WorkflowFailureError
-from temporalio.testing import WorkflowEnvironment
-from temporalio.worker import Worker
-from unittest import TestCase
-from unittest.mock import Mock, patch, AsyncMock
+from datetime import timedelta
+from unittest.mock import Mock
+import uuid
 
 from osm_common.dataclasses.temporal_dataclasses import (
     ChangeVnfInstantiationStateInput,
@@ -32,12 +29,16 @@ from osm_common.dataclasses.temporal_dataclasses import (
     GetTaskQueueOutput,
     GetVimCloudInput,
     GetVimCloudOutput,
-    GetVnfDetailsInput,
-    GetVnfDetailsOutput,
+    GetVnfDescriptorInput,
+    GetVnfDescriptorOutput,
+    GetVnfRecordInput,
+    GetVnfRecordOutput,
+    SetVnfModelInput,
     VduComputeConstraints,
     VduInstantiateInput,
     VnfInstantiateInput,
     VnfInstantiationState,
+    VnfPrepareInput,
     VnfState,
 )
 from osm_common.temporal_constants import (
@@ -45,27 +46,35 @@ from osm_common.temporal_constants import (
     ACTIVITY_CHANGE_VNF_INSTANTIATION_STATE,
     ACTIVITY_GET_TASK_QUEUE,
     ACTIVITY_GET_VIM_CLOUD,
-    ACTIVITY_GET_VNF_DETAILS,
+    ACTIVITY_GET_VNF_DESCRIPTOR,
+    ACTIVITY_GET_VNF_RECORD,
     ACTIVITY_SEND_NOTIFICATION_FOR_VNF,
     ACTIVITY_SET_VNF_MODEL,
     LCM_TASK_QUEUE,
     WORKFLOW_VDU_INSTANTIATE,
     WORKFLOW_VNF_PREPARE,
 )
-
 from osm_lcm.temporal.vnf_workflows import VnfInstantiateWorkflow, VnfPrepareWorkflow
+from temporalio import activity
+from temporalio import workflow
+from temporalio.client import WorkflowFailureError
+from temporalio.exceptions import (
+    ActivityError,
+    ChildWorkflowError,
+    RetryState,
+)
+from temporalio.testing import WorkflowEnvironment
+from temporalio.worker import Worker
+
 
 # The variables used in the tests
 model_name = "my-model-name"
-wf_input = VnfInstantiateInput(
-    vnfr_uuid="86b53d92-4f5a-402e-8ac2-585ec6b64698",
-    model_name=model_name,
-)
 cloud = "microk8s"
-config = {}
 juju_task_queue = "juju_task_queue"
 vnfr_uuid = "9f472177-95c0-4335-b357-5cdc17a79965"
 vim_account_id = "9b0bedc3-ea8e-42fd-acc9-dd03f4dee73c"
+vdu_id = "hackfest_basic-VM"
+vnf_index = "vnf-profile-id"
 sample_vnfr = {
     "_id": vnfr_uuid,
     "id": vnfr_uuid,
@@ -74,13 +83,38 @@ sample_vnfr = {
     "vnfd-id": "f1b38eac-190c-485d-9a74-c6e169c929d8",
     "vim-account-id": vim_account_id,
     "namespace": model_name,
+    "member-vnf-index-ref": vnf_index,
+}
+vnf_config = {
+    "member-vnf-index": vnf_index,
+    "vdu": [
+        {
+            "id": vdu_id,
+            "configurable-properties": {
+                "config::redirect-map": "https://osm.instantiation.params"
+            },
+        }
+    ],
 }
+app_config = {"domain_name1": "osm.org", "domain_name2": "osm.com"}
+config = [
+    {"key": "config::domain_name1", "value": "osm.org"},
+    {"key": "config::domain_name2", "value": "osm.com"},
+    {"key": "track", "value": "latest"},
+    {"key": "channel", "value": "stable"},
+]
+wf_input = VnfInstantiateInput(
+    vnfr_uuid=vnfr_uuid,
+    model_name=model_name,
+    instantiation_config={},
+)
 vdu = {
-    "id": "hackfest_basic-VM",
+    "id": vdu_id,
     "name": "hackfest_basic-VM",
-    "sw-image-desc": "ubuntu18.04",
+    "sw-image-desc": "ubuntu20.04",
     "virtual-compute-desc": "hackfest_basic-VM-compute",
     "virtual-storage-desc": ["hackfest_basic-VM-storage"],
+    "configurable-properties": config,
 }
 vnfd_id = "97784f19-d254-4252-946c-cf92d85443ca"
 flavor_1 = {
@@ -92,10 +126,6 @@ flavor_2 = {
     "id": "compute-id-2",
     "virtual-cpu": {"cpu-architecture": "x86", "num-virtual-cpu": 2},
 }
-flavor_3 = {
-    "id": "compute-id-2",
-    "virtual-memory": {"size": "4"},
-}
 sample_vnfd = {
     "_id": vnfd_id,
     "id": "sol006-vnf",
@@ -104,6 +134,14 @@ sample_vnfd = {
     "software-version": "1.0",
     "vdu": [vdu],
     "virtual-compute-desc": [flavor_1, flavor_2],
+    "sw-image-desc": [
+        {
+            "id": "ubuntu20.04",
+            "image": "ubuntu20.04",
+            "name": "ubuntu20.04",
+            "version": "1.0",
+        }
+    ],
 }
 sample_charm_info = CharmInfo(app_name="my-app", channel="latest", entity_url="my-url")
 sample_constraints = VduComputeConstraints(cores=1, mem=1)
@@ -113,25 +151,53 @@ sample_vdu_instantiate_input = VduInstantiateInput(
     charm_info=sample_charm_info,
     constraints=sample_constraints,
     cloud=cloud,
-    config=config,
+    config={},
 )
+sample_vdu_instantiate_wf_id_1 = "vdu-instantiate-workflow-id-1"
+sample_vdu_instantiate_input_with_config = VduInstantiateInput(
+    vim_uuid=vim_account_id,
+    model_name=model_name,
+    charm_info=sample_charm_info,
+    constraints=sample_constraints,
+    cloud=cloud,
+    config=app_config,
+)
+
 SANDBOXED = False
 DEBUG_MODE = True
+TASK_TIMEOUT = timedelta(seconds=0.5)
+EXECUTION_TIMEOUT = timedelta(seconds=1)
 
 
 class TestException(Exception):
     pass
 
 
+class GetApplicationConfigException(Exception):
+    pass
+
+
+class GetVduInstantiationParamsException(Exception):
+    pass
+
+
+class GetCharmInfoException(Exception):
+    pass
+
+
+class GetComputeConstraintsException(Exception):
+    pass
+
+
 # Mock Activities
 @activity.defn(name=ACTIVITY_SET_VNF_MODEL)
-async def mock_set_vnf_model(set_vnf_model_input: VnfInstantiateInput) -> None:
+async def mock_set_vnf_model(set_vnf_model_input: SetVnfModelInput) -> None:
     pass
 
 
 @activity.defn(name=ACTIVITY_SET_VNF_MODEL)
 async def mock_set_vnf_model_raise_exception(
-    set_vnf_model_input: VnfInstantiateInput,
+    set_vnf_model_input: SetVnfModelInput,
 ) -> None:
     raise TestException(f"{ACTIVITY_SET_VNF_MODEL} failed.")
 
@@ -157,25 +223,39 @@ async def mock_get_task_queue_raise_exception(
     raise TestException(f"{ACTIVITY_GET_TASK_QUEUE} failed.")
 
 
-@activity.defn(name=ACTIVITY_GET_VNF_DETAILS)
-async def mock_get_vnf_details(
-    get_vnf_details_input: GetVnfDetailsInput,
-) -> GetVnfDetailsOutput:
-    return GetVnfDetailsOutput(vnfr=sample_vnfr, vnfd=sample_vnfd)
+@activity.defn(name=ACTIVITY_GET_VNF_DESCRIPTOR)
+async def mock_get_vnf_descriptor(
+    get_vnf_descriptor_input: GetVnfDescriptorInput,
+) -> GetVnfDescriptorOutput:
+    return GetVnfDescriptorOutput(vnfd=sample_vnfd)
 
 
-@activity.defn(name=ACTIVITY_GET_VNF_DETAILS)
-async def mock_get_vnf_details_empty_output(
-    get_vnf_details_input: GetVnfDetailsInput,
-) -> GetVnfDetailsOutput:
-    return GetVnfDetailsOutput(vnfr={}, vnfd={})
+@activity.defn(name=ACTIVITY_GET_VNF_DESCRIPTOR)
+async def mock_get_vnf_descriptor_empty_output(
+    get_vnf_descriptor_input: GetVnfDescriptorInput,
+) -> GetVnfDescriptorOutput:
+    return GetVnfDescriptorOutput(vnfd={})
 
 
-@activity.defn(name=ACTIVITY_GET_VNF_DETAILS)
-async def mock_get_vnf_details_raise_exception(
-    get_vnf_details_input: GetVnfDetailsInput,
-) -> GetVnfDetailsOutput:
-    raise TestException(f"{ACTIVITY_GET_VNF_DETAILS} failed.")
+@activity.defn(name=ACTIVITY_GET_VNF_DESCRIPTOR)
+async def mock_get_vnf_descriptor_raise_exception(
+    get_vnf_descriptor_input: GetVnfDescriptorInput,
+) -> GetVnfDescriptorOutput:
+    raise TestException(f"{ACTIVITY_GET_VNF_DESCRIPTOR} failed.")
+
+
+@activity.defn(name=ACTIVITY_GET_VNF_RECORD)
+async def mock_get_vnf_record(
+    get_vnf_record_input: GetVnfRecordInput,
+) -> GetVnfRecordOutput:
+    return GetVnfRecordOutput(vnfr=sample_vnfr)
+
+
+@activity.defn(name=ACTIVITY_GET_VNF_RECORD)
+async def mock_get_vnf_record_raise_exception(
+    get_vnf_record_input: GetVnfRecordInput,
+) -> GetVnfRecordOutput:
+    raise TestException(f"{ACTIVITY_GET_VNF_RECORD} failed.")
 
 
 @activity.defn(name=ACTIVITY_GET_VIM_CLOUD)
@@ -192,14 +272,30 @@ async def mock_get_vim_cloud_raise_exception(
     raise TestException(f"{ACTIVITY_GET_VIM_CLOUD} failed.")
 
 
-# Mock Workflowa
+# Mock Workflows
 @workflow.defn(name=WORKFLOW_VNF_PREPARE, sandboxed=SANDBOXED)
 class MockPrepareVnfWorkflow:
     @workflow.run
-    async def run(self, input: VnfInstantiateInput) -> None:
+    async def run(self, input: VnfPrepareInput) -> None:
         pass
 
 
+@workflow.defn(name=WORKFLOW_VNF_PREPARE, sandboxed=SANDBOXED)
+class MockPrepareVnfWorkflowFailed:
+    @workflow.run
+    async def run(self, input: VnfPrepareInput) -> None:
+        raise ChildWorkflowError(
+            message=f"{WORKFLOW_VNF_PREPARE} child workflow failed.",
+            namespace="default",
+            workflow_id="123",
+            run_id="1",
+            workflow_type=WORKFLOW_VNF_PREPARE,
+            initiated_event_id=0,
+            started_event_id=0,
+            retry_state=RetryState.NON_RETRYABLE_FAILURE,
+        )
+
+
 @workflow.defn(name=WORKFLOW_VDU_INSTANTIATE, sandboxed=SANDBOXED)
 class MockVduInstantiateWorkflow:
     @workflow.run
@@ -207,13 +303,29 @@ class MockVduInstantiateWorkflow:
         pass
 
 
+@workflow.defn(name=WORKFLOW_VDU_INSTANTIATE, sandboxed=SANDBOXED)
+class MockVduInstantiateWorkflowFailed:
+    @workflow.run
+    async def run(self, input: VduInstantiateInput) -> None:
+        raise ChildWorkflowError(
+            message=f"{WORKFLOW_VDU_INSTANTIATE} child workflow failed.",
+            namespace="default",
+            workflow_id="123",
+            run_id="1",
+            workflow_type=WORKFLOW_VDU_INSTANTIATE,
+            initiated_event_id=0,
+            started_event_id=0,
+            retry_state=RetryState.NON_RETRYABLE_FAILURE,
+        )
+
+
 class TestVnfPrepareWorkflow(asynctest.TestCase):
     async def setUp(self):
         self.env = await WorkflowEnvironment.start_time_skipping()
         self.client = self.env.client
         self.task_queue = LCM_TASK_QUEUE
 
-    async def test_vnf_prepare_workflow_successful(self):
+    async def test_vnf_prepare_workflow__successful(self):
         async with self.env:
             async with Worker(
                 self.client,
@@ -227,9 +339,35 @@ class TestVnfPrepareWorkflow(asynctest.TestCase):
                     arg=wf_input,
                     id=f"{WORKFLOW_VNF_PREPARE}-{wf_input.vnfr_uuid}",
                     task_queue=self.task_queue,
+                    task_timeout=TASK_TIMEOUT,
+                    execution_timeout=EXECUTION_TIMEOUT,
                 )
 
-    async def test_vnf_prepare_workflow_fails(self):
+    async def test_vnf_prepare_workflow__activity_set_vnf_model_fails__raise_activity_error(
+        self,
+    ):
+        async with self.env:
+            async with Worker(
+                self.client,
+                debug_mode=DEBUG_MODE,
+                task_queue=self.task_queue,
+                workflows=[VnfPrepareWorkflow],
+                activities=[mock_set_vnf_model_raise_exception],
+            ):
+                with self.assertRaises(WorkflowFailureError) as err:
+                    await self.client.execute_workflow(
+                        VnfPrepareWorkflow.run,
+                        arg=wf_input,
+                        id=f"{WORKFLOW_VNF_PREPARE}-{wf_input.vnfr_uuid}",
+                        task_queue=self.task_queue,
+                        task_timeout=TASK_TIMEOUT,
+                        execution_timeout=EXECUTION_TIMEOUT,
+                    )
+        self.assertTrue(isinstance(err.exception.cause, ActivityError))
+
+    async def test_vnf_prepare_workflow__activity_set_vnf_model_fails__get_expected_error_details(
+        self,
+    ):
         async with self.env:
             async with Worker(
                 self.client,
@@ -244,11 +382,11 @@ class TestVnfPrepareWorkflow(asynctest.TestCase):
                         arg=wf_input,
                         id=f"{WORKFLOW_VNF_PREPARE}-{wf_input.vnfr_uuid}",
                         task_queue=self.task_queue,
+                        task_timeout=TASK_TIMEOUT,
+                        execution_timeout=EXECUTION_TIMEOUT,
                     )
-        self.assertEqual(str(err.exception), "Workflow execution failed")
         self.assertEqual(
-            str(err.exception.cause.cause.message),
-            "set-vnf-model failed.",
+            str(err.exception.cause.cause), "TestException: set-vnf-model failed."
         )
 
 
@@ -275,55 +413,33 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase):
             debug_mode=DEBUG_MODE,
         )
 
-    async def execute_workflow(self):
+    async def execute_workflow(self, wf_input=wf_input):
         return await self.client.execute_workflow(
             VnfInstantiateWorkflow.run,
-            arg=VnfInstantiateInput(vnfr_uuid=vnfr_uuid, model_name="a-model-name"),
-            id="wf1",
+            arg=wf_input,
+            id=uuid.uuid4().hex,
             task_queue=self.task_queue,
+            task_timeout=TASK_TIMEOUT,
+            execution_timeout=EXECUTION_TIMEOUT,
         )
 
-    def check_state_change_call_counts(self):
-        self.assertEqual(self.mock_change_vnf_instantiation_state_tracker.call_count, 2)
-        self.assertEqual(self.mock_change_vnf_state_tracker.call_count, 1)
-        self.assertEqual(self.mock_send_notification_for_vnf_tracker.call_count, 2)
-
-    def workflow_is_succeeded(self):
+    def vnf_instantiation_state_is_updated(self, state):
         call_mock_change_vnf_instantiation_state_tracker = (
             self.mock_change_vnf_instantiation_state_tracker.call_args_list
         )
-        call_mock_change_vnf_state_tracker = (
-            self.mock_change_vnf_state_tracker.call_args_list
-        )
         self.assertEqual(
             call_mock_change_vnf_instantiation_state_tracker[1].args[0],
-            ChangeVnfInstantiationStateInput(
-                vnfr_uuid=vnfr_uuid, state=VnfInstantiationState.INSTANTIATED
-            ),
-        )
-        self.assertEqual(
-            call_mock_change_vnf_state_tracker[0].args[0],
-            ChangeVnfStateInput(vnfr_uuid=vnfr_uuid, state=VnfState.STARTED),
+            ChangeVnfInstantiationStateInput(vnfr_uuid=vnfr_uuid, state=state),
         )
 
-    def workflow_is_failed(self, error):
-        call_mock_change_vnf_instantiation_state_tracker = (
-            self.mock_change_vnf_instantiation_state_tracker.call_args_list
-        )
+    def vnf_state_is_updated(self, state):
         call_mock_change_vnf_state_tracker = (
             self.mock_change_vnf_state_tracker.call_args_list
         )
-        self.assertEqual(
-            call_mock_change_vnf_instantiation_state_tracker[1].args[0],
-            ChangeVnfInstantiationStateInput(
-                vnfr_uuid=vnfr_uuid, state=VnfInstantiationState.INSTANTIATED
-            ),
-        )
         self.assertEqual(
             call_mock_change_vnf_state_tracker[0].args[0],
-            ChangeVnfStateInput(vnfr_uuid=vnfr_uuid, state=VnfState.STOPPED),
+            ChangeVnfStateInput(vnfr_uuid=vnfr_uuid, state=state),
         )
-        self.assertEqual(str(error.exception), "Workflow execution failed")
 
     @activity.defn(name=ACTIVITY_CHANGE_VNF_STATE)
     async def mock_change_vnf_state(self, nf_state_input: ChangeVnfStateInput) -> None:
@@ -355,34 +471,70 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase):
     async def mock_send_notification_for_vnf(self, input) -> None:
         self.mock_send_notification_for_vnf_tracker()
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
+    @patch(
+        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow._get_vdu_instantiate_info"
     )
-    async def test_vnf_instantiate_workflow_successful(self, mock_instantiate_vdus):
+    async def test_vnf_instantiate_workflow__successful__updates_vnf_instantiation_state(
+        self, mock_get_vdu_instantiate_info
+    ):
+        workflows = [
+            VnfInstantiateWorkflow,
+            MockPrepareVnfWorkflow,
+            MockVduInstantiateWorkflow,
+        ]
         activities = [
             mock_get_task_queue,
             self.mock_change_vnf_instantiation_state,
             self.mock_change_vnf_state,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
             mock_get_vim_cloud,
         ]
+        mock_get_vdu_instantiate_info.return_value = (
+            sample_vdu_instantiate_input_with_config,
+            sample_vdu_instantiate_wf_id_1,
+        )
         async with self.env, self.get_worker(
-            self.task_queue, self.workflows, activities
-        ):
+            self.task_queue, workflows, activities
+        ), self.get_worker(self.task_queue, workflows, activities):
             await self.execute_workflow()
-        self.check_state_change_call_counts()
-        self.workflow_is_succeeded()
-        mock_instantiate_vdus.assert_called_once_with(
-            vnfr=sample_vnfr, vnfd=sample_vnfd, task_queue=LCM_TASK_QUEUE, cloud=cloud
-        )
+        self.vnf_instantiation_state_is_updated(VnfInstantiationState.INSTANTIATED)
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
+    @patch(
+        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow._get_vdu_instantiate_info"
     )
-    async def test_vnf_instantiate_workflow_change_vnf_instantiation_state_exception(
-        self, mock_instantiate_vdus
+    async def test_vnf_instantiate_workflow__successful__updates_vnf_state(
+        self, mock_get_vdu_instantiate_info
+    ):
+        workflows = [
+            VnfInstantiateWorkflow,
+            MockPrepareVnfWorkflow,
+            MockVduInstantiateWorkflow,
+        ]
+        activities = [
+            mock_get_task_queue,
+            self.mock_change_vnf_instantiation_state,
+            self.mock_change_vnf_state,
+            self.mock_send_notification_for_vnf,
+            mock_set_vnf_model,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
+            mock_get_vim_cloud,
+        ]
+        mock_get_vdu_instantiate_info.return_value = (
+            sample_vdu_instantiate_input_with_config,
+            sample_vdu_instantiate_wf_id_1,
+        )
+        async with self.env, self.get_worker(
+            self.task_queue, workflows, activities
+        ), self.get_worker(self.task_queue, workflows, activities):
+            await self.execute_workflow()
+        self.vnf_instantiation_state_is_updated(VnfState.STARTED)
+
+    async def test_vnf_instantiate_workflow__activity_change_vnf_instantiation_state_failed__raise_activity_error(
+        self,
     ):
         activities = [
             mock_get_task_queue,
@@ -390,7 +542,8 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase):
             self.mock_change_vnf_state,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
             mock_get_vim_cloud,
         ]
         async with self.env, self.get_worker(
@@ -398,30 +551,30 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase):
         ):
             with self.assertRaises(WorkflowFailureError) as err:
                 await self.execute_workflow()
-        self.assertEqual(self.mock_change_vnf_instantiation_state_tracker.call_count, 6)
-        self.assertEqual(self.mock_change_vnf_state_tracker.call_count, 0)
-        self.assertEqual(self.mock_send_notification_for_vnf_tracker.call_count, 0)
-        call_mock_change_vnf_instantiation_state_tracker = (
-            self.mock_change_vnf_instantiation_state_tracker.call_args_list
-        )
-        self.assertEqual(
-            call_mock_change_vnf_instantiation_state_tracker[1].args[0],
-            ChangeVnfInstantiationStateInput(
-                vnfr_uuid=vnfr_uuid, state=VnfInstantiationState.NOT_INSTANTIATED
-            ),
-        )
-        mock_instantiate_vdus.assert_not_called()
-        self.assertEqual(str(err.exception), "Workflow execution failed")
-        self.assertEqual(
-            str(err.exception.cause.cause.message),
-            "change-vnf-instantiation-state failed.",
-        )
+            self.assertTrue(isinstance(err.exception.cause, ActivityError))
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
-    )
-    async def test_vnf_instantiate_workflow_change_vnf_state_exception(
-        self, mock_instantiate_vdus
+    async def test_vnf_instantiate_workflow__activity_change_vnf_instantiation_state_failed__vnf_state_not_updated(
+        self,
+    ):
+        activities = [
+            mock_get_task_queue,
+            self.mock_change_vnf_instantiation_state_exception,
+            self.mock_change_vnf_state,
+            self.mock_send_notification_for_vnf,
+            mock_set_vnf_model,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
+            mock_get_vim_cloud,
+        ]
+        async with self.env, self.get_worker(
+            self.task_queue, self.workflows, activities
+        ):
+            with self.assertRaises(WorkflowFailureError):
+                await self.execute_workflow()
+        self.mock_change_vnf_state_tracker.assert_not_called()
+
+    async def test_vnf_instantiate_workflow__change_vnf_state_failed__raise_activity_error(
+        self,
     ):
         activities = [
             mock_get_task_queue,
@@ -429,7 +582,8 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase):
             self.mock_change_vnf_state_exception,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
             mock_get_vim_cloud,
         ]
         async with self.env, self.get_worker(
@@ -437,447 +591,699 @@ class TestVnfInstantiateWorkflow(asynctest.TestCase):
         ):
             with self.assertRaises(WorkflowFailureError) as err:
                 await self.execute_workflow()
-        self.assertEqual(self.mock_change_vnf_instantiation_state_tracker.call_count, 3)
-        self.assertEqual(self.mock_change_vnf_state_tracker.call_count, 6)
-        self.assertEqual(self.mock_send_notification_for_vnf_tracker.call_count, 1)
-        # using workflow_is_succeeded method since vnfInstantationState is being changed to INSTANTIATED and
-        # vnfState to STARTED (not STOPPED)
-        self.workflow_is_succeeded()
-        self.assertEqual(
-            str(err.exception.cause.cause.message),
-            "change-vnf-state failed.",
-        )
+            self.assertTrue(isinstance(err.exception.cause, ActivityError))
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
-    )
-    async def test_vnf_instantiate_workflow_empty_vnf_details(
-        self, mock_instantiate_vdus
+    async def test_vnf_instantiate_workflow__get_task_queue_failed__raises_activity_error(
+        self,
     ):
         activities = [
-            mock_get_task_queue,
+            mock_get_task_queue_raise_exception,
             self.mock_change_vnf_instantiation_state,
             self.mock_change_vnf_state,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details_empty_output,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
             mock_get_vim_cloud,
         ]
         async with self.env, self.get_worker(
             self.task_queue, self.workflows, activities
         ):
-            await self.execute_workflow()
-        self.check_state_change_call_counts()
-        self.workflow_is_succeeded()
-        mock_instantiate_vdus.assert_called_once_with(
-            vnfr={}, vnfd={}, task_queue=LCM_TASK_QUEUE, cloud=cloud
-        )
+            with self.assertRaises(WorkflowFailureError) as err:
+                await self.execute_workflow()
+            self.assertTrue(isinstance(err.exception.cause, ActivityError))
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
-    )
-    async def test_vnf_instantiate_workflow_get_task_queue_raises_exception(
-        self, mock_instantiate_vdus
+    async def test_vnf_instantiate_workflow__wf_vnf_prepare_failed__raises_child_wf_error(
+        self,
     ):
         activities = [
-            mock_get_task_queue_raise_exception,
+            mock_get_task_queue,
             self.mock_change_vnf_instantiation_state,
             self.mock_change_vnf_state,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
             mock_get_vim_cloud,
         ]
         async with self.env, self.get_worker(
-            self.task_queue, self.workflows, activities
+            self.task_queue,
+            [
+                VnfInstantiateWorkflow,
+                MockPrepareVnfWorkflowFailed,
+                MockVduInstantiateWorkflow,
+            ],
+            activities,
         ):
             with self.assertRaises(WorkflowFailureError) as err:
                 await self.execute_workflow()
-        self.check_state_change_call_counts()
-        self.workflow_is_failed(err)
-        mock_instantiate_vdus.assert_not_called()
-        self.assertEqual(
-            str(err.exception.cause.cause.message),
-            "get_task_queue failed.",
-        )
+            self.assertTrue(isinstance(err.exception.cause.cause, ChildWorkflowError))
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
-    )
-    async def test_vnf_instantiate_workflow_get_vnf_details_raises_exception(
-        self, mock_instantiate_vdus
+    async def test_vnf_instantiate_workflow__wf_vdu_instantiate_failed__raises_child_wf_error(
+        self,
     ):
-        workflows = [VnfInstantiateWorkflow, MockPrepareVnfWorkflow]
         activities = [
             mock_get_task_queue,
             self.mock_change_vnf_instantiation_state,
             self.mock_change_vnf_state,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details_raise_exception,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
             mock_get_vim_cloud,
         ]
-        async with self.env, self.get_worker(self.task_queue, workflows, activities):
+        async with self.env, self.get_worker(
+            self.task_queue,
+            [
+                VnfInstantiateWorkflow,
+                MockPrepareVnfWorkflow,
+                MockVduInstantiateWorkflowFailed,
+            ],
+            activities,
+        ):
             with self.assertRaises(WorkflowFailureError) as err:
                 await self.execute_workflow()
-        self.check_state_change_call_counts()
-        self.workflow_is_failed(err)
-        mock_instantiate_vdus.assert_not_called()
-        self.assertEqual(
-            str(err.exception.cause.cause.message),
-            "get_vnf_details failed.",
-        )
+            self.assertTrue(isinstance(err.exception.cause, ChildWorkflowError))
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
-    )
-    async def test_vnf_instantiate_workflow_get_vim_cloud_raises_exception(
-        self, mock_instantiate_vdus
+    async def test_vnf_instantiate_workflow__get_vnf_descriptor_failed__raises_activity_error(
+        self,
     ):
-        workflows = [VnfInstantiateWorkflow, MockPrepareVnfWorkflow]
         activities = [
             mock_get_task_queue,
             self.mock_change_vnf_instantiation_state,
             self.mock_change_vnf_state,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details,
-            mock_get_vim_cloud_raise_exception,
+            mock_get_vnf_descriptor_raise_exception,
+            mock_get_vnf_record,
+            mock_get_vim_cloud,
         ]
-        async with self.env, self.get_worker(self.task_queue, workflows, activities):
+        async with self.env, self.get_worker(
+            self.task_queue, self.workflows, activities
+        ):
             with self.assertRaises(WorkflowFailureError) as err:
                 await self.execute_workflow()
-        self.check_state_change_call_counts()
-        self.workflow_is_failed(err)
-        mock_instantiate_vdus.assert_not_called()
-        self.assertEqual(
-            str(err.exception.cause.cause.message),
-            "get-vim-cloud failed.",
-        )
+            self.assertTrue(isinstance(err.exception.cause, ActivityError))
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_vdu_instantiate_input"
-    )
-    async def test_vnf_instantiate_workflow_calls_vdu_instantiate_workflow(
-        self, mock_vdu_instantiate_input
+    async def test_vnf_instantiate_workflow__get_vnf_record_failed__raises_activity_error(
+        self,
     ):
-        mock_vdu_instantiate_input.return_value = (
-            sample_vdu_instantiate_input,
-            "vdu-instantiate-workflow-id",
-        )
         activities = [
             mock_get_task_queue,
             self.mock_change_vnf_instantiation_state,
             self.mock_change_vnf_state,
             self.mock_send_notification_for_vnf,
             mock_set_vnf_model,
-            mock_get_vnf_details,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record_raise_exception,
             mock_get_vim_cloud,
         ]
         async with self.env, self.get_worker(
             self.task_queue, self.workflows, activities
         ):
+            with self.assertRaises(WorkflowFailureError) as err:
+                await self.execute_workflow()
+            self.assertTrue(isinstance(err.exception.cause, ActivityError))
+
+    async def test_vnf_instantiate_workflow__get_vim_cloud_failed__raises_activity_error(
+        self,
+    ):
+        activities = [
+            mock_get_task_queue,
+            self.mock_change_vnf_instantiation_state,
+            self.mock_change_vnf_state,
+            self.mock_send_notification_for_vnf,
+            mock_set_vnf_model,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
+            mock_get_vim_cloud_raise_exception,
+        ]
+        async with self.env, self.get_worker(
+            self.task_queue, self.workflows, activities
+        ):
+            with self.assertRaises(WorkflowFailureError) as err:
+                await self.execute_workflow()
+            self.assertTrue(isinstance(err.exception.cause, ActivityError))
+
+    @patch("osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus")
+    async def test_vnf_instantiate_workflow__use_different_task_queue__use_juju_task_queue(
+        self, mock_instantiate_vdus
+    ):
+        workflows = [VnfInstantiateWorkflow, MockPrepareVnfWorkflow]
+        activities = [
+            mock_get_different_task_queue,
+            self.mock_change_vnf_instantiation_state,
+            self.mock_change_vnf_state,
+            self.mock_send_notification_for_vnf,
+            mock_set_vnf_model,
+            mock_get_vnf_descriptor,
+            mock_get_vnf_record,
+            mock_get_vim_cloud,
+        ]
+        async with self.env, self.get_worker(
+            self.task_queue, workflows, activities
+        ), self.get_worker(juju_task_queue, workflows, activities):
             await self.execute_workflow()
-        self.check_state_change_call_counts()
-        self.workflow_is_succeeded()
-        call_mock_vdu_instantiate_input = mock_vdu_instantiate_input.call_args_list
-        self.assertEqual(call_mock_vdu_instantiate_input[0][1]["vnfr"], sample_vnfr)
-        self.assertEqual(call_mock_vdu_instantiate_input[0][1]["vnfd"], sample_vnfd)
-        self.assertEqual(call_mock_vdu_instantiate_input[0][1]["vdu"], vdu)
+        mock_instantiate_vdus.assert_called_once_with(
+            vnfr=sample_vnfr,
+            vnfd=sample_vnfd,
+            task_queue=juju_task_queue,
+            cloud=cloud,
+            vnf_instantiation_config={},
+        )
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_vdu_instantiate_input"
+
+class TestInstantiateVdus(asynctest.TestCase):
+    @patch(
+        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow._get_vdu_instantiate_info"
     )
-    @asynctest.mock.patch("temporalio.workflow.execute_child_workflow")
-    async def test_instantiate_vdus_single_vdu(
-        self, mock_execute_child_workflow: AsyncMock, mock_get_vdu_instantiate_input
+    @patch("temporalio.workflow.execute_child_workflow")
+    async def test_instantiate_vdus__empty_vnf_instantiation_config__child_wf_executed_with_expected_vdu_instantiate_input(
+        self,
+        mock_execute_child_workflow,
+        mock_get_vdu_instantiate_info,
     ):
-        mock_get_vdu_instantiate_input.return_value = (
+        mock_get_vdu_instantiate_info.return_value = (
             sample_vdu_instantiate_input,
-            "vdu-instantiate-workflow-id",
+            sample_vdu_instantiate_wf_id_1,
         )
         await VnfInstantiateWorkflow.instantiate_vdus(
-            sample_vnfr, sample_vnfd, LCM_TASK_QUEUE, cloud
-        )
-        self.assertEqual(mock_execute_child_workflow.call_count, 1)
-        mock_execute_child_workflow.assert_called_once_with(
-            workflow=WORKFLOW_VDU_INSTANTIATE,
-            arg=sample_vdu_instantiate_input,
-            task_queue=LCM_TASK_QUEUE,
-            id="vdu-instantiate-workflow-id",
+            sample_vnfr, sample_vnfd, LCM_TASK_QUEUE, cloud, {}
         )
-        mock_get_vdu_instantiate_input.assert_called_once_with(
-            vnfr=sample_vnfr, vnfd=sample_vnfd, vdu=vdu, cloud=cloud
+        call_mock_execute_child_workflow = mock_execute_child_workflow.call_args_list
+        self.assertEqual(
+            call_mock_execute_child_workflow[0].kwargs,
+            {
+                "arg": sample_vdu_instantiate_input,
+                "id": sample_vdu_instantiate_wf_id_1,
+                "task_queue": LCM_TASK_QUEUE,
+                "workflow": "vdu-instantiate",
+            },
         )
 
-    @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_vdu_instantiate_input"
+    @patch(
+        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow._get_vdu_instantiate_info"
     )
-    @asynctest.mock.patch("temporalio.workflow.execute_child_workflow")
-    async def test_instantiate_vdus_multiple_vdus(
-        self, mock_execute_child_workflow: AsyncMock, mock_get_vdu_instantiate_input
+    @patch("temporalio.workflow.execute_child_workflow")
+    async def test_instantiate_vdus__vnf_instantiation_config_exists__child_wf_executed_with_expected_vdu_instantiate_input(
+        self,
+        mock_execute_child_workflow,
+        mock_get_vdu_instantiate_info,
     ):
-        sample_vnfd_multi_vdu = deepcopy(sample_vnfd)
-        sample_vnfd_multi_vdu["vdu"] = [vdu, vdu]
-        mock_get_vdu_instantiate_input.side_effect = [
-            (sample_vdu_instantiate_input, "vdu-instantiate-workflow-id-1"),
-            (sample_vdu_instantiate_input, "vdu-instantiate-workflow-id-2"),
-        ]
+        mock_get_vdu_instantiate_info.return_value = (
+            sample_vdu_instantiate_input_with_config,
+            sample_vdu_instantiate_wf_id_1,
+        )
         await VnfInstantiateWorkflow.instantiate_vdus(
-            sample_vnfr, sample_vnfd_multi_vdu, LCM_TASK_QUEUE, cloud
+            sample_vnfr, sample_vnfd, LCM_TASK_QUEUE, cloud, vnf_config
         )
-        self.assertEqual(mock_execute_child_workflow.call_count, 2)
         call_mock_execute_child_workflow = mock_execute_child_workflow.call_args_list
         self.assertEqual(
-            call_mock_execute_child_workflow[0][1],
+            call_mock_execute_child_workflow[0].kwargs,
             {
-                "arg": sample_vdu_instantiate_input,
-                "id": "vdu-instantiate-workflow-id-1",
-                "task_queue": "lcm-task-queue",
+                "arg": sample_vdu_instantiate_input_with_config,
+                "id": sample_vdu_instantiate_wf_id_1,
+                "task_queue": LCM_TASK_QUEUE,
                 "workflow": "vdu-instantiate",
             },
         )
-        self.assertEqual(
-            call_mock_execute_child_workflow[1][1],
-            {
-                "arg": sample_vdu_instantiate_input,
-                "id": "vdu-instantiate-workflow-id-2",
-                "task_queue": "lcm-task-queue",
-                "workflow": "vdu-instantiate",
-            },
+
+    @patch(
+        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow._get_vdu_instantiate_info"
+    )
+    @patch("temporalio.workflow.execute_child_workflow")
+    async def test_instantiate_vdus__vnfd_without_vdu__child_wf_is_not_executed(
+        self,
+        mock_execute_child_workflow,
+        mock_get_vdu_instantiate_info,
+    ):
+        vnfd = deepcopy(sample_vnfd)
+        vnfd["vdu"] = []
+        await VnfInstantiateWorkflow.instantiate_vdus(
+            sample_vnfr, vnfd, LCM_TASK_QUEUE, cloud, {}
         )
-        call_mock_get_vdu_instantiate_input = (
-            mock_get_vdu_instantiate_input.call_args_list
+        mock_execute_child_workflow.assert_not_called()
+
+    @patch(
+        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow._get_vdu_instantiate_info"
+    )
+    @patch("temporalio.workflow.execute_child_workflow")
+    async def test_instantiate_vdus__use_juju_task_queue__child_wf_executed_with_juju_task_queue(
+        self,
+        mock_execute_child_workflow,
+        mock_get_vdu_instantiate_info,
+    ):
+        mock_get_vdu_instantiate_info.return_value = (
+            sample_vdu_instantiate_input_with_config,
+            sample_vdu_instantiate_wf_id_1,
         )
-        self.assertEqual(
-            call_mock_get_vdu_instantiate_input[0][1],
-            {
-                "vnfr": sample_vnfr,
-                "vnfd": sample_vnfd_multi_vdu,
-                "vdu": vdu,
-                "cloud": cloud,
-            },
+        await VnfInstantiateWorkflow.instantiate_vdus(
+            sample_vnfr, sample_vnfd, juju_task_queue, cloud, vnf_config
         )
+        call_mock_execute_child_workflow = mock_execute_child_workflow.call_args_list
         self.assertEqual(
-            call_mock_get_vdu_instantiate_input[1][1],
+            call_mock_execute_child_workflow[0].kwargs,
             {
-                "vnfr": sample_vnfr,
-                "vnfd": sample_vnfd_multi_vdu,
-                "vdu": vdu,
-                "cloud": cloud,
+                "arg": sample_vdu_instantiate_input_with_config,
+                "id": sample_vdu_instantiate_wf_id_1,
+                "task_queue": juju_task_queue,
+                "workflow": "vdu-instantiate",
             },
         )
 
+
+class TestGetVduInstantiateInfo(asynctest.TestCase):
     @asynctest.mock.patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.instantiate_vdus"
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
     )
-    async def test_vnf_instantiate_workflow_successful_use_different_task_queue(
-        self, mock_instantiate_vdus
-    ):
-        workflows = [VnfInstantiateWorkflow, MockPrepareVnfWorkflow]
-        activities = [
-            mock_get_different_task_queue,
-            self.mock_change_vnf_instantiation_state,
-            self.mock_change_vnf_state,
-            self.mock_send_notification_for_vnf,
-            mock_set_vnf_model,
-            mock_get_vnf_details,
-            mock_get_vim_cloud,
-        ]
-        async with self.env, self.get_worker(
-            self.task_queue, workflows, activities
-        ), self.get_worker(juju_task_queue, workflows, activities):
-            await self.execute_workflow()
-        self.check_state_change_call_counts()
-        self.workflow_is_succeeded()
-        mock_instantiate_vdus.assert_called_once_with(
-            vnfr=sample_vnfr, vnfd=sample_vnfd, task_queue=juju_task_queue, cloud=cloud
-        )
-
-
-class TestGetVduInstantiateInputMethods(TestCase):
-    def test_get_flavor_details_successful(self):
-        compute_desc_id = "compute-id-1"
-        result = VnfInstantiateWorkflow.get_flavor_details(compute_desc_id, sample_vnfd)
-        self.assertEqual(result, flavor_1)
-
-    def test_get_flavor_details_empty_compute_desc(self):
-        compute_desc_id = ""
-        result = VnfInstantiateWorkflow.get_flavor_details(compute_desc_id, sample_vnfd)
-        self.assertEqual(result, {})
-
-    def test_get_flavor_details_compute_desc_not_found(self):
-        compute_desc_id = "compute-id-5"
-        result = VnfInstantiateWorkflow.get_flavor_details(compute_desc_id, sample_vnfd)
-        self.assertEqual(result, {})
-
-    def test_get_flavor_details_empty_vnfd(self):
-        compute_desc_id = "compute-id-5"
-        result = VnfInstantiateWorkflow.get_flavor_details(compute_desc_id, {})
-        self.assertEqual(result, {})
-
-    def test_get_flavor_details_wrong_vnfd_format(self):
-        compute_desc_id = "compute-id-2"
-        sample_vnfd = {
-            "_id": vnfd_id,
-            "vdu": [vdu],
-            "virtual-compute-desc": [
-                {
-                    "virtual-memory": {"size": "4"},
-                }
-            ],
-        }
-        result = VnfInstantiateWorkflow.get_flavor_details(compute_desc_id, sample_vnfd)
-        self.assertEqual(result, {})
-
-    @patch("osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_flavor_details")
-    def test_get_compute_constraints(self, mock_get_flavor_details):
-        mock_get_flavor_details.return_value = flavor_1
-        result = VnfInstantiateWorkflow.get_compute_constraints(vdu, sample_vnfd)
-        self.assertEqual(VduComputeConstraints(cores=2, mem=4), result)
-
-    @patch("osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_flavor_details")
-    def test_get_compute_constraints_empty_flavor_details(
-        self, mock_get_flavor_details
-    ):
-        mock_get_flavor_details.return_value = {}
-        result = VnfInstantiateWorkflow.get_compute_constraints(vdu, sample_vnfd)
-        self.assertEqual(VduComputeConstraints(cores=0, mem=0), result)
-
-    @patch("osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_flavor_details")
-    def test_get_compute_constraints_flavor_details_is_none(
-        self, mock_get_flavor_details
-    ):
-        mock_get_flavor_details.return_value = None
-        result = VnfInstantiateWorkflow.get_compute_constraints(vdu, sample_vnfd)
-        self.assertEqual(VduComputeConstraints(cores=0, mem=0), result)
-
-    @patch("osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_flavor_details")
-    def test_get_compute_constraints_flavor_has_only_cpu(self, mock_get_flavor_details):
-        mock_get_flavor_details.return_value = flavor_2
-        result = VnfInstantiateWorkflow.get_compute_constraints(vdu, sample_vnfd)
-        self.assertEqual(VduComputeConstraints(cores=2, mem=0), result)
-
-    @patch("osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_flavor_details")
-    def test_get_compute_constraints_flavor_has_only_memory(
-        self, mock_get_flavor_details
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__empty_vnf_instantiation_config__get_expected_vdu_inst_input_and_wf_id(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
     ):
-        mock_get_flavor_details.return_value = flavor_3
-        result = VnfInstantiateWorkflow.get_compute_constraints(vdu, sample_vnfd)
-        self.assertEqual(VduComputeConstraints(cores=0, mem=4), result)
+        mock_get_compute_constraints.return_value = sample_constraints
+        mock_get_charm_info.return_value = sample_charm_info
+        mock_get_vdu_instantiation_params.return_value = {}
+        mock_get_application_config.return_value = {}
+        (
+            vdu_instantiate_input,
+            vdu_instantiate_workflow_id,
+        ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+            vnfr=sample_vnfr,
+            vnfd=sample_vnfd,
+            vdu=vdu,
+            cloud=cloud,
+            vnf_instantiation_config={},
+        )
+        self.assertTupleEqual(
+            (vdu_instantiate_input, vdu_instantiate_workflow_id),
+            (sample_vdu_instantiate_input, "my-model-name-my-app"),
+        )
 
-    @patch("osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info")
-    @patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_compute_constraints"
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
     )
-    def test_get_vdu_instantiate_input(
-        self, mock_get_compute_constraints, mock_get_charm_info
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__with_vnf_instantiation_config__get_expected_vdu_inst_input_and_wf_id(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
     ):
         mock_get_compute_constraints.return_value = sample_constraints
         mock_get_charm_info.return_value = sample_charm_info
+        mock_get_vdu_instantiation_params.return_value = config
+        mock_get_application_config.return_value = app_config
         (
             vdu_instantiate_input,
-            vdu_instantiate_wf_id,
-        ) = VnfInstantiateWorkflow.get_vdu_instantiate_input(
-            sample_vnfr, sample_vnfd, vdu, cloud
+            vdu_instantiate_workflow_id,
+        ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+            vnfr=sample_vnfr,
+            vnfd=sample_vnfd,
+            vdu=vdu,
+            cloud=cloud,
+            vnf_instantiation_config=vnf_config,
+        )
+        self.assertTupleEqual(
+            (vdu_instantiate_input, vdu_instantiate_workflow_id),
+            (sample_vdu_instantiate_input_with_config, "my-model-name-my-app"),
         )
-        self.assertEqual(vdu_instantiate_input, sample_vdu_instantiate_input)
-        self.assertEqual(vdu_instantiate_wf_id, "my-model-name-my-app")
-        mock_get_charm_info.assert_called_once()
-        mock_get_compute_constraints.assert_called_once()
 
-    @patch("osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info")
-    @patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_compute_constraints"
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
     )
-    def test_get_vdu_instantiate_input_vnfr_without_namespace(
-        self, mock_get_compute_constraints, mock_get_charm_info
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__vnfr_without_namespace__raise_type_error(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
     ):
         vnfr = deepcopy(sample_vnfr)
         vnfr.pop("namespace")
         mock_get_compute_constraints.return_value = sample_constraints
         mock_get_charm_info.return_value = sample_charm_info
-        with self.assertRaises(TypeError) as err:
-            VnfInstantiateWorkflow.get_vdu_instantiate_input(
-                vnfr, sample_vnfd, vdu, cloud
+        with self.assertRaises(TypeError):
+            VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
             )
-        self.assertEqual(
-            str(err.exception),
-            "unsupported operand type(s) for +: 'NoneType' and 'str'",
-        )
-        mock_get_charm_info.assert_called_once()
-        mock_get_compute_constraints.assert_called_once()
 
-    @patch("osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info")
-    @patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_compute_constraints"
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
     )
-    def test_get_vdu_instantiate_input_app_name_is_empty(
-        self, mock_get_compute_constraints, mock_get_charm_info
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__app_name_is_empty__expected_wf_id(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
     ):
         mock_get_compute_constraints.return_value = sample_constraints
         charm_info = CharmInfo(app_name="", channel="latest", entity_url="my-url")
+        mock_get_application_config.return_value = app_config
         mock_get_charm_info.return_value = charm_info
         (
-            vdu_instantiate_input,
-            vdu_instantiate_wf_id,
-        ) = VnfInstantiateWorkflow.get_vdu_instantiate_input(
-            sample_vnfr, sample_vnfd, vdu, cloud
+            _,
+            vdu_instantiate_workflow_id,
+        ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+            vnfr=sample_vnfr,
+            vnfd=sample_vnfd,
+            vdu=vdu,
+            cloud=cloud,
+            vnf_instantiation_config={},
         )
-        self.assertEqual(
-            vdu_instantiate_input,
-            VduInstantiateInput(
-                vim_uuid=vim_account_id,
-                model_name=model_name,
-                charm_info=charm_info,
-                constraints=sample_constraints,
-                cloud=cloud,
-                config=config,
-            ),
+        self.assertEqual(vdu_instantiate_workflow_id, "my-model-name-")
+
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_compute_constraints_failed__no_vdu_inst_wf_input_and_vdu_wf_id(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
+    ):
+        mock_get_compute_constraints.side_effect = GetComputeConstraintsException(
+            "get_compute_constraints failed"
         )
-        self.assertEqual(vdu_instantiate_wf_id, "my-model-name-")
-        mock_get_charm_info.assert_called_once()
-        mock_get_compute_constraints.assert_called_once()
+        mock_get_charm_info.return_value = sample_charm_info
+        with self.assertRaises(Exception):
+            (
+                vdu_instantiate_input,
+                vdu_instantiate_workflow_id,
+            ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
+            )
+            self.assertEqual(
+                (vdu_instantiate_input, vdu_instantiate_workflow_id), (None, None)
+            )
 
-    @patch("osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info")
-    @patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_compute_constraints"
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
     )
-    def test_get_vdu_instantiate_input_get_compute_constraints_raises(
-        self, mock_get_compute_constraints, mock_get_charm_info
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_compute_constraints_failed__raise_compute_constraints_exception(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
     ):
-        mock_get_compute_constraints.side_effect = TestException(
+        mock_get_compute_constraints.side_effect = GetComputeConstraintsException(
             "get_compute_constraints failed"
         )
         mock_get_charm_info.return_value = sample_charm_info
-        with self.assertRaises(TestException) as err:
-            result = VnfInstantiateWorkflow.get_vdu_instantiate_input(
-                sample_vnfr, sample_vnfd, vdu, cloud
+        with self.assertRaises(GetComputeConstraintsException):
+            VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
             )
-            self.assertEqual(result, None)
-        self.assertEqual(str(err.exception), "get_compute_constraints failed")
-        mock_get_charm_info.assert_called_once()
-        mock_get_compute_constraints.assert_called_once()
 
-    @patch("osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info")
-    @patch(
-        "osm_lcm.temporal.vnf_workflows.VnfInstantiateWorkflow.get_compute_constraints"
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
     )
-    def test_get_vdu_instantiate_input_get_charm_info_raises(
-        self, mock_get_compute_constraints, mock_get_charm_info
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_charm_info_failed__raise_get_charm_info_exception(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
     ):
         mock_get_compute_constraints.return_value = sample_constraints
-        mock_get_charm_info.side_effect = TestException(
+        mock_get_charm_info.side_effect = GetCharmInfoException(
             "get_compute_constraints failed"
         )
-        with self.assertRaises(TestException) as err:
-            result = VnfInstantiateWorkflow.get_vdu_instantiate_input(
-                sample_vnfr, sample_vnfd, vdu, cloud
+        with self.assertRaises(GetCharmInfoException):
+            VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
+            )
+
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_charm_info_failed__no_vdu_inst_wf_input_and_vdu_wf_id(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
+    ):
+        mock_get_compute_constraints.return_value = sample_constraints
+        mock_get_charm_info.side_effect = GetCharmInfoException(
+            "get_compute_constraints failed"
+        )
+        with self.assertRaises(Exception):
+            (
+                vdu_instantiate_input,
+                vdu_instantiate_workflow_id,
+            ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
+            )
+            self.assertEqual(
+                (vdu_instantiate_input, vdu_instantiate_workflow_id), (None, None)
+            )
+
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_vdu_instantiation_params_failed__raise_get_vdu_instantiation_params_exception(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
+    ):
+        mock_get_compute_constraints.return_value = sample_constraints
+        mock_get_charm_info.return_value = sample_charm_info
+        mock_get_vdu_instantiation_params.side_effect = (
+            GetVduInstantiationParamsException("get_vdu_instantiation_params failed")
+        )
+        mock_get_application_config.return_value = {}
+        with self.assertRaises(GetVduInstantiationParamsException):
+            VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
+            )
+
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_vdu_instantiation_params_failed__no_vdu_inst_wf_input_and_vdu_wf_id(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
+    ):
+        mock_get_compute_constraints.return_value = sample_constraints
+        mock_get_charm_info.return_value = sample_charm_info
+        mock_get_vdu_instantiation_params.side_effect = (
+            GetVduInstantiationParamsException("get_vdu_instantiation_params failed")
+        )
+        mock_get_application_config.return_value = {}
+        with self.assertRaises(Exception):
+            (
+                vdu_instantiate_input,
+                vdu_instantiate_workflow_id,
+            ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
+            )
+
+            self.assertEqual(
+                (vdu_instantiate_input, vdu_instantiate_workflow_id), (None, None)
+            )
+
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_application_config_failed__raise_get_application_config_exception(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
+    ):
+        mock_get_compute_constraints.return_value = sample_constraints
+        mock_get_charm_info.return_value = sample_charm_info
+        mock_get_vdu_instantiation_params.return_value = config
+        mock_get_application_config.side_effect = GetApplicationConfigException(
+            "get_vdu_instantiation_params failed"
+        )
+        with self.assertRaises(GetApplicationConfigException):
+            VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
+            )
+
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_application_config"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_vdu_instantiation_params"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.juju_paas_activities.CharmInfoUtils.get_charm_info"
+    )
+    @asynctest.mock.patch(
+        "osm_lcm.temporal.vnf_activities.VnfOperations.get_compute_constraints"
+    )
+    async def test_get_vdu_instantiate_info__get_application_config_failed__no_vdu_inst_wf_input_and_vdu_wf_id(
+        self,
+        mock_get_compute_constraints,
+        mock_get_charm_info,
+        mock_get_vdu_instantiation_params,
+        mock_get_application_config,
+    ):
+        mock_get_compute_constraints.return_value = sample_constraints
+        mock_get_charm_info.return_value = sample_charm_info
+        mock_get_vdu_instantiation_params.return_value = config
+        mock_get_application_config.side_effect = GetApplicationConfigException(
+            "get_vdu_instantiation_params failed"
+        )
+        with self.assertRaises(Exception):
+            (
+                vdu_instantiate_input,
+                vdu_instantiate_workflow_id,
+            ) = VnfInstantiateWorkflow._get_vdu_instantiate_info(
+                vnfr=sample_vnfr,
+                vnfd=sample_vnfd,
+                vdu=vdu,
+                cloud=cloud,
+                vnf_instantiation_config={},
+            )
+            self.assertEqual(
+                (vdu_instantiate_input, vdu_instantiate_workflow_id), (None, None)
             )
-            self.assertEqual(result, None)
-        self.assertEqual(str(err.exception), "get_compute_constraints failed")
-        mock_get_charm_info.assert_called_once()
-        mock_get_compute_constraints.assert_not_called()
 
 
 if __name__ == "__main__":