Feature 10886 manual scaling for KNF (helm chart) deployment
[osm/LCM.git] / osm_lcm / ns.py
index 78c58f8..4192625 100644 (file)
@@ -17,6 +17,7 @@
 ##
 
 import asyncio
+from typing import Any, Dict, List
 import yaml
 import logging
 import logging.handlers
@@ -31,7 +32,21 @@ from jinja2 import (
 )
 
 from osm_lcm import ROclient
-from osm_lcm.data_utils.nsr import get_deployed_kdu
+from osm_lcm.data_utils.nsr import (
+    get_deployed_kdu,
+    get_deployed_vca,
+    get_deployed_vca_list,
+    get_nsd,
+)
+from osm_lcm.data_utils.vca import (
+    DeployedComponent,
+    DeployedK8sResource,
+    DeployedVCA,
+    EELevel,
+    Relation,
+    EERelation,
+    safe_get_ee_relation,
+)
 from osm_lcm.ng_ro import NgRoClient, NgRoException
 from osm_lcm.lcm_utils import (
     LcmException,
@@ -41,8 +56,13 @@ from osm_lcm.lcm_utils import (
     get_iterable,
     populate_dict,
 )
-from osm_lcm.data_utils.nsd import get_vnf_profiles
+from osm_lcm.data_utils.nsd import (
+    get_ns_configuration_relation_list,
+    get_vnf_profile,
+    get_vnf_profiles,
+)
 from osm_lcm.data_utils.vnfd import (
+    get_relation_list,
     get_vdu_list,
     get_vdu_profile,
     get_ee_sorted_initial_config_primitive_list,
@@ -55,12 +75,13 @@ from osm_lcm.data_utils.vnfd import (
     get_scaling_aspect,
     get_number_of_instances,
     get_juju_ee_ref,
-    get_kdu_profile,
+    get_kdu_resource_profile,
 )
 from osm_lcm.data_utils.list_utils import find_in_list
 from osm_lcm.data_utils.vnfr import get_osm_params, get_vdur_index, get_kdur
 from osm_lcm.data_utils.dict_utils import parse_yaml_strings
 from osm_lcm.data_utils.database.vim_account import VimAccountDB
+from n2vc.definitions import RelationEndpoint
 from n2vc.k8s_helm_conn import K8sHelmConnector
 from n2vc.k8s_helm3_conn import K8sHelm3Connector
 from n2vc.k8s_juju_conn import K8sJujuConnector
@@ -75,6 +96,7 @@ from n2vc.n2vc_juju_conn import N2VCJujuConnector
 from n2vc.exceptions import N2VCException, N2VCNotFound, K8sException
 
 from osm_lcm.lcm_helm_conn import LCMHelmConn
+from osm_lcm.prometheus import parse_job
 
 from copy import copy, deepcopy
 from time import time
@@ -102,7 +124,7 @@ class NsLcm(LcmBase):
     SUBOPERATION_STATUS_SKIP = -3
     task_name_deploy_vca = "Deploying VCA"
 
-    def __init__(self, msg, lcm_tasks, config, loop, prometheus=None):
+    def __init__(self, msg, lcm_tasks, config, loop):
         """
         Init, Connect to database, filesystem storage, and messaging
         :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
@@ -179,8 +201,6 @@ class NsLcm(LcmBase):
             "helm-v3": self.conn_helm_ee,
         }
 
-        self.prometheus = prometheus
-
         # create RO client
         self.RO = NgRoClient(self.loop, **self.ro_config)
 
@@ -394,11 +414,17 @@ class NsLcm(LcmBase):
         try:
             if vdu.get("cloud-init-file"):
                 base_folder = vnfd["_admin"]["storage"]
-                cloud_init_file = "{}/{}/cloud_init/{}".format(
-                    base_folder["folder"],
-                    base_folder["pkg-dir"],
-                    vdu["cloud-init-file"],
-                )
+                if base_folder["pkg-dir"]:
+                    cloud_init_file = "{}/{}/cloud_init/{}".format(
+                        base_folder["folder"],
+                        base_folder["pkg-dir"],
+                        vdu["cloud-init-file"],
+                    )
+                else:
+                    cloud_init_file = "{}/Scripts/cloud_init/{}".format(
+                        base_folder["folder"],
+                        vdu["cloud-init-file"],
+                    )
                 with self.fs.file_open(cloud_init_file, "r") as ci_file:
                     cloud_init_content = ci_file.read()
             elif vdu.get("cloud-init"):
@@ -1042,11 +1068,17 @@ class NsLcm(LcmBase):
                     # read file and put content at target.cloul_init_content. Avoid ng_ro to use shared package system
                     if vdur["cloud-init"] not in target["cloud_init_content"]:
                         base_folder = vnfd["_admin"]["storage"]
-                        cloud_init_file = "{}/{}/cloud_init/{}".format(
-                            base_folder["folder"],
-                            base_folder["pkg-dir"],
-                            vdud.get("cloud-init-file"),
-                        )
+                        if base_folder["pkg-dir"]:
+                            cloud_init_file = "{}/{}/cloud_init/{}".format(
+                                base_folder["folder"],
+                                base_folder["pkg-dir"],
+                                vdud.get("cloud-init-file"),
+                            )
+                        else:
+                            cloud_init_file = "{}/Scripts/cloud_init/{}".format(
+                                base_folder["folder"],
+                                vdud.get("cloud-init-file"),
+                            )
                         with self.fs.file_open(cloud_init_file, "r") as ci_file:
                             target["cloud_init_content"][
                                 vdur["cloud-init"]
@@ -1557,9 +1589,13 @@ class NsLcm(LcmBase):
         raise LcmException("Configuration aborted because dependent charm/s timeout")
 
     def get_vca_id(self, db_vnfr: dict, db_nsr: dict):
-        return deep_get(db_vnfr, ("vca-id",)) or deep_get(
-            db_nsr, ("instantiate_params", "vcaId")
-        )
+        vca_id = None
+        if db_vnfr:
+            vca_id = deep_get(db_vnfr, ("vca-id",))
+        elif db_nsr:
+            vim_account_id = deep_get(db_nsr, ("instantiate_params", "vimAccountId"))
+            vca_id = VimAccountDB.get_vim_account_with_id(vim_account_id).get("vca")
+        return vca_id
 
     async def instantiate_N2VC(
         self,
@@ -1603,30 +1639,44 @@ class NsLcm(LcmBase):
 
             namespace = "{nsi}.{ns}".format(nsi=nsi_id if nsi_id else "", ns=nsr_id)
 
+            if vca_type == "native_charm":
+                index_number = 0
+            else:
+                index_number = vdu_index or 0
+
             if vnfr_id:
                 element_type = "VNF"
                 element_under_configuration = vnfr_id
-                namespace += ".{}-{}".format(vnfr_id, vdu_index or 0)
+                namespace += ".{}-{}".format(vnfr_id, index_number)
                 if vdu_id:
-                    namespace += ".{}-{}".format(vdu_id, vdu_index or 0)
+                    namespace += ".{}-{}".format(vdu_id, index_number)
                     element_type = "VDU"
-                    element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0)
+                    element_under_configuration = "{}-{}".format(vdu_id, index_number)
                     osm_config["osm"]["vdu_id"] = vdu_id
                 elif kdu_name:
-                    namespace += ".{}.{}".format(kdu_name, vdu_index or 0)
+                    namespace += ".{}".format(kdu_name)
                     element_type = "KDU"
                     element_under_configuration = kdu_name
                     osm_config["osm"]["kdu_name"] = kdu_name
 
             # Get artifact path
-            artifact_path = "{}/{}/{}/{}".format(
-                base_folder["folder"],
-                base_folder["pkg-dir"],
-                "charms"
-                if vca_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
-                else "helm-charts",
-                vca_name,
-            )
+            if base_folder["pkg-dir"]:
+                artifact_path = "{}/{}/{}/{}".format(
+                    base_folder["folder"],
+                    base_folder["pkg-dir"],
+                    "charms"
+                    if vca_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
+                    else "helm-charts",
+                    vca_name,
+                )
+            else:
+                artifact_path = "{}/Scripts/{}/{}/".format(
+                    base_folder["folder"],
+                    "charms"
+                    if vca_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
+                    else "helm-charts",
+                    vca_name,
+                )
 
             self.logger.debug("Artifact path > {}".format(artifact_path))
 
@@ -1806,6 +1856,7 @@ class NsLcm(LcmBase):
                     config=config,
                     num_units=num_units,
                     vca_id=vca_id,
+                    vca_type=vca_type,
                 )
 
             # write in db flag of configuration_sw already installed
@@ -1817,9 +1868,8 @@ class NsLcm(LcmBase):
             await self._add_vca_relations(
                 logging_text=logging_text,
                 nsr_id=nsr_id,
-                vca_index=vca_index,
-                vca_id=vca_id,
                 vca_type=vca_type,
+                vca_index=vca_index,
             )
 
             # if SSH access is required, then get execution environment SSH public
@@ -1922,6 +1972,7 @@ class NsLcm(LcmBase):
                     params_dict=primitive_params_,
                     db_dict=db_dict,
                     vca_id=vca_id,
+                    vca_type=vca_type,
                 )
                 # Once some primitive has been exec, check and write at db if it needs to exec terminated primitives
                 if check_if_terminated_needed:
@@ -1935,7 +1986,7 @@ class NsLcm(LcmBase):
 
             # STEP 7 Configure metrics
             if vca_type == "helm" or vca_type == "helm-v3":
-                prometheus_jobs = await self.add_prometheus_metrics(
+                prometheus_jobs = await self.extract_prometheus_scrape_jobs(
                     ee_id=ee_id,
                     artifact_path=artifact_path,
                     ee_config_descriptor=ee_config_descriptor,
@@ -1950,6 +2001,17 @@ class NsLcm(LcmBase):
                         {db_update_entry + "prometheus_jobs": prometheus_jobs},
                     )
 
+                    for job in prometheus_jobs:
+                        self.db.set_one(
+                            "prometheus_jobs",
+                            {
+                                "job_name": job["job_name"]
+                            },
+                            job,
+                            upsert=True,
+                            fail_on_empty=False
+                        )
+
             step = "instantiated at VCA"
             self.logger.debug(logging_text + step)
 
@@ -2224,8 +2286,10 @@ class NsLcm(LcmBase):
 
             # read from db: ns
             stage[1] = "Getting nsr={} from db.".format(nsr_id)
+            self.logger.debug(logging_text + stage[1])
             db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
             stage[1] = "Getting nsd={} from db.".format(db_nsr["nsd-id"])
+            self.logger.debug(logging_text + stage[1])
             nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
             self.fs.sync(db_nsr["nsd-id"])
             db_nsr["nsd"] = nsd
@@ -2390,9 +2454,7 @@ class NsLcm(LcmBase):
                     deploy_params_vdu["OSM"] = get_osm_params(
                         db_vnfr, vdu_id, vdu_count_index=0
                     )
-                    vdud_count = get_vdu_profile(vnfd, vdu_id).get(
-                        "max-number-of-instances", 1
-                    )
+                    vdud_count = get_number_of_instances(vnfd, vdu_id)
 
                     self.logger.debug("VDUD > {}".format(vdud))
                     self.logger.debug(
@@ -2621,14 +2683,295 @@ class NsLcm(LcmBase):
             self.logger.debug(logging_text + "Exit")
             self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
 
+    def _get_vnfd(self, vnfd_id: str, cached_vnfds: Dict[str, Any]):
+        if vnfd_id not in cached_vnfds:
+            cached_vnfds[vnfd_id] = self.db.get_one("vnfds", {"id": vnfd_id})
+        return cached_vnfds[vnfd_id]
+
+    def _get_vnfr(self, nsr_id: str, vnf_profile_id: str, cached_vnfrs: Dict[str, Any]):
+        if vnf_profile_id not in cached_vnfrs:
+            cached_vnfrs[vnf_profile_id] = self.db.get_one(
+                "vnfrs",
+                {
+                    "member-vnf-index-ref": vnf_profile_id,
+                    "nsr-id-ref": nsr_id,
+                },
+            )
+        return cached_vnfrs[vnf_profile_id]
+
+    def _is_deployed_vca_in_relation(
+        self, vca: DeployedVCA, relation: Relation
+    ) -> bool:
+        found = False
+        for endpoint in (relation.provider, relation.requirer):
+            if endpoint["kdu-resource-profile-id"]:
+                continue
+            found = (
+                vca.vnf_profile_id == endpoint.vnf_profile_id
+                and vca.vdu_profile_id == endpoint.vdu_profile_id
+                and vca.execution_environment_ref == endpoint.execution_environment_ref
+            )
+            if found:
+                break
+        return found
+
+    def _update_ee_relation_data_with_implicit_data(
+        self, nsr_id, nsd, ee_relation_data, cached_vnfds, vnf_profile_id: str = None
+    ):
+        ee_relation_data = safe_get_ee_relation(
+            nsr_id, ee_relation_data, vnf_profile_id=vnf_profile_id
+        )
+        ee_relation_level = EELevel.get_level(ee_relation_data)
+        if (ee_relation_level in (EELevel.VNF, EELevel.VDU)) and not ee_relation_data[
+            "execution-environment-ref"
+        ]:
+            vnf_profile = get_vnf_profile(nsd, ee_relation_data["vnf-profile-id"])
+            vnfd_id = vnf_profile["vnfd-id"]
+            db_vnfd = self._get_vnfd(vnfd_id, cached_vnfds)
+            entity_id = (
+                vnfd_id
+                if ee_relation_level == EELevel.VNF
+                else ee_relation_data["vdu-profile-id"]
+            )
+            ee = get_juju_ee_ref(db_vnfd, entity_id)
+            if not ee:
+                raise Exception(
+                    f"not execution environments found for ee_relation {ee_relation_data}"
+                )
+            ee_relation_data["execution-environment-ref"] = ee["id"]
+        return ee_relation_data
+
+    def _get_ns_relations(
+        self,
+        nsr_id: str,
+        nsd: Dict[str, Any],
+        vca: DeployedVCA,
+        cached_vnfds: Dict[str, Any],
+    ) -> List[Relation]:
+        relations = []
+        db_ns_relations = get_ns_configuration_relation_list(nsd)
+        for r in db_ns_relations:
+            provider_dict = None
+            requirer_dict = None
+            if all(key in r for key in ("provider", "requirer")):
+                provider_dict = r["provider"]
+                requirer_dict = r["requirer"]
+            elif "entities" in r:
+                provider_id = r["entities"][0]["id"]
+                provider_dict = {
+                    "nsr-id": nsr_id,
+                    "endpoint": r["entities"][0]["endpoint"],
+                }
+                if provider_id != nsd["id"]:
+                    provider_dict["vnf-profile-id"] = provider_id
+                requirer_id = r["entities"][1]["id"]
+                requirer_dict = {
+                    "nsr-id": nsr_id,
+                    "endpoint": r["entities"][1]["endpoint"],
+                }
+                if requirer_id != nsd["id"]:
+                    requirer_dict["vnf-profile-id"] = requirer_id
+            else:
+                raise Exception("provider/requirer or entities must be included in the relation.")
+            relation_provider = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, provider_dict, cached_vnfds
+            )
+            relation_requirer = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, requirer_dict, cached_vnfds
+            )
+            provider = EERelation(relation_provider)
+            requirer = EERelation(relation_requirer)
+            relation = Relation(r["name"], provider, requirer)
+            vca_in_relation = self._is_deployed_vca_in_relation(vca, relation)
+            if vca_in_relation:
+                relations.append(relation)
+        return relations
+
+    def _get_vnf_relations(
+        self,
+        nsr_id: str,
+        nsd: Dict[str, Any],
+        vca: DeployedVCA,
+        cached_vnfds: Dict[str, Any],
+    ) -> List[Relation]:
+        relations = []
+        vnf_profile = get_vnf_profile(nsd, vca.vnf_profile_id)
+        vnf_profile_id = vnf_profile["id"]
+        vnfd_id = vnf_profile["vnfd-id"]
+        db_vnfd = self._get_vnfd(vnfd_id, cached_vnfds)
+        db_vnf_relations = get_relation_list(db_vnfd, vnfd_id)
+        for r in db_vnf_relations:
+            provider_dict = None
+            requirer_dict = None
+            if all(key in r for key in ("provider", "requirer")):
+                provider_dict = r["provider"]
+                requirer_dict = r["requirer"]
+            elif "entities" in r:
+                provider_id = r["entities"][0]["id"]
+                provider_dict = {
+                    "nsr-id": nsr_id,
+                    "vnf-profile-id": vnf_profile_id,
+                    "endpoint": r["entities"][0]["endpoint"],
+                }
+                if provider_id != vnfd_id:
+                    provider_dict["vdu-profile-id"] = provider_id
+                requirer_id = r["entities"][1]["id"]
+                requirer_dict = {
+                    "nsr-id": nsr_id,
+                    "vnf-profile-id": vnf_profile_id,
+                    "endpoint": r["entities"][1]["endpoint"],
+                }
+                if requirer_id != vnfd_id:
+                    requirer_dict["vdu-profile-id"] = requirer_id
+            else:
+                raise Exception("provider/requirer or entities must be included in the relation.")
+            relation_provider = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, provider_dict, cached_vnfds, vnf_profile_id=vnf_profile_id
+            )
+            relation_requirer = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, requirer_dict, cached_vnfds, vnf_profile_id=vnf_profile_id
+            )
+            provider = EERelation(relation_provider)
+            requirer = EERelation(relation_requirer)
+            relation = Relation(r["name"], provider, requirer)
+            vca_in_relation = self._is_deployed_vca_in_relation(vca, relation)
+            if vca_in_relation:
+                relations.append(relation)
+        return relations
+
+    def _get_kdu_resource_data(
+        self,
+        ee_relation: EERelation,
+        db_nsr: Dict[str, Any],
+        cached_vnfds: Dict[str, Any],
+    ) -> DeployedK8sResource:
+        nsd = get_nsd(db_nsr)
+        vnf_profiles = get_vnf_profiles(nsd)
+        vnfd_id = find_in_list(
+            vnf_profiles,
+            lambda vnf_profile: vnf_profile["id"] == ee_relation.vnf_profile_id,
+        )["vnfd-id"]
+        db_vnfd = self._get_vnfd(vnfd_id, cached_vnfds)
+        kdu_resource_profile = get_kdu_resource_profile(
+            db_vnfd, ee_relation.kdu_resource_profile_id
+        )
+        kdu_name = kdu_resource_profile["kdu-name"]
+        deployed_kdu, _ = get_deployed_kdu(
+            db_nsr.get("_admin", ()).get("deployed", ()),
+            kdu_name,
+            ee_relation.vnf_profile_id,
+        )
+        deployed_kdu.update({"resource-name": kdu_resource_profile["resource-name"]})
+        return deployed_kdu
+
+    def _get_deployed_component(
+        self,
+        ee_relation: EERelation,
+        db_nsr: Dict[str, Any],
+        cached_vnfds: Dict[str, Any],
+    ) -> DeployedComponent:
+        nsr_id = db_nsr["_id"]
+        deployed_component = None
+        ee_level = EELevel.get_level(ee_relation)
+        if ee_level == EELevel.NS:
+            vca = get_deployed_vca(db_nsr, {"vdu_id": None, "member-vnf-index": None})
+            if vca:
+                deployed_component = DeployedVCA(nsr_id, vca)
+        elif ee_level == EELevel.VNF:
+            vca = get_deployed_vca(
+                db_nsr,
+                {
+                    "vdu_id": None,
+                    "member-vnf-index": ee_relation.vnf_profile_id,
+                    "ee_descriptor_id": ee_relation.execution_environment_ref,
+                },
+            )
+            if vca:
+                deployed_component = DeployedVCA(nsr_id, vca)
+        elif ee_level == EELevel.VDU:
+            vca = get_deployed_vca(
+                db_nsr,
+                {
+                    "vdu_id": ee_relation.vdu_profile_id,
+                    "member-vnf-index": ee_relation.vnf_profile_id,
+                    "ee_descriptor_id": ee_relation.execution_environment_ref,
+                },
+            )
+            if vca:
+                deployed_component = DeployedVCA(nsr_id, vca)
+        elif ee_level == EELevel.KDU:
+            kdu_resource_data = self._get_kdu_resource_data(
+                ee_relation, db_nsr, cached_vnfds
+            )
+            if kdu_resource_data:
+                deployed_component = DeployedK8sResource(kdu_resource_data)
+        return deployed_component
+
+    async def _add_relation(
+        self,
+        relation: Relation,
+        vca_type: str,
+        db_nsr: Dict[str, Any],
+        cached_vnfds: Dict[str, Any],
+        cached_vnfrs: Dict[str, Any],
+    ) -> bool:
+        deployed_provider = self._get_deployed_component(
+            relation.provider, db_nsr, cached_vnfds
+        )
+        deployed_requirer = self._get_deployed_component(
+            relation.requirer, db_nsr, cached_vnfds
+        )
+        if (
+            deployed_provider
+            and deployed_requirer
+            and deployed_provider.config_sw_installed
+            and deployed_requirer.config_sw_installed
+        ):
+            provider_db_vnfr = (
+                self._get_vnfr(
+                    relation.provider.nsr_id,
+                    relation.provider.vnf_profile_id,
+                    cached_vnfrs,
+                )
+                if relation.provider.vnf_profile_id
+                else None
+            )
+            requirer_db_vnfr = (
+                self._get_vnfr(
+                    relation.requirer.nsr_id,
+                    relation.requirer.vnf_profile_id,
+                    cached_vnfrs,
+                )
+                if relation.requirer.vnf_profile_id
+                else None
+            )
+            provider_vca_id = self.get_vca_id(provider_db_vnfr, db_nsr)
+            requirer_vca_id = self.get_vca_id(requirer_db_vnfr, db_nsr)
+            provider_relation_endpoint = RelationEndpoint(
+                deployed_provider.ee_id,
+                provider_vca_id,
+                relation.provider.endpoint,
+            )
+            requirer_relation_endpoint = RelationEndpoint(
+                deployed_requirer.ee_id,
+                requirer_vca_id,
+                relation.requirer.endpoint,
+            )
+            await self.vca_map[vca_type].add_relation(
+                provider=provider_relation_endpoint,
+                requirer=requirer_relation_endpoint,
+            )
+            # remove entry from relations list
+            return True
+        return False
+
     async def _add_vca_relations(
         self,
         logging_text,
         nsr_id,
+        vca_type: str,
         vca_index: int,
         timeout: int = 3600,
-        vca_type: str = None,
-        vca_id: str = None,
     ) -> bool:
 
         # steps:
@@ -2637,59 +2980,28 @@ class NsLcm(LcmBase):
         # 3. add relations
 
         try:
-            vca_type = vca_type or "lxc_proxy_charm"
-
             # STEP 1: find all relations for this VCA
 
             # read nsr record
             db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
-            nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
+            nsd = get_nsd(db_nsr)
 
             # this VCA data
-            my_vca = deep_get(db_nsr, ("_admin", "deployed", "VCA"))[vca_index]
-
-            # read all ns-configuration relations
-            ns_relations = list()
-            db_ns_relations = deep_get(nsd, ("ns-configuration", "relation"))
-            if db_ns_relations:
-                for r in db_ns_relations:
-                    # check if this VCA is in the relation
-                    if my_vca.get("member-vnf-index") in (
-                        r.get("entities")[0].get("id"),
-                        r.get("entities")[1].get("id"),
-                    ):
-                        ns_relations.append(r)
-
-            # read all vnf-configuration relations
-            vnf_relations = list()
-            db_vnfd_list = db_nsr.get("vnfd-id")
-            if db_vnfd_list:
-                for vnfd in db_vnfd_list:
-                    db_vnf_relations = None
-                    db_vnfd = self.db.get_one("vnfds", {"_id": vnfd})
-                    db_vnf_configuration = get_configuration(db_vnfd, db_vnfd["id"])
-                    if db_vnf_configuration:
-                        db_vnf_relations = db_vnf_configuration.get("relation", [])
-                    if db_vnf_relations:
-                        for r in db_vnf_relations:
-                            # check if this VCA is in the relation
-                            if my_vca.get("vdu_id") in (
-                                r.get("entities")[0].get("id"),
-                                r.get("entities")[1].get("id"),
-                            ):
-                                vnf_relations.append(r)
+            deployed_vca_dict = get_deployed_vca_list(db_nsr)[vca_index]
+            my_vca = DeployedVCA(nsr_id, deployed_vca_dict)
+
+            cached_vnfds = {}
+            cached_vnfrs = {}
+            relations = []
+            relations.extend(self._get_ns_relations(nsr_id, nsd, my_vca, cached_vnfds))
+            relations.extend(self._get_vnf_relations(nsr_id, nsd, my_vca, cached_vnfds))
 
             # if no relations, terminate
-            if not ns_relations and not vnf_relations:
+            if not relations:
                 self.logger.debug(logging_text + " No relations")
                 return True
 
-            self.logger.debug(
-                logging_text
-                + " adding relations\n    {}\n    {}".format(
-                    ns_relations, vnf_relations
-                )
-            )
+            self.logger.debug(logging_text + " adding relations {}".format(relations))
 
             # add all relations
             start = time()
@@ -2700,124 +3012,25 @@ class NsLcm(LcmBase):
                     self.logger.error(logging_text + " : timeout adding relations")
                     return False
 
-                # reload nsr from database (we need to update record: _admin.deloyed.VCA)
+                # reload nsr from database (we need to update record: _admin.deployed.VCA)
                 db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
 
-                # for each defined NS relation, find the VCA's related
-                for r in ns_relations.copy():
-                    from_vca_ee_id = None
-                    to_vca_ee_id = None
-                    from_vca_endpoint = None
-                    to_vca_endpoint = None
-                    vca_list = deep_get(db_nsr, ("_admin", "deployed", "VCA"))
-                    for vca in vca_list:
-                        if vca.get("member-vnf-index") == r.get("entities")[0].get(
-                            "id"
-                        ) and vca.get("config_sw_installed"):
-                            from_vca_ee_id = vca.get("ee_id")
-                            from_vca_endpoint = r.get("entities")[0].get("endpoint")
-                        if vca.get("member-vnf-index") == r.get("entities")[1].get(
-                            "id"
-                        ) and vca.get("config_sw_installed"):
-                            to_vca_ee_id = vca.get("ee_id")
-                            to_vca_endpoint = r.get("entities")[1].get("endpoint")
-                    if from_vca_ee_id and to_vca_ee_id:
-                        # add relation
-                        await self.vca_map[vca_type].add_relation(
-                            ee_id_1=from_vca_ee_id,
-                            ee_id_2=to_vca_ee_id,
-                            endpoint_1=from_vca_endpoint,
-                            endpoint_2=to_vca_endpoint,
-                            vca_id=vca_id,
-                        )
-                        # remove entry from relations list
-                        ns_relations.remove(r)
-                    else:
-                        # check failed peers
-                        try:
-                            vca_status_list = db_nsr.get("configurationStatus")
-                            if vca_status_list:
-                                for i in range(len(vca_list)):
-                                    vca = vca_list[i]
-                                    vca_status = vca_status_list[i]
-                                    if vca.get("member-vnf-index") == r.get("entities")[
-                                        0
-                                    ].get("id"):
-                                        if vca_status.get("status") == "BROKEN":
-                                            # peer broken: remove relation from list
-                                            ns_relations.remove(r)
-                                    if vca.get("member-vnf-index") == r.get("entities")[
-                                        1
-                                    ].get("id"):
-                                        if vca_status.get("status") == "BROKEN":
-                                            # peer broken: remove relation from list
-                                            ns_relations.remove(r)
-                        except Exception:
-                            # ignore
-                            pass
-
-                # for each defined VNF relation, find the VCA's related
-                for r in vnf_relations.copy():
-                    from_vca_ee_id = None
-                    to_vca_ee_id = None
-                    from_vca_endpoint = None
-                    to_vca_endpoint = None
-                    vca_list = deep_get(db_nsr, ("_admin", "deployed", "VCA"))
-                    for vca in vca_list:
-                        key_to_check = "vdu_id"
-                        if vca.get("vdu_id") is None:
-                            key_to_check = "vnfd_id"
-                        if vca.get(key_to_check) == r.get("entities")[0].get(
-                            "id"
-                        ) and vca.get("config_sw_installed"):
-                            from_vca_ee_id = vca.get("ee_id")
-                            from_vca_endpoint = r.get("entities")[0].get("endpoint")
-                        if vca.get(key_to_check) == r.get("entities")[1].get(
-                            "id"
-                        ) and vca.get("config_sw_installed"):
-                            to_vca_ee_id = vca.get("ee_id")
-                            to_vca_endpoint = r.get("entities")[1].get("endpoint")
-                    if from_vca_ee_id and to_vca_ee_id:
-                        # add relation
-                        await self.vca_map[vca_type].add_relation(
-                            ee_id_1=from_vca_ee_id,
-                            ee_id_2=to_vca_ee_id,
-                            endpoint_1=from_vca_endpoint,
-                            endpoint_2=to_vca_endpoint,
-                            vca_id=vca_id,
-                        )
-                        # remove entry from relations list
-                        vnf_relations.remove(r)
-                    else:
-                        # check failed peers
-                        try:
-                            vca_status_list = db_nsr.get("configurationStatus")
-                            if vca_status_list:
-                                for i in range(len(vca_list)):
-                                    vca = vca_list[i]
-                                    vca_status = vca_status_list[i]
-                                    if vca.get("vdu_id") == r.get("entities")[0].get(
-                                        "id"
-                                    ):
-                                        if vca_status.get("status") == "BROKEN":
-                                            # peer broken: remove relation from list
-                                            vnf_relations.remove(r)
-                                    if vca.get("vdu_id") == r.get("entities")[1].get(
-                                        "id"
-                                    ):
-                                        if vca_status.get("status") == "BROKEN":
-                                            # peer broken: remove relation from list
-                                            vnf_relations.remove(r)
-                        except Exception:
-                            # ignore
-                            pass
-
-                # wait for next try
-                await asyncio.sleep(5.0)
+                # for each relation, find the VCA's related
+                for relation in relations.copy():
+                    added = await self._add_relation(
+                        relation,
+                        vca_type,
+                        db_nsr,
+                        cached_vnfds,
+                        cached_vnfrs,
+                    )
+                    if added:
+                        relations.remove(relation)
 
-                if not ns_relations and not vnf_relations:
+                if not relations:
                     self.logger.debug("Relations added")
                     break
+                await asyncio.sleep(5.0)
 
             return True
 
@@ -3123,16 +3336,21 @@ class NsLcm(LcmBase):
                             db_vnfds, lambda vnfd: vnfd["_id"] == vnfd_id
                         )
                         storage = deep_get(vnfd_with_id, ("_admin", "storage"))
-                        if storage and storage.get(
-                            "pkg-dir"
-                        ):  # may be not present if vnfd has not artifacts
+                        if storage:  # may be not present if vnfd has not artifacts
                             # path format: /vnfdid/pkkdir/helm-charts|juju-bundles/kdumodel
-                            filename = "{}/{}/{}s/{}".format(
-                                storage["folder"],
-                                storage["pkg-dir"],
-                                k8sclustertype,
-                                kdumodel,
-                            )
+                            if storage["pkg-dir"]:
+                                filename = "{}/{}/{}s/{}".format(
+                                    storage["folder"],
+                                    storage["pkg-dir"],
+                                    k8sclustertype,
+                                    kdumodel,
+                                )
+                            else:
+                                filename = "{}/Scripts/{}s/{}".format(
+                                    storage["folder"],
+                                    k8sclustertype,
+                                    kdumodel,
+                                )
                             if self.fs.file_exists(
                                 filename, mode="file"
                             ) or self.fs.file_exists(filename, mode="dir"):
@@ -3740,13 +3958,15 @@ class NsLcm(LcmBase):
                     "nsrs", db_nslcmop["nsInstanceId"], {db_update_entry: False}
                 )
 
-        if vca_deployed.get("prometheus_jobs") and self.prometheus:
-            await self.prometheus.update(remove_jobs=vca_deployed["prometheus_jobs"])
+        # Delete Prometheus Jobs if any
+        # This uses NSR_ID, so it will destroy any jobs under this index
+        self.db.del_list("prometheus_jobs", {"nsr_id": db_nslcmop["nsInstanceId"]})
 
         if destroy_ee:
             await self.vca_map[vca_type].delete_execution_environment(
                 vca_deployed["ee_id"],
                 scaling_in=scaling_in,
+                vca_type=vca_type,
                 vca_id=vca_id,
             )
 
@@ -4520,6 +4740,7 @@ class NsLcm(LcmBase):
                             total_timeout=self.timeout_primitive,
                             db_dict=db_dict,
                             vca_id=vca_id,
+                            vca_type=vca_type,
                         ),
                         timeout=timeout or self.timeout_primitive,
                     )
@@ -4625,6 +4846,9 @@ class NsLcm(LcmBase):
                 )
                 step = "Getting vnfd from database"
                 db_vnfd = self.db.get_one("vnfds", {"_id": db_vnfr["vnfd-id"]})
+
+                # Sync filesystem before running a primitive
+                self.fs.sync(db_vnfr["vnfd-id"])
             else:
                 step = "Getting nsd from database"
                 db_nsd = self.db.get_one("nsds", {"_id": db_nsr["nsd-id"]})
@@ -5107,9 +5331,9 @@ class NsLcm(LcmBase):
                                 )
                         scaling_info["vdu-create"][vdu_delta["id"]] = instances_number
                     for kdu_delta in delta.get("kdu-resource-delta", {}):
-                        kdu_profile = get_kdu_profile(db_vnfd, kdu_delta["id"])
+                        kdu_profile = get_kdu_resource_profile(db_vnfd, kdu_delta["id"])
                         kdu_name = kdu_profile["kdu-name"]
-                        resource_name = kdu_profile["resource-name"]
+                        resource_name = kdu_profile.get("resource-name", "")
 
                         # Might have different kdus in the same delta
                         # Should have list for each kdu
@@ -5125,7 +5349,6 @@ class NsLcm(LcmBase):
                                 and kdur.get("helm-version") == "v2"
                             ):
                                 k8s_cluster_type = "helm-chart"
-                            raise NotImplementedError
                         elif kdur.get("juju-bundle"):
                             k8s_cluster_type = "juju-bundle"
                         else:
@@ -5155,7 +5378,13 @@ class NsLcm(LcmBase):
                         kdu_instance = deployed_kdu.get("kdu-instance")
                         instance_num = await self.k8scluster_map[
                             k8s_cluster_type
-                        ].get_scale_count(resource_name, kdu_instance, vca_id=vca_id)
+                        ].get_scale_count(
+                            resource_name,
+                            kdu_instance,
+                            vca_id=vca_id,
+                            cluster_uuid=deployed_kdu.get("k8scluster-uuid"),
+                            kdu_model=deployed_kdu.get("kdu-model"),
+                        )
                         kdu_replica_count = instance_num + kdu_delta.get(
                             "number-of-instances", 1
                         )
@@ -5237,9 +5466,9 @@ class NsLcm(LcmBase):
                             )
                         scaling_info["vdu-delete"][vdu_delta["id"]] = instances_number
                     for kdu_delta in delta.get("kdu-resource-delta", {}):
-                        kdu_profile = get_kdu_profile(db_vnfd, kdu_delta["id"])
+                        kdu_profile = get_kdu_resource_profile(db_vnfd, kdu_delta["id"])
                         kdu_name = kdu_profile["kdu-name"]
-                        resource_name = kdu_profile["resource-name"]
+                        resource_name = kdu_profile.get("resource-name", "")
 
                         if not scaling_info["kdu-delete"].get(kdu_name, None):
                             scaling_info["kdu-delete"][kdu_name] = []
@@ -5253,7 +5482,6 @@ class NsLcm(LcmBase):
                                 and kdur.get("helm-version") == "v2"
                             ):
                                 k8s_cluster_type = "helm-chart"
-                            raise NotImplementedError
                         elif kdur.get("juju-bundle"):
                             k8s_cluster_type = "juju-bundle"
                         else:
@@ -5281,7 +5509,13 @@ class NsLcm(LcmBase):
                         kdu_instance = deployed_kdu.get("kdu-instance")
                         instance_num = await self.k8scluster_map[
                             k8s_cluster_type
-                        ].get_scale_count(resource_name, kdu_instance, vca_id=vca_id)
+                        ].get_scale_count(
+                            resource_name,
+                            kdu_instance,
+                            vca_id=vca_id,
+                            cluster_uuid=deployed_kdu.get("k8scluster-uuid"),
+                            kdu_model=deployed_kdu.get("kdu-model"),
+                        )
                         kdu_replica_count = instance_num - kdu_delta.get(
                             "number-of-instances", 1
                         )
@@ -5386,7 +5620,6 @@ class NsLcm(LcmBase):
                         # Pre-scale retry check: Check if this sub-operation has been executed before
                         op_index = self._check_or_add_scale_suboperation(
                             db_nslcmop,
-                            nslcmop_id,
                             vnf_index,
                             vnf_config_primitive,
                             primitive_params,
@@ -5795,7 +6028,6 @@ class NsLcm(LcmBase):
                         # Post-scale retry check: Check if this sub-operation has been executed before
                         op_index = self._check_or_add_scale_suboperation(
                             db_nslcmop,
-                            nslcmop_id,
                             vnf_index,
                             vnf_config_primitive,
                             primitive_params,
@@ -5989,6 +6221,7 @@ class NsLcm(LcmBase):
                 )
                 cluster_uuid = deployed_kdu["k8scluster-uuid"]
                 kdu_instance = deployed_kdu["kdu-instance"]
+                kdu_model = deployed_kdu.get("kdu-model")
                 scale = int(kdu_scaling_info["scale"])
                 k8s_cluster_type = kdu_scaling_info["k8s-cluster-type"]
 
@@ -6043,6 +6276,10 @@ class NsLcm(LcmBase):
                         scale,
                         kdu_scaling_info["resource-name"],
                         vca_id=vca_id,
+                        cluster_uuid=cluster_uuid,
+                        kdu_model=kdu_model,
+                        atomic=True,
+                        db_dict=db_dict,
                     ),
                     timeout=self.timeout_vca_on_error,
                 )
@@ -6126,11 +6363,15 @@ class NsLcm(LcmBase):
                 db_vnfr, None, vdu_scaling_info["vdu-delete"], mark_delete=False
             )
 
-    async def add_prometheus_metrics(
-        self, ee_id, artifact_path, ee_config_descriptor, vnfr_id, nsr_id, target_ip
+    async def extract_prometheus_scrape_jobs(
+        self,
+        ee_id,
+        artifact_path,
+        ee_config_descriptor,
+        vnfr_id,
+        nsr_id,
+        target_ip
     ):
-        if not self.prometheus:
-            return
         # look if exist a file called 'prometheus*.j2' and
         artifact_content = self.fs.dir_ls(artifact_path)
         job_file = next(
@@ -6157,7 +6398,7 @@ class NsLcm(LcmBase):
             "EXPORTER_POD_IP": host_name,
             "EXPORTER_POD_PORT": host_port,
         }
-        job_list = self.prometheus.parse_job(job_data, variables)
+        job_list = parse_job(job_data, variables)
         # ensure job_name is using the vnfr_id. Adding the metadata nsr_id
         for job in job_list:
             if (
@@ -6166,9 +6407,8 @@ class NsLcm(LcmBase):
             ):
                 job["job_name"] = vnfr_id + "_" + str(randint(1, 10000))
             job["nsr_id"] = nsr_id
-        job_dict = {jl["job_name"]: jl for jl in job_list}
-        if await self.prometheus.update(job_dict):
-            return list(job_dict.keys())
+            job["vnfr_id"] = vnfr_id
+        return job_list
 
     def get_vca_cloud_and_credentials(self, vim_account_id: str) -> (str, str):
         """