Feature 10887: Add cross-model relations support 98/11298/4 release-v11.0-start
authorDavid Garcia <david.garcia@canonical.com>
Thu, 28 Oct 2021 00:00:43 +0000 (02:00 +0200)
committerDavid Garcia <david.garcia@canonical.com>
Wed, 3 Nov 2021 10:48:39 +0000 (11:48 +0100)
Changes:
- Update the `osm_lcm.ns.NsLcm._add_vca_relations` function, and
decouple it into simpler and smaller functions.
- Get relation data for the descriptor taking into account the changes
introduced here: https://osm.etsi.org/gerrit/11212/
- Add `osm_lcm.data_utils.vca` module to take care of VCA related parts
in the IM
- Add a couple of functions into the `osm_lcm.data_utils` package:
  - nsd: get_vnf_profile(), get_ns_configuration(),
get_ns_configuration_relation_list()
  - nsr: get_nsd(), get_deployed_vca_list(), get_deployed_vca()
  - vnfd: get_relation_list()
- Rename `osm_lcm.data_utils.vnfd.get_kdu_profile` to `osm_lcm.data_utils.vnfd.get_kdu_resource_profile`

Change-Id: I6da29e656d092e17c85b44f5b3960a6ca3aa3ad8
Signed-off-by: David Garcia <david.garcia@canonical.com>
osm_lcm/data_utils/nsd.py
osm_lcm/data_utils/nsr.py
osm_lcm/data_utils/vca.py [new file with mode: 0644]
osm_lcm/data_utils/vnfd.py
osm_lcm/ns.py

index 2917c04..9b6fb81 100644 (file)
 ##
 import ast
 
+from osm_lcm.data_utils.list_utils import find_in_list
+
 
 def get_vnf_profiles(nsd):
     return nsd.get("df")[0].get("vnf-profile", ())
 
 
+def get_vnf_profile(nsd, vnf_profile_id):
+    return find_in_list(
+        get_vnf_profiles(nsd),
+        lambda vnf_profile: vnf_profile["id"] == vnf_profile_id,
+    )
+
+
 def get_virtual_link_profiles(nsd):
     return nsd.get("df")[0].get("virtual-link-profile", ())
 
@@ -36,3 +45,11 @@ def replace_vnf_id(nsd, old_vnf_id, new_vnf_id):
     dict_str = str(nsd)
     dict_str.replace(old_vnf_id, new_vnf_id)
     return ast.literal_eval(dict_str)
+
+
+def get_ns_configuration(nsd):
+    return nsd.get("ns-configuration", {})
+
+
+def get_ns_configuration_relation_list(nsd):
+    return get_ns_configuration(nsd).get("relation", [])
index 006713c..f2fccb6 100644 (file)
@@ -22,6 +22,7 @@
 # contact: fbravo@whitestack.com
 ##
 
+from osm_lcm.data_utils import list_utils
 from osm_lcm.lcm_utils import get_iterable
 
 
@@ -39,3 +40,18 @@ def get_deployed_kdu(nsr_deployed, kdu_name, member_vnf_index):
         ):
             break
     return deployed_kdu, index
+
+
+def get_nsd(nsr):
+    return nsr.get("nsd", {})
+
+
+def get_deployed_vca_list(nsr):
+    return nsr.get("_admin", ()).get("deployed", ()).get("VCA", [])
+
+
+def get_deployed_vca(nsr, filter):
+    return list_utils.find_in_list(
+        get_deployed_vca_list(nsr),
+        lambda vca: all(vca[key] == value for key, value in filter.items()),
+    )
diff --git a/osm_lcm/data_utils/vca.py b/osm_lcm/data_utils/vca.py
new file mode 100644 (file)
index 0000000..2116549
--- /dev/null
@@ -0,0 +1,222 @@
+# Copyright 2021 Canonical Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+#     Unless required by applicable law or agreed to in writing, software
+#     distributed under the License is distributed on an "AS IS" BASIS,
+#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#     See the License for the specific language governing permissions and
+#     limitations under the License.
+
+from typing import Any, Dict, NoReturn
+
+
+def safe_get_ee_relation(
+    nsr_id: str, ee_relation: Dict[str, Any], vnf_profile_id: str = None
+) -> Dict[str, Any]:
+    return {
+        "nsr-id": nsr_id,
+        "vnf-profile-id": ee_relation.get("vnf-profile-id") or vnf_profile_id,
+        "vdu-profile-id": ee_relation.get("vdu-profile-id"),
+        "kdu-resource-profile-id": ee_relation.get("kdu-resource-profile-id"),
+        "execution-environment-ref": ee_relation.get("execution-environment-ref"),
+        "endpoint": ee_relation["endpoint"],
+    }
+
+
+class EELevel:
+    VDU = "vdu"
+    VNF = "vnf"
+    KDU = "kdu"
+    NS = "ns"
+
+    @staticmethod
+    def get_level(ee_relation: dict):
+        """Get the execution environment level"""
+        level = None
+        if (
+            not ee_relation["vnf-profile-id"]
+            and not ee_relation["vdu-profile-id"]
+            and not ee_relation["kdu-resource-profile-id"]
+        ):
+            level = EELevel.NS
+        elif (
+            ee_relation["vnf-profile-id"]
+            and not ee_relation["vdu-profile-id"]
+            and not ee_relation["kdu-resource-profile-id"]
+        ):
+            level = EELevel.VNF
+        elif (
+            ee_relation["vnf-profile-id"]
+            and ee_relation["vdu-profile-id"]
+            and not ee_relation["kdu-resource-profile-id"]
+        ):
+            level = EELevel.VDU
+        elif (
+            ee_relation["vnf-profile-id"]
+            and not ee_relation["vdu-profile-id"]
+            and ee_relation["kdu-resource-profile-id"]
+        ):
+            level = EELevel.KDU
+        else:
+            raise Exception("invalid relation endpoint")
+        return level
+
+
+class EERelation(dict):
+    """Represents the execution environment of a relation"""
+
+    def __init__(
+        self,
+        relation_ee: Dict[str, Any],
+    ) -> NoReturn:
+        """
+        Args:
+            relation_ee: Relation Endpoint object in the VNFd or NSd.
+                      Example:
+                        {
+                            "nsr-id": <>,
+                            "vdu-profile-id": <>,
+                            "kdu-resource-profile-id": <>,
+                            "vnf-profile-id": <>,
+                            "execution-environment-ref": <>,
+                            "endpoint": <>,
+                        }
+        """
+        for key, value in relation_ee.items():
+            self.__setitem__(key, value)
+
+    @property
+    def vdu_profile_id(self):
+        """Returns the vdu-profile id"""
+        return self["vdu-profile-id"]
+
+    @property
+    def kdu_resource_profile_id(self):
+        """Returns the kdu-resource-profile id"""
+        return self["kdu-resource-profile-id"]
+
+    @property
+    def vnf_profile_id(self):
+        """Returns the vnf-profile id"""
+        return self["vnf-profile-id"]
+
+    @property
+    def execution_environment_ref(self):
+        """Returns the reference to the execution environment (id)"""
+        return self["execution-environment-ref"]
+
+    @property
+    def endpoint(self):
+        """Returns the endpoint of the execution environment"""
+        return self["endpoint"]
+
+    @property
+    def nsr_id(self) -> str:
+        """Returns the nsr id"""
+        return self["nsr-id"]
+
+
+class Relation(dict):
+    """Represents a relation"""
+    def __init__(self, name, provider: EERelation, requirer: EERelation) -> NoReturn:
+        """
+        Args:
+            name: Name of the relation.
+            provider: Execution environment that provides the service for the relation.
+            requirer: Execution environment that requires the service from the provider.
+        """
+        self.__setitem__("name", name)
+        self.__setitem__("provider", provider)
+        self.__setitem__("requirer", requirer)
+
+    @property
+    def name(self) -> str:
+        """Returns the name of the relation"""
+        return self["name"]
+
+    @property
+    def provider(self) -> EERelation:
+        """Returns the provider endpoint"""
+        return self["provider"]
+
+    @property
+    def requirer(self) -> EERelation:
+        """Returns the requirer endpoint"""
+        return self["requirer"]
+
+
+class DeployedComponent(dict):
+    """Represents a deployed component (nsr["_admin"]["deployed"])"""
+    def __init__(self, data: Dict[str, Any]):
+        """
+        Args:
+            data: dictionary with the data of the deployed component
+        """
+        for key, value in data.items():
+            self.__setitem__(key, value)
+
+    @property
+    def vnf_profile_id(self):
+        """Returns the vnf-profile id"""
+        return self["member-vnf-index"]
+
+    @property
+    def ee_id(self):
+        raise NotImplementedError()
+
+    @property
+    def config_sw_installed(self) -> bool:
+        raise NotImplementedError()
+
+
+class DeployedK8sResource(DeployedComponent):
+    """Represents a deployed component for a kdu resource"""
+    def __init__(self, data: Dict[str, Any]):
+        super().__init__(data)
+
+    @property
+    def ee_id(self):
+        """Returns the execution environment id"""
+        model = self["kdu-instance"]
+        application_name = self["resource-name"]
+        return f"{model}.{application_name}.k8s"
+
+    @property
+    def config_sw_installed(self) -> bool:
+        return True
+
+
+class DeployedVCA(DeployedComponent):
+    """Represents a VCA deployed component"""
+    def __init__(self, nsr_id: str, deployed_vca: Dict[str, Any]) -> NoReturn:
+        """
+        Args:
+            db_nsr: NS record
+            vca_index: Vca index for the deployed VCA
+        """
+        super().__init__(deployed_vca)
+        self.nsr_id = nsr_id
+
+    @property
+    def ee_id(self) -> str:
+        """Returns the execution environment id"""
+        return self["ee_id"]
+
+    @property
+    def vdu_profile_id(self) -> str:
+        """Returns the vdu-profile id"""
+        return self["vdu_id"]
+
+    @property
+    def execution_environment_ref(self) -> str:
+        """Returns the execution environment id"""
+        return self["ee_descriptor_id"]
+
+    @property
+    def config_sw_installed(self) -> bool:
+        return self.get("config_sw_installed", False)
index 17a98a9..714487c 100644 (file)
@@ -101,7 +101,7 @@ def get_vdu_profile(vnfd, vdu_profile_id):
     )
 
 
-def get_kdu_profile(vnfd, kdu_profile_id):
+def get_kdu_resource_profile(vnfd, kdu_profile_id):
     return list_utils.find_in_list(
         vnfd.get("df", ())[0]["kdu-resource-profile"],
         lambda kdu_profile: kdu_profile["id"] == kdu_profile_id,
@@ -121,6 +121,10 @@ def get_configuration(vnfd, entity_id):
     )
 
 
+def get_relation_list(vnfd, entity_id):
+    return (get_configuration(vnfd, entity_id) or {}).get("relation", [])
+
+
 def get_virtual_link_profiles(vnfd):
     return vnfd.get("df")[0].get("virtual-link-profile", ())
 
index fc2c9f6..ab25be3 100644 (file)
@@ -17,6 +17,7 @@
 ##
 
 import asyncio
+from typing import Any, Dict
 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
@@ -1827,9 +1848,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
@@ -2630,14 +2650,249 @@ 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],
+    ):
+        relations = []
+        db_ns_relations = get_ns_configuration_relation_list(nsd)
+        for r in db_ns_relations:
+            relation_provider = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, r["provider"], cached_vnfds
+            )
+            relation_requirer = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, r["requirer"], 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],
+    ):
+        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:
+            relation_provider = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, r["provider"], cached_vnfds, vnf_profile_id=vnf_profile_id
+            )
+            relation_requirer = self._update_ee_relation_data_with_implicit_data(
+                nsr_id, nsd, r["requirer"], 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:
@@ -2646,59 +2901,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()
@@ -2709,124 +2933,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
 
@@ -5118,7 +5243,7 @@ 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"]
 
@@ -5248,7 +5373,7 @@ 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"]