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/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: