Bug 1830 fixed: maps completed operations to original operation types
[osm/NBI.git] / osm_nbi / instance_topics.py
index f9539dd..695a8f8 100644 (file)
@@ -31,6 +31,7 @@ from osm_nbi.validation import (
     nsi_instantiate,
     ns_migrate,
     ns_verticalscale,
+    nslcmop_cancel,
 )
 from osm_nbi.base_topic import (
     BaseTopic,
@@ -440,6 +441,23 @@ class NsrTopic(BaseTopic):
 
         return ns_k8s_namespace
 
+    def _add_shared_volumes_to_nsr(
+        self, vdu, vnfd, nsr_descriptor, member_vnf_index, revision=None
+    ):
+        svsd = []
+        for vsd in vnfd.get("virtual-storage-desc", ()):
+            if vsd.get("vdu-storage-requirements"):
+                if (
+                    vsd.get("vdu-storage-requirements")[0].get("key") == "multiattach"
+                    and vsd.get("vdu-storage-requirements")[0].get("value") == "True"
+                ):
+                    # Avoid setting the volume name multiple times
+                    if not match(f"shared-.*-{vnfd['id']}", vsd["id"]):
+                        vsd["id"] = f"shared-{vsd['id']}-{vnfd['id']}"
+                    svsd.append(vsd)
+        if svsd:
+            nsr_descriptor["shared-volumes"] = svsd
+
     def _add_flavor_to_nsr(
         self, vdu, vnfd, nsr_descriptor, member_vnf_index, revision=None
     ):
@@ -543,6 +561,8 @@ class NsrTopic(BaseTopic):
             "flavor": [],
             "image": [],
             "affinity-or-anti-affinity-group": [],
+            "shared-volumes": [],
+            "vnffgd": [],
         }
         if "revision" in nsd["_admin"]:
             nsr_descriptor["revision"] = nsd["_admin"]["revision"]
@@ -580,6 +600,9 @@ class NsrTopic(BaseTopic):
                 for vdu in vnfd.get("vdu", ()):
                     member_vnf_index = vnf_profile.get("id")
                     self._add_flavor_to_nsr(vdu, vnfd, nsr_descriptor, member_vnf_index)
+                    self._add_shared_volumes_to_nsr(
+                        vdu, vnfd, nsr_descriptor, member_vnf_index
+                    )
                     sw_image_id = vdu.get("sw-image-desc")
                     if sw_image_id:
                         image_data = self._get_image_data_from_vnfd(vnfd, sw_image_id)
@@ -619,6 +642,16 @@ class NsrTopic(BaseTopic):
                 )
                 vld["name"] = vld["id"]
             nsr_descriptor["vld"] = nsr_vld
+        if nsd.get("vnffgd"):
+            vnffgd = nsd.get("vnffgd")
+            for vnffg in vnffgd:
+                info = {}
+                for k, v in vnffg.items():
+                    if k == "id":
+                        info.update({k: v})
+                    if k == "nfpd":
+                        info.update({k: v})
+                nsr_descriptor["vnffgd"].append(info)
 
         return nsr_descriptor
 
@@ -885,7 +918,7 @@ class NsrTopic(BaseTopic):
                     # Name, mac-address and interface position is taken from VNFD
                     # and included into VNFR. By this way RO can process this information
                     # while creating the VDU.
-                    iface_fields = ("name", "mac-address", "position")
+                    iface_fields = ("name", "mac-address", "position", "ip-address")
                     vdu_iface = {
                         x: iface[x] for x in iface_fields if iface.get(x) is not None
                     }
@@ -955,7 +988,7 @@ class NsrTopic(BaseTopic):
                                         if (
                                             cpd.get("constituent-cpd-id")
                                             == iface_ext_cp
-                                        ):
+                                        ) and vnf_profile.get("id") == vnf_index:
                                             vdu_iface["ns-vld-id"] = vlc.get(
                                                 "virtual-link-profile-id"
                                             )
@@ -1021,6 +1054,21 @@ class NsrTopic(BaseTopic):
             if nsr_flavor_desc:
                 vdur["ns-flavor-id"] = nsr_flavor_desc["id"]
 
+            # Adding Shared Volume information to vdur
+            if vdur.get("virtual-storages"):
+                nsr_sv = []
+                for vsd in vdur["virtual-storages"]:
+                    if vsd.get("vdu-storage-requirements"):
+                        if (
+                            vsd["vdu-storage-requirements"][0].get("key")
+                            == "multiattach"
+                            and vsd["vdu-storage-requirements"][0].get("value")
+                            == "True"
+                        ):
+                            nsr_sv.append(vsd["id"])
+                if nsr_sv:
+                    vdur["shared-volumes-id"] = nsr_sv
+
             # Adding Affinity groups information to vdur
             try:
                 vdu_profile_affinity_group = utils.find_in_list(
@@ -1081,7 +1129,6 @@ class NsrTopic(BaseTopic):
                 vdur["id"] = vdur["_id"]
                 vdur["count-index"] = index
                 vnfr_descriptor["vdur"].append(vdur)
-
         return vnfr_descriptor
 
     def vca_status_refresh(self, session, ns_instance_content, filter_q):
@@ -1168,6 +1215,7 @@ class NsLcmOpTopic(BaseTopic):
         "terminate": ns_terminate,
         "migrate": ns_migrate,
         "verticalscale": ns_verticalscale,
+        "cancel": nslcmop_cancel,
     }
 
     def __init__(self, db, fs, msg, auth):
@@ -1535,8 +1583,8 @@ class NsLcmOpTopic(BaseTopic):
             ivld.get("id"): set()
             for ivld in get_iterable(vnfd.get("int-virtual-link-desc"))
         }
-        for vdu in get_iterable(vnfd.get("vdu")):
-            for cpd in get_iterable(vnfd.get("int-cpd")):
+        for vdu in vnfd.get("vdu", {}):
+            for cpd in vdu.get("int-cpd", {}):
                 if cpd.get("int-virtual-link-desc"):
                     vnfd_ivlds_cpds[cpd.get("int-virtual-link-desc")] = cpd.get("id")
 
@@ -2279,6 +2327,9 @@ class NsLcmOpTopic(BaseTopic):
                         vnf_index = vnfr["member-vnf-index-ref"]
                         self.logger.info("nsr {}".format(nsr))
                         for vdu in vnfd["vdu"]:
+                            self.nsrtopic._add_shared_volumes_to_nsr(
+                                vdu, vnfd, nsr, vnf_index, latest_vnfd_revision
+                            )
                             self.nsrtopic._add_flavor_to_nsr(
                                 vdu, vnfd, nsr, vnf_index, latest_vnfd_revision
                             )
@@ -2295,6 +2346,7 @@ class NsLcmOpTopic(BaseTopic):
                                 self.nsrtopic._add_image_to_nsr(nsr, image_data)
                         nsr_update["image"] = nsr["image"]
                         nsr_update["flavor"] = nsr["flavor"]
+                        nsr_update["shared-volumes"] = nsr["shared-volumes"]
                         self.db.set_one("nsrs", {"_id": nsr["_id"]}, nsr_update)
                         ns_k8s_namespace = self.nsrtopic._get_ns_k8s_namespace(
                             nsd, ns_request, session
@@ -2332,6 +2384,41 @@ class NsLcmOpTopic(BaseTopic):
         # except DbException as e:
         #     raise EngineException("Cannot get ns_instance '{}': {}".format(e), HTTPStatus.NOT_FOUND)
 
+    def cancel(self, rollback, session, indata=None, kwargs=None, headers=None):
+        validate_input(indata, self.operation_schema["cancel"])
+        # Override descriptor with query string kwargs
+        self._update_input_with_kwargs(indata, kwargs, yaml_format=True)
+        nsLcmOpOccId = indata["nsLcmOpOccId"]
+        cancelMode = indata["cancelMode"]
+        # get nslcmop from nsLcmOpOccId
+        _filter = BaseTopic._get_project_filter(session)
+        _filter["_id"] = nsLcmOpOccId
+        nslcmop = self.db.get_one("nslcmops", _filter)
+        # Fail is this is not an ongoing nslcmop
+        if nslcmop.get("operationState") not in [
+            "STARTING",
+            "PROCESSING",
+            "ROLLING_BACK",
+        ]:
+            raise EngineException(
+                "Operation is not in STARTING, PROCESSING or ROLLING_BACK state",
+                http_code=HTTPStatus.CONFLICT,
+            )
+        nsInstanceId = nslcmop["nsInstanceId"]
+        update_dict = {
+            "isCancelPending": True,
+            "cancelMode": cancelMode,
+        }
+        self.db.set_one(
+            "nslcmops", q_filter=_filter, update_dict=update_dict, fail_on_empty=False
+        )
+        data = {
+            "_id": nsLcmOpOccId,
+            "nsInstanceId": nsInstanceId,
+            "cancelMode": cancelMode,
+        }
+        self.msg.write("nslcmops", "cancel", data)
+
     def delete(self, session, _id, dry_run=False, not_send_msg=None):
         raise EngineException(
             "Method delete called directly", HTTPStatus.INTERNAL_SERVER_ERROR