Bug 1972 fixed
[osm/NBI.git] / osm_nbi / instance_topics.py
index 4e88de5..006dbc4 100644 (file)
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 # import logging
+import json
 from uuid import uuid4
 from http import HTTPStatus
 from time import time
@@ -164,9 +165,14 @@ class NsrTopic(BaseTopic):
         ns_request, member_vnf_index=None, vdu_id=None, kdu_name=None, descriptor=None
     ):
         """
-        Get and format user additional params for NS or VNF
+        Get and format user additional params for NS or VNF.
+        The vdu_id and kdu_name params are mutually exclusive! If none of them are given, then the method will
+        exclusively search for the VNF/NS LCM additional params.
+
         :param ns_request: User instantiation additional parameters
         :param member_vnf_index: None for extract NS params, or member_vnf_index to extract VNF params
+        :vdu_id: VDU's ID against which we want to format the additional params
+        :kdu_name: KDU's name against which we want to format the additional params
         :param descriptor: If not None it check that needed parameters of descriptor are supplied
         :return: tuple with a formatted copy of additional params or None if not supplied, plus other parameters
         """
@@ -239,14 +245,19 @@ class NsrTopic(BaseTopic):
                             where_, k
                         )
                     )
-                if "." in k or "$" in k:
+                if "$" in k:
                     raise EngineException(
-                        "Invalid param at {}:{}. Keys must not contain dots or $".format(
+                        "Invalid param at {}:{}. Keys must not contain $ symbol".format(
                             where_, k
                         )
                     )
                 if isinstance(v, (dict, tuple, list)):
                     additional_params[k] = "!!yaml " + safe_dump(v)
+            if kdu_name:
+                additional_params = json.dumps(additional_params)
+
+        # Select the VDU ID, KDU name or NS/VNF ID, depending on the method's call intent
+        selector = vdu_id if vdu_id else kdu_name if kdu_name else descriptor.get("id")
 
         if descriptor:
             for df in descriptor.get("df", []):
@@ -262,10 +273,13 @@ class NsrTopic(BaseTopic):
                         for config in df["lcm-operations-configuration"][
                             "operate-vnf-op-config"
                         ].get("day1-2", []):
-                            for primitive in get_iterable(
-                                config.get("initial-config-primitive")
-                            ):
-                                initial_primitives.append(primitive)
+                            # Verify the target object (VNF|NS|VDU|KDU) where we need to populate 
+                            # the params with the additional ones given by the user
+                            if config.get("id") == selector:
+                                for primitive in get_iterable(
+                                    config.get("initial-config-primitive")
+                                ):
+                                    initial_primitives.append(primitive)
                 else:
                     initial_primitives = deep_get(
                         descriptor, ("ns-configuration", "initial-config-primitive")
@@ -474,11 +488,11 @@ class NsrTopic(BaseTopic):
             "ssh-authorized-key": ns_request.get("ssh_keys"),  # TODO remove
             "flavor": [],
             "image": [],
+            "affinity-or-anti-affinity-group": [],
         }
         ns_request["nsr_id"] = nsr_id
         if ns_request and ns_request.get("config-units"):
             nsr_descriptor["config-units"] = ns_request["config-units"]
-
         # Create vld
         if nsd.get("virtual-link-desc"):
             nsr_vld = deepcopy(nsd.get("virtual-link-desc", []))
@@ -589,6 +603,29 @@ class NsrTopic(BaseTopic):
                         image_data = self._get_image_data_from_vnfd(vnfd, alt_image)
                         self._add_image_to_nsr(nsr_descriptor, image_data)
 
+                # Add Affinity or Anti-affinity group information to NSR
+                vdu_profiles = vnfd.get("df", [[]])[0].get("vdu-profile", ())
+                affinity_group_prefix_name = "{}-{}".format(
+                    nsr_descriptor["name"][:16], vnf_profile.get("id")[:16]
+                )
+
+                for vdu_profile in vdu_profiles:
+                    affinity_group_data = {}
+                    for affinity_group in vdu_profile.get(
+                        "affinity-or-anti-affinity-group", ()
+                    ):
+                        affinity_group_data = (
+                            self._get_affinity_or_anti_affinity_group_data_from_vnfd(
+                                vnfd, affinity_group["id"]
+                            )
+                        )
+                        affinity_group_data["member-vnf-index"] = vnf_profile.get("id")
+                        self._add_affinity_or_anti_affinity_group_to_nsr(
+                            nsr_descriptor,
+                            affinity_group_data,
+                            affinity_group_prefix_name,
+                        )
+
             for vld in nsr_vld:
                 vld["vnfd-connection-point-ref"] = all_vld_connection_point_data.get(
                     vld.get("id"), []
@@ -598,6 +635,51 @@ class NsrTopic(BaseTopic):
 
         return nsr_descriptor
 
+    def _get_affinity_or_anti_affinity_group_data_from_vnfd(
+        self, vnfd, affinity_group_id
+    ):
+        """
+        Gets affinity-or-anti-affinity-group info from df and returns the desired affinity group
+        """
+        affinity_group = utils.find_in_list(
+            vnfd.get("df", [[]])[0].get("affinity-or-anti-affinity-group", ()),
+            lambda ag: ag["id"] == affinity_group_id,
+        )
+        affinity_group_data = {}
+        if affinity_group:
+            if affinity_group.get("id"):
+                affinity_group_data["ag-id"] = affinity_group["id"]
+            if affinity_group.get("type"):
+                affinity_group_data["type"] = affinity_group["type"]
+            if affinity_group.get("scope"):
+                affinity_group_data["scope"] = affinity_group["scope"]
+        return affinity_group_data
+
+    def _add_affinity_or_anti_affinity_group_to_nsr(
+        self, nsr_descriptor, affinity_group_data, affinity_group_prefix_name
+    ):
+        """
+        Adds affinity-or-anti-affinity-group to nsr checking first it is not already added
+        """
+        affinity_group = next(
+            (
+                f
+                for f in nsr_descriptor["affinity-or-anti-affinity-group"]
+                if all(f.get(k) == affinity_group_data[k] for k in affinity_group_data)
+            ),
+            None,
+        )
+        if not affinity_group:
+            affinity_group_data["id"] = str(
+                len(nsr_descriptor["affinity-or-anti-affinity-group"])
+            )
+            affinity_group_data["name"] = "{}-{}".format(
+                affinity_group_prefix_name, affinity_group_data["ag-id"][:32]
+            )
+            nsr_descriptor["affinity-or-anti-affinity-group"].append(
+                affinity_group_data
+            )
+
     def _get_image_data_from_vnfd(self, vnfd, sw_image_id):
         sw_image_desc = utils.find_in_list(
             vnfd.get("sw-image-desc", ()), lambda sw: sw["id"] == sw_image_id
@@ -766,6 +848,14 @@ class NsrTopic(BaseTopic):
             additional_params, vdu_params = self._format_additional_params(
                 ns_request, vnf_index, vdu_id=vdu["id"], descriptor=vnfd
             )
+
+            try:
+                vdu_virtual_storage_descriptors = utils.filter_in_list(
+                    vnfd.get("virtual-storage-desc", []),
+                    lambda stg_desc: stg_desc["id"] in vdu["virtual-storage-desc"]
+                )
+            except Exception:
+                vdu_virtual_storage_descriptors = []
             vdur = {
                 "vdu-id-ref": vdu["id"],
                 # TODO      "name": ""     Name of the VDU in the VIM
@@ -775,6 +865,7 @@ class NsrTopic(BaseTopic):
                 "interfaces": [],
                 "additionalParams": additional_params,
                 "vdu-name": vdu["name"],
+                "virtual-storages": vdu_virtual_storage_descriptors
             }
             if vdu_params and vdu_params.get("config-units"):
                 vdur["config-units"] = vdu_params["config-units"]
@@ -929,6 +1020,49 @@ class NsrTopic(BaseTopic):
             if nsr_flavor_desc:
                 vdur["ns-flavor-id"] = nsr_flavor_desc["id"]
 
+            # Adding Affinity groups information to vdur
+            try:
+                vdu_profile_affinity_group = utils.find_in_list(
+                    vnfd.get("df")[0]["vdu-profile"],
+                    lambda a_vdu: a_vdu["id"] == vdu["id"],
+                )
+            except Exception:
+                vdu_profile_affinity_group = None
+
+            if vdu_profile_affinity_group:
+                affinity_group_ids = []
+                for affinity_group in vdu_profile_affinity_group.get(
+                    "affinity-or-anti-affinity-group", ()
+                ):
+                    vdu_affinity_group = utils.find_in_list(
+                        vdu_profile_affinity_group.get(
+                            "affinity-or-anti-affinity-group", ()
+                        ),
+                        lambda ag_fp: ag_fp["id"] == affinity_group["id"],
+                    )
+                    nsr_affinity_group = utils.find_in_list(
+                        nsr_descriptor["affinity-or-anti-affinity-group"],
+                        lambda nsr_ag: (
+                            nsr_ag.get("ag-id") == vdu_affinity_group.get("id")
+                            and nsr_ag.get("member-vnf-index")
+                            == vnfr_descriptor.get("member-vnf-index-ref")
+                        ),
+                    )
+                    # Update Affinity Group VIM name if VDU instantiation parameter is present
+                    if vnf_params and vnf_params.get("affinity-or-anti-affinity-group"):
+                        vnf_params_affinity_group = utils.find_in_list(
+                            vnf_params["affinity-or-anti-affinity-group"],
+                            lambda vnfp_ag: (
+                                vnfp_ag.get("id") == vdu_affinity_group.get("id")
+                            ),
+                        )
+                        if vnf_params_affinity_group.get("vim-affinity-group-id"):
+                            nsr_affinity_group[
+                                "vim-affinity-group-id"
+                            ] = vnf_params_affinity_group["vim-affinity-group-id"]
+                    affinity_group_ids.append(nsr_affinity_group["id"])
+                vdur["affinity-or-anti-affinity-group-id"] = affinity_group_ids
+
             if vdu_instantiation_level:
                 count = vdu_instantiation_level.get("number-of-instances")
             else:
@@ -1325,13 +1459,25 @@ class NsLcmOpTopic(BaseTopic):
             )
         vim_accounts.append(vim_account)
 
+    def _get_vim_account(self, vim_id: str, session):
+        try:
+            db_filter = self._get_project_filter(session)
+            db_filter["_id"] = vim_id
+            return self.db.get_one("vim_accounts", db_filter)
+        except Exception:
+            raise EngineException(
+                "Invalid vimAccountId='{}' not present for the project".format(
+                    vim_id
+                )
+            )
+
     def _check_valid_wim_account(self, wim_account, wim_accounts, session):
         if not isinstance(wim_account, str):
             return
         if wim_account in wim_accounts:
             return
         try:
-            db_filter = self._get_project_filter(session, write=False, show_all=True)
+            db_filter = self._get_project_filter(session)
             db_filter["_id"] = wim_account
             self.db.get_one("wim_accounts", db_filter)
         except Exception:
@@ -1587,6 +1733,68 @@ class NsLcmOpTopic(BaseTopic):
             # TODO check that this forcing is not incompatible with other forcing
         return ifaces_forcing_vim_network
 
+    def _update_vnfrs_from_nsd(self, nsr):
+        try:
+            nsr_id = nsr["_id"]
+            nsd = nsr["nsd"]
+
+            step = "Getting vnf_profiles from nsd"
+            vnf_profiles = nsd.get("df", [{}])[0].get("vnf-profile", ())
+            vld_fixed_ip_connection_point_data = {}
+
+            step = "Getting ip-address info from vnf_profile if it exists"
+            for vnfp in vnf_profiles:
+                # Checking ip-address info from nsd.vnf_profile and storing
+                for vlc in vnfp.get("virtual-link-connectivity", ()):
+                    for cpd in vlc.get("constituent-cpd-id", ()):
+                        if cpd.get("ip-address"):
+                            step = "Storing ip-address info"
+                            vld_fixed_ip_connection_point_data.update({vlc.get("virtual-link-profile-id") + '.' + cpd.get("constituent-base-element-id"): {
+                                "vnfd-connection-point-ref": cpd.get(
+                                    "constituent-cpd-id"),
+                                    "ip-address": cpd.get(
+                                    "ip-address")}})
+
+            # Inserting ip address to vnfr
+            if len(vld_fixed_ip_connection_point_data) > 0:
+                step = "Getting vnfrs"
+                vnfrs = self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})
+                for item in vld_fixed_ip_connection_point_data.keys():
+                    step = "Filtering vnfrs"
+                    vnfr = next(filter(lambda vnfr: vnfr["member-vnf-index-ref"] == item.split('.')[1], vnfrs), None)
+                    if vnfr:
+                        vnfr_update = {}
+                        for vdur_index, vdur in enumerate(vnfr["vdur"]):
+                            for iface_index, iface in enumerate(vdur["interfaces"]):
+                                step = "Looking for matched interface"
+                                if (
+                                        iface.get("external-connection-point-ref")
+                                        == vld_fixed_ip_connection_point_data[item].get("vnfd-connection-point-ref") and
+                                        iface.get("ns-vld-id") == item.split('.')[0]
+
+                                ):
+                                    vnfr_update_text = "vdur.{}.interfaces.{}".format(
+                                        vdur_index, iface_index
+                                    )
+                                    step = "Storing info in order to update vnfr"
+                                    vnfr_update[
+                                        vnfr_update_text + ".ip-address"
+                                        ] = increment_ip_mac(
+                                        vld_fixed_ip_connection_point_data[item].get("ip-address"),
+                                        vdur.get("count-index", 0), )
+                                    vnfr_update[vnfr_update_text + ".fixed-ip"] = True
+
+                        step = "updating vnfr at database"
+                        self.db.set_one("vnfrs", {"_id": vnfr["_id"]}, vnfr_update)
+        except (
+                ValidationError,
+                EngineException,
+                DbException,
+                MsgException,
+                FsException,
+        ) as e:
+            raise type(e)("{} while '{}'".format(e, step), http_code=e.http_code)
+
     def _update_vnfrs(self, session, rollback, nsr, indata):
         # get vnfr
         nsr_id = nsr["_id"]
@@ -1599,15 +1807,14 @@ class NsLcmOpTopic(BaseTopic):
             # update vim-account-id
 
             vim_account = indata["vimAccountId"]
-            vca_id = indata.get("vcaId")
+            vca_id = self._get_vim_account(vim_account, session).get("vca")
             # check instantiate parameters
             for vnf_inst_params in get_iterable(indata.get("vnf")):
                 if vnf_inst_params["member-vnf-index"] != member_vnf_index:
                     continue
                 if vnf_inst_params.get("vimAccountId"):
                     vim_account = vnf_inst_params.get("vimAccountId")
-                if vnf_inst_params.get("vcaId"):
-                    vca_id = vnf_inst_params.get("vcaId")
+                    vca_id = self._get_vim_account(vim_account, session).get("vca")
 
                 # get vnf.vdu.interface instantiation params to update vnfr.vdur.interfaces ip, mac
                 for vdu_inst_param in get_iterable(vnf_inst_params.get("vdu")):
@@ -1905,8 +2112,13 @@ class NsLcmOpTopic(BaseTopic):
                         HTTPStatus.CONFLICT,
                     )
             self._check_ns_operation(session, nsr, operation, indata)
+            if (indata.get("primitive_params")):
+                indata["primitive_params"] = json.dumps(indata["primitive_params"])
+            elif (indata.get("additionalParamsForVnf")):
+                indata["additionalParamsForVnf"] = json.dumps(indata["additionalParamsForVnf"])
 
             if operation == "instantiate":
+                self._update_vnfrs_from_nsd(nsr)
                 self._update_vnfrs(session, rollback, nsr, indata)
 
             nslcmop_desc = self._create_nslcmop(nsInstanceId, operation, indata)