OSMENG-1048 Implement day1 configuration for VDU

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>
diff --git a/osm_lcm/temporal/juju_paas_activities.py b/osm_lcm/temporal/juju_paas_activities.py
index 3182317..5082254 100644
--- a/osm_lcm/temporal/juju_paas_activities.py
+++ b/osm_lcm/temporal/juju_paas_activities.py
@@ -17,6 +17,7 @@
 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 @@
             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)
diff --git a/osm_lcm/temporal/lcm_activities.py b/osm_lcm/temporal/lcm_activities.py
index 5f360f1..6ddb0f3 100644
--- a/osm_lcm/temporal/lcm_activities.py
+++ b/osm_lcm/temporal/lcm_activities.py
@@ -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 @@
     ACTIVITY_NSLCM_NO_OP,
 )
 from osm_lcm.data_utils.database.database import Database
+from temporalio import activity
 
 
 class NsLcmActivity:
diff --git a/osm_lcm/temporal/lcm_workflows.py b/osm_lcm/temporal/lcm_workflows.py
index 2dc7096..6c8c28f 100644
--- a/osm_lcm/temporal/lcm_workflows.py
+++ b/osm_lcm/temporal/lcm_workflows.py
@@ -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 @@
     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 @@
             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 @@
             await self.update_operation_state(
                 LcmOperationState.FAILED,
                 error_message=str(e),
-                detailed_status=err_details,
+                detailed_status=str(e),
             )
             raise e
 
diff --git a/osm_lcm/temporal/ns_activities.py b/osm_lcm/temporal/ns_activities.py
index b937717..8c7c182 100644
--- a/osm_lcm/temporal/ns_activities.py
+++ b/osm_lcm/temporal/ns_activities.py
@@ -18,17 +18,19 @@
 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 @@
         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 @@
             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:
diff --git a/osm_lcm/temporal/ns_workflows.py b/osm_lcm/temporal/ns_workflows.py
index 343a9c9..6281ee6 100644
--- a/osm_lcm/temporal/ns_workflows.py
+++ b/osm_lcm/temporal/ns_workflows.py
@@ -15,14 +15,13 @@
 # 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.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 @@
     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 @@
                 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 @@
             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 @@
     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 {}
diff --git a/osm_lcm/temporal/vdu_workflows.py b/osm_lcm/temporal/vdu_workflows.py
index 0c91e99..7349535 100644
--- a/osm_lcm/temporal/vdu_workflows.py
+++ b/osm_lcm/temporal/vdu_workflows.py
@@ -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 @@
     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)
diff --git a/osm_lcm/temporal/vim_activities.py b/osm_lcm/temporal/vim_activities.py
index 4bd5514..e9db668 100644
--- a/osm_lcm/temporal/vim_activities.py
+++ b/osm_lcm/temporal/vim_activities.py
@@ -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 @@
     UpdateVimStateInput,
 )
 from osm_lcm.data_utils.database.database import Database
+from temporalio import activity
 
 
 class VimDbActivity:
diff --git a/osm_lcm/temporal/vim_workflows.py b/osm_lcm/temporal/vim_workflows.py
index cb7d636..5d6b08c 100644
--- a/osm_lcm/temporal/vim_workflows.py
+++ b/osm_lcm/temporal/vim_workflows.py
@@ -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 @@
     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)
diff --git a/osm_lcm/temporal/vnf_activities.py b/osm_lcm/temporal/vnf_activities.py
index 2e8ab1c..9581db0 100644
--- a/osm_lcm/temporal/vnf_activities.py
+++ b/osm_lcm/temporal/vnf_activities.py
@@ -14,12 +14,15 @@
 # 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 @@
     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 @@
         """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,14 @@
         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, vnfds
+            DB read:           vnfrs
 
         Raises (retryable):
             DbException: If DB read operations fail, the collection or DB record ID does not exist.
@@ -126,10 +135,93 @@
             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"]})
+        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:           vnfds
+
+        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.
+
+        """
+        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 @@
         )
 
     @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:
diff --git a/osm_lcm/temporal/vnf_workflows.py b/osm_lcm/temporal/vnf_workflows.py
index c97f908..c121b05 100644
--- a/osm_lcm/temporal/vnf_workflows.py
+++ b/osm_lcm/temporal/vnf_workflows.py
@@ -13,14 +13,11 @@
 # 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 @@
     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 @@
     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 @@
                 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 @@
                     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 @@
             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 @@
         )
 
     @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 @@
             )
 
     @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 @@
         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,