Feature 8840 Notify without auth and typo fixes
[osm/NBI.git] / osm_nbi / descriptor_topics.py
index 2b42d06..517f1db 100644 (file)
@@ -21,7 +21,10 @@ from hashlib import md5
 from osm_common.dbbase import DbException, deep_update_rfc7396
 from http import HTTPStatus
 from time import time
-from osm_nbi.validation import ValidationError, pdu_new_schema, pdu_edit_schema
+from uuid import uuid4
+from re import fullmatch
+from osm_nbi.validation import ValidationError, pdu_new_schema, pdu_edit_schema, \
+    validate_input, vnfpkgop_new_schema
 from osm_nbi.base_topic import BaseTopic, EngineException, get_iterable
 from osm_im.vnfd import vnfd as vnfd_im
 from osm_im.nsd import nsd as nsd_im
@@ -143,29 +146,27 @@ class DescriptorTopic(BaseTopic):
         :return: _id, None: identity of the inserted data; and None as there is not any operation
         """
 
-        try:
-            # Check Quota
-            self.check_quota(session)
+        # No needed to capture exceptions
+        # Check Quota
+        self.check_quota(session)
 
-            # _remove_envelop
-            if indata:
-                if "userDefinedData" in indata:
-                    indata = indata['userDefinedData']
+        # _remove_envelop
+        if indata:
+            if "userDefinedData" in indata:
+                indata = indata['userDefinedData']
 
-            # Override descriptor with query string kwargs
-            self._update_input_with_kwargs(indata, kwargs)
-            # uncomment when this method is implemented.
-            # Avoid override in this case as the target is userDefinedData, but not vnfd,nsd descriptors
-            # indata = DescriptorTopic._validate_input_new(self, indata, project_id=session["force"])
-
-            content = {"_admin": {"userDefinedData": indata}}
-            self.format_on_new(content, session["project_id"], make_public=session["public"])
-            _id = self.db.create(self.topic, content)
-            rollback.append({"topic": self.topic, "_id": _id})
-            self._send_msg("created", {"_id": _id})
-            return _id, None
-        except ValidationError as e:
-            raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
+        # Override descriptor with query string kwargs
+        self._update_input_with_kwargs(indata, kwargs)
+        # uncomment when this method is implemented.
+        # Avoid override in this case as the target is userDefinedData, but not vnfd,nsd descriptors
+        # indata = DescriptorTopic._validate_input_new(self, indata, project_id=session["force"])
+
+        content = {"_admin": {"userDefinedData": indata}}
+        self.format_on_new(content, session["project_id"], make_public=session["public"])
+        _id = self.db.create(self.topic, content)
+        rollback.append({"topic": self.topic, "_id": _id})
+        self._send_msg("created", {"_id": _id})
+        return _id, None
 
     def upload_content(self, session, _id, indata, kwargs, headers):
         """
@@ -509,8 +510,17 @@ class VnfdTopic(DescriptorTopic):
                                           http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
 
         for vdu in get_iterable(indata.get("vdu")):
+            icp_refs = []
+            ecp_refs = []
             for interface in get_iterable(vdu.get("interface")):
                 if interface.get("external-connection-point-ref"):
+                    if interface.get("external-connection-point-ref") in ecp_refs:
+                        raise EngineException("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}' "
+                                              "is referenced by other interface"
+                                              .format(vdu["id"], interface["name"],
+                                                      interface["external-connection-point-ref"]),
+                                              http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+                    ecp_refs.append(interface.get("external-connection-point-ref"))
                     for cp in get_iterable(indata.get("connection-point")):
                         if cp["name"] == interface["external-connection-point-ref"]:
                             break
@@ -520,8 +530,14 @@ class VnfdTopic(DescriptorTopic):
                                               .format(vdu["id"], interface["name"],
                                                       interface["external-connection-point-ref"]),
                                               http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-
                 elif interface.get("internal-connection-point-ref"):
+                    if interface.get("internal-connection-point-ref") in icp_refs:
+                        raise EngineException("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' "
+                                              "is referenced by other interface"
+                                              .format(vdu["id"], interface["name"],
+                                                      interface["internal-connection-point-ref"]),
+                                              http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+                    icp_refs.append(interface.get("internal-connection-point-ref"))
                     for internal_cp in get_iterable(vdu.get("internal-connection-point")):
                         if interface["internal-connection-point-ref"] == internal_cp.get("id"):
                             break
@@ -551,13 +567,14 @@ class VnfdTopic(DescriptorTopic):
         vld_names = []  # For detection of duplicated VLD names
         for ivld in get_iterable(indata.get("internal-vld")):
             # BEGIN Detection of duplicated VLD names
-            ivld_name = ivld["name"]
-            if ivld_name in vld_names:
-                raise EngineException("Duplicated VLD name '{}' in vnfd[id={}]:internal-vld[id={}]"
-                                      .format(ivld["name"], indata["id"], ivld["id"]),
-                                      http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
-            else:
-                vld_names.append(ivld_name)
+            ivld_name = ivld.get("name")
+            if ivld_name:
+                if ivld_name in vld_names:
+                    raise EngineException("Duplicated VLD name '{}' in vnfd[id={}]:internal-vld[id={}]"
+                                          .format(ivld["name"], indata["id"], ivld["id"]),
+                                          http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+                else:
+                    vld_names.append(ivld_name)
             # END Detection of duplicated VLD names
             for icp in get_iterable(ivld.get("internal-connection-point")):
                 icp_mark = False
@@ -675,6 +692,19 @@ class VnfdTopic(DescriptorTopic):
                         return True
             return False
 
+    def delete_extra(self, session, _id, db_content, not_send_msg=None):
+        """
+        Deletes associate file system storage (via super)
+        Deletes associated vnfpkgops from database.
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param db_content: The database content of the descriptor
+        :return: None
+        :raises: FsException in case of error while deleting associated storage
+        """
+        super().delete_extra(session, _id, db_content, not_send_msg)
+        self.db.del_list("vnfpkgops", {"vnfPkgId": _id})
+
 
 class NsdTopic(DescriptorTopic):
     topic = "nsds"
@@ -826,6 +856,7 @@ class NsdTopic(DescriptorTopic):
 class NstTopic(DescriptorTopic):
     topic = "nsts"
     topic_msg = "nst"
+    quota_name = "slice_templates"
 
     def __init__(self, db, fs, msg, auth):
         DescriptorTopic.__init__(self, db, fs, msg, auth)
@@ -899,6 +930,7 @@ class NstTopic(DescriptorTopic):
 class PduTopic(BaseTopic):
     topic = "pdus"
     topic_msg = "pdu"
+    quota_name = "pduds"
     schema_new = pdu_new_schema
     schema_edit = pdu_edit_schema
 
@@ -927,3 +959,97 @@ class PduTopic(BaseTopic):
         _filter["vdur.pdu-id"] = _id
         if self.db.get_list("vnfrs", _filter):
             raise EngineException("There is at least one VNF using this PDU", http_code=HTTPStatus.CONFLICT)
+
+
+class VnfPkgOpTopic(BaseTopic):
+    topic = "vnfpkgops"
+    topic_msg = "vnfd"
+    schema_new = vnfpkgop_new_schema
+    schema_edit = None
+
+    def __init__(self, db, fs, msg, auth):
+        BaseTopic.__init__(self, db, fs, msg, auth)
+
+    def edit(self, session, _id, indata=None, kwargs=None, content=None):
+        raise EngineException("Method 'edit' not allowed for topic '{}'".format(self.topic),
+                              HTTPStatus.METHOD_NOT_ALLOWED)
+
+    def delete(self, session, _id, dry_run=False):
+        raise EngineException("Method 'delete' not allowed for topic '{}'".format(self.topic),
+                              HTTPStatus.METHOD_NOT_ALLOWED)
+
+    def delete_list(self, session, filter_q=None):
+        raise EngineException("Method 'delete_list' not allowed for topic '{}'".format(self.topic),
+                              HTTPStatus.METHOD_NOT_ALLOWED)
+
+    def new(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates a new entry into database.
+        :param rollback: list to append created items at database in case a rollback may to be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: data to be inserted
+        :param kwargs: used to override the indata descriptor
+        :param headers: http request headers
+        :return: _id, op_id:
+            _id: identity of the inserted data.
+             op_id: None
+        """
+        self._update_input_with_kwargs(indata, kwargs)
+        validate_input(indata, self.schema_new)
+        vnfpkg_id = indata["vnfPkgId"]
+        filter_q = BaseTopic._get_project_filter(session)
+        filter_q["_id"] = vnfpkg_id
+        vnfd = self.db.get_one("vnfds", filter_q)
+        operation = indata["lcmOperationType"]
+        kdu_name = indata["kdu_name"]
+        for kdu in vnfd.get("kdu", []):
+            if kdu["name"] == kdu_name:
+                helm_chart = kdu.get("helm-chart")
+                juju_bundle = kdu.get("juju-bundle")
+                break
+        else:
+            raise EngineException("Not found vnfd[id='{}']:kdu[name='{}']".format(vnfpkg_id, kdu_name))
+        if helm_chart:
+            indata["helm-chart"] = helm_chart
+            match = fullmatch(r"([^/]*)/([^/]*)", helm_chart)
+            repo_name = match.group(1) if match else None
+        elif juju_bundle:
+            indata["juju-bundle"] = juju_bundle
+            match = fullmatch(r"([^/]*)/([^/]*)", juju_bundle)
+            repo_name = match.group(1) if match else None
+        else:
+            raise EngineException("Found neither 'helm-chart' nor 'juju-bundle' in vnfd[id='{}']:kdu[name='{}']"
+                                  .format(vnfpkg_id, kdu_name))
+        if repo_name:
+            del filter_q["_id"]
+            filter_q["name"] = repo_name
+            repo = self.db.get_one("k8srepos", filter_q)
+            k8srepo_id = repo.get("_id")
+            k8srepo_url = repo.get("url")
+        else:
+            k8srepo_id = None
+            k8srepo_url = None
+        indata["k8srepoId"] = k8srepo_id
+        indata["k8srepo_url"] = k8srepo_url
+        vnfpkgop_id = str(uuid4())
+        vnfpkgop_desc = {
+            "_id": vnfpkgop_id,
+            "operationState": "PROCESSING",
+            "vnfPkgId": vnfpkg_id,
+            "lcmOperationType": operation,
+            "isAutomaticInvocation": False,
+            "isCancelPending": False,
+            "operationParams": indata,
+            "links": {
+                "self": "/osm/vnfpkgm/v1/vnfpkg_op_occs/" + vnfpkgop_id,
+                "vnfpkg": "/osm/vnfpkgm/v1/vnf_packages/" + vnfpkg_id,
+            }
+        }
+        self.format_on_new(vnfpkgop_desc, session["project_id"], make_public=session["public"])
+        ctime = vnfpkgop_desc["_admin"]["created"]
+        vnfpkgop_desc["statusEnteredTime"] = ctime
+        vnfpkgop_desc["startTime"] = ctime
+        self.db.create(self.topic, vnfpkgop_desc)
+        rollback.append({"topic": self.topic, "_id": vnfpkgop_id})
+        self.msg.write(self.topic_msg, operation, vnfpkgop_desc)
+        return vnfpkgop_id, None