Feature 11009 Ns Config Template as first class citizens in OSM - NBI 44/14444/4
authorkayal2001 <kayalvizhi.v@tataelxsi.co.in>
Tue, 25 Jun 2024 09:56:24 +0000 (15:26 +0530)
committergarciadeblas <gerardo.garciadeblas@telefonica.com>
Tue, 16 Jul 2024 10:25:03 +0000 (12:25 +0200)
Change-Id: I20edf909b461edc73bebdf9bcad0d2a321b0818f
Signed-off-by: kayal2001 <kayalvizhi.v@tataelxsi.co.in>
osm_nbi/base_topic.py
osm_nbi/descriptor_topics.py
osm_nbi/engine.py
osm_nbi/instance_topics.py
osm_nbi/nbi.py
osm_nbi/validation.py

index 820bd8e..5b5818c 100644 (file)
@@ -83,6 +83,7 @@ def detect_descriptor_usage(descriptor: dict, db_collection: str, db: object) ->
         search_dict = {
             "vnfds": ("vnfrs", "vnfd-id"),
             "nsds": ("nsrs", "nsd-id"),
+            "ns_config_template": ("ns_config_template", "_id"),
         }
 
         if db_collection not in search_dict:
@@ -653,7 +654,6 @@ class BaseTopic:
         :param not_send_msg: To not send message (False) or store content (list) instead
         :return: operation id (None if there is not operation), raise exception if error or not found, conflict, ...
         """
-
         # To allow addressing projects and users by name AS WELL AS by _id
         if not self.multiproject:
             filter_q = {}
@@ -661,8 +661,26 @@ class BaseTopic:
             filter_q = self._get_project_filter(session)
         filter_q[self.id_field(self.topic, _id)] = _id
         item_content = self.db.get_one(self.topic, filter_q)
+        nsd_id = item_content.get("_id")
 
         self.check_conflict_on_del(session, _id, item_content)
+
+        # While deteling ns descriptor associated ns config template should also get deleted.
+        if self.topic == "nsds":
+            ns_config_template_content = self.db.get_list(
+                "ns_config_template", {"nsdId": _id}
+            )
+            for template_content in ns_config_template_content:
+                if template_content is not None:
+                    if template_content.get("nsdId") == nsd_id:
+                        ns_config_template_id = template_content.get("_id")
+                        self.db.del_one("ns_config_template", {"nsdId": nsd_id})
+                        self.delete_extra(
+                            session,
+                            ns_config_template_id,
+                            template_content,
+                            not_send_msg=not_send_msg,
+                        )
         if dry_run:
             return None
 
index 70b5d23..28c923e 100644 (file)
@@ -38,6 +38,10 @@ from osm_nbi.validation import (
     pdu_edit_schema,
     validate_input,
     vnfpkgop_new_schema,
+    ns_config_template,
+    vnf_schema,
+    vld_schema,
+    additional_params_for_vnf,
 )
 from osm_nbi.base_topic import (
     BaseTopic,
@@ -2217,3 +2221,107 @@ class VnfPkgOpTopic(BaseTopic):
         rollback.append({"topic": self.topic, "_id": vnfpkgop_id})
         self.msg.write(self.topic_msg, operation, vnfpkgop_desc)
         return vnfpkgop_id, None
+
+
+class NsConfigTemplateTopic(DescriptorTopic):
+    topic = "ns_config_template"
+    topic_msg = "nsd"
+    schema_new = ns_config_template
+    instantiation_params = {
+        "vnf": vnf_schema,
+        "vld": vld_schema,
+        "additionalParamsForVnf": additional_params_for_vnf,
+    }
+
+    def __init__(self, db, fs, msg, auth):
+        super().__init__(db, fs, msg, auth)
+
+    def check_conflict_on_del(self, session, _id, db_content):
+        """
+        Check that there is not any NSR that uses this NS CONFIG TEMPLATE. Only NSRs belonging to this project are considered.
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: ns config template internal id
+        :param db_content: The database content of the _id
+        :return: None or raises EngineException with the conflict
+        """
+        if session["force"]:
+            return
+        descriptor = db_content
+        descriptor_id = descriptor.get("nsdId")
+        if not descriptor_id:  # empty nsd not uploaded
+            return
+
+        # check NS CONFIG TEMPLATE used by NS
+        ns_config_template_id = _id
+        self.logger.info("The id is : {}".format(_id))
+        if self.db.get_list(
+            "nsrs", {"instantiate_params.nsConfigTemplateId": ns_config_template_id}
+        ):
+            raise EngineException(
+                "There is at least one NS instance using this template",
+                http_code=HTTPStatus.CONFLICT,
+            )
+
+    def check_unique_template_name(self, edit_content, _id, session):
+        """
+        Check whether the name of the template is unique or not
+        """
+
+        if edit_content.get("name"):
+            name = edit_content.get("name")
+            db_content = self.db.get_one(
+                "ns_config_template", {"name": name}, fail_on_empty=False
+            )
+            if db_content is not None:
+                if db_content.get("_id") == _id:
+                    if db_content.get("name") == name:
+                        return
+                elif db_content.get("_id") != _id:
+                    raise EngineException(
+                        "{} of the template already exist".format(name)
+                    )
+            else:
+                return
+
+    def check_conflict_on_edit(self, session, final_content, edit_content, _id):
+        """
+        Check the input data format
+        And the edit content data too.
+        """
+        final_content = super().check_conflict_on_edit(
+            session, final_content, edit_content, _id
+        )
+        db_content_id = self.db.get_one(
+            "ns_config_template", {"_id": _id}, fail_on_empty=False
+        )
+        if not (
+            db_content_id.get("name")
+            and db_content_id.get("nsdId")
+            and db_content_id.get("config")
+        ):
+            validate_input(edit_content, self.schema_new)
+
+        try:
+            for key, value in edit_content.items():
+                if key == "name":
+                    self.check_unique_template_name(edit_content, _id, session)
+                elif key == "nsdId":
+                    ns_config_template = self.db.get_one(
+                        "ns_config_template", {"_id": _id}, fail_on_empty=False
+                    )
+                    if not ns_config_template.get("nsdId"):
+                        pass
+                    else:
+                        raise EngineException("Nsd id cannot be edited")
+                elif key == "config":
+                    edit_content_param = edit_content.get("config")
+                    for key, value in edit_content_param.items():
+                        param = key
+                        param_content = value
+                        validate_input(param_content, self.instantiation_params[param])
+            return final_content
+        except Exception as e:
+            raise EngineException(
+                "Error in instantiation parameters validation: {}".format(str(e)),
+                http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+            )
index c4c8eb2..fe7d8db 100644 (file)
@@ -44,6 +44,7 @@ from osm_nbi.descriptor_topics import (
     PduTopic,
     NstTopic,
     VnfPkgOpTopic,
+    NsConfigTemplateTopic,
 )
 from osm_nbi.instance_topics import (
     NsrTopic,
@@ -90,6 +91,7 @@ class Engine(object):
         "vnf_instances": VnfInstances,
         "vnflcmops": VnfLcmOpTopic,
         "vnflcm_subscriptions": VnflcmSubscriptionsTopic,
+        "nsconfigtemps": NsConfigTemplateTopic,
         # [NEW_TOPIC]: add an entry here
         # "pm_jobs": PmJobsTopic will be added manually because it needs other parameters
     }
index 9d2da99..df4a1d1 100644 (file)
@@ -119,6 +119,24 @@ class NsrTopic(BaseTopic):
                     "nsds", {"_id": used_nsd_id}, {"_admin.usageState": "NOT_IN_USE"}
                 )
 
+        # Set NS CONFIG TEMPLATE usageState
+        if nsr.get("instantiate_params", {}).get("nsConfigTemplateId"):
+            nsconfigtemplate_id = nsr.get("instantiate_params", {}).get(
+                "nsConfigTemplateId"
+            )
+            nsconfigtemplate_list = self.db.get_one(
+                "nsrs",
+                {"instantiate_params.nsConfigTemplateId": nsconfigtemplate_id},
+                fail_on_empty=False,
+                fail_on_more=False,
+            )
+            if not nsconfigtemplate_list:
+                self.db.set_one(
+                    "ns_config_template",
+                    {"_id": nsconfigtemplate_id},
+                    {"_admin.usageState": "NOT_IN_USE"},
+                )
+
         # Set VNFD usageState
         used_vnfd_id_list = nsr.get("vnfd-id")
         if used_vnfd_id_list:
@@ -324,6 +342,38 @@ class NsrTopic(BaseTopic):
             nsd = self._get_nsd_from_db(ns_request["nsdId"], session)
             ns_k8s_namespace = self._get_ns_k8s_namespace(nsd, ns_request, session)
 
+            # Uploading the instantiation parameters to ns_request from ns config template
+            if ns_request.get("nsConfigTemplateId"):
+                step = "getting ns_config_template is='{}' from database".format(
+                    ns_request.get("nsConfigTemplateId")
+                )
+                ns_config_template_db = self._get_nsConfigTemplate_from_db(
+                    ns_request.get("nsConfigTemplateId"), session
+                )
+                ns_config_params = ns_config_template_db.get("config")
+                for key, value in ns_config_params.items():
+                    if key == "vnf":
+                        ns_request["vnf"] = ns_config_params.get("vnf")
+                    elif key == "additionalParamsForVnf":
+                        ns_request["additionalParamsForVnf"] = ns_config_params.get(
+                            "additionalParamsForVnf"
+                        )
+                    elif key == "additionalParamsForNs":
+                        ns_request["additionalParamsForNs"] = ns_config_params.get(
+                            "additionalParamsForNs"
+                        )
+                    elif key == "vld":
+                        ns_request["vld"] = ns_config_params.get("vld")
+                step = "checking ns_config_templateOperationalState"
+                self._check_ns_config_template_operational_state(
+                    ns_config_template_db, ns_request
+                )
+
+                step = "Updating NSCONFIG TEMPLATE usageState"
+                update_descriptor_usage_state(
+                    ns_config_template_db, "ns_config_template", self.db
+                )
+
             step = "checking nsdOperationalState"
             self._check_nsd_operational_state(nsd, ns_request)
 
@@ -399,6 +449,14 @@ class NsrTopic(BaseTopic):
         _filter["_id"] = nsd_id
         return self.db.get_one("nsds", _filter)
 
+    def _get_nsConfigTemplate_from_db(self, nsConfigTemplate_id, session):
+        _filter = self._get_project_filter(session)
+        _filter["_id"] = nsConfigTemplate_id
+        ns_config_template_db = self.db.get_one(
+            "ns_config_template", _filter, fail_on_empty=False
+        )
+        return ns_config_template_db
+
     def _get_vnfd_from_db(self, vnfd_id, session):
         _filter = self._get_project_filter(session)
         _filter["id"] = vnfd_id
@@ -427,6 +485,16 @@ class NsrTopic(BaseTopic):
                 http_code=HTTPStatus.CONFLICT,
             )
 
+    def _check_ns_config_template_operational_state(
+        self, ns_config_template_db, ns_request
+    ):
+        if ns_config_template_db["_admin"]["operationalState"] == "DISABLED":
+            raise EngineException(
+                "ns_config_template with id '{}' is DISABLED, and thus cannot be used to create "
+                "a network service".format(ns_request["nsConfigTemplateId"]),
+                http_code=HTTPStatus.CONFLICT,
+            )
+
     def _get_ns_k8s_namespace(self, nsd, ns_request, session):
         additional_params, _ = self._format_additional_params(
             ns_request, descriptor=nsd
index e1d3c11..fe0e053 100644 (file)
@@ -60,6 +60,9 @@ URL: /osm                                                       GET     POST
                     /nsd_content                                O5              O5
                     /nsd                                        O
                     /artifacts[/<artifactPath>]                 O
+            /ns_config_template                                 O       O
+                /<nsConfigTemplateId>                           O                       O
+                    /template_content                           O               O
             /pnf_descriptors                                    5       5
                 /<pnfdInfoId>                                   5                       5       5
                     /pnfd_content                               5               5
@@ -355,6 +358,18 @@ valid_url_methods = {
                     },
                 },
             },
+            "ns_config_template": {
+                "METHODS": ("GET", "POST"),
+                "ROLE_PERMISSION": "ns_config_template:content:",
+                "<ID>": {
+                    "METHODS": ("GET", "DELETE"),
+                    "ROLE_PERMISSION": "ns_config_template:id:",
+                    "template_content": {
+                        "METHODS": ("GET", "PUT"),
+                        "ROLE_PERMISSION": "ns_config_template:id:content:",
+                    },
+                },
+            },
             "pnf_descriptors": {
                 "TODO": ("GET", "POST"),
                 "<ID>": {
@@ -1489,6 +1504,8 @@ class Server(object):
 
             if main_topic == "nsd":
                 engine_topic = "nsds"
+                if topic == "ns_config_template":
+                    engine_topic = "nsconfigtemps"
             elif main_topic == "vnfpkgm":
                 engine_topic = "vnfds"
                 if topic == "vnfpkg_op_occs":
@@ -1529,6 +1546,7 @@ class Server(object):
                     "nsd",
                     "nst",
                     "nst_content",
+                    "ns_config_template",
                 ):
                     if item in ("vnfd", "nsd", "nst"):
                         path = "$DESCRIPTOR"
@@ -1567,6 +1585,7 @@ class Server(object):
                     "ns_descriptors_content",
                     "vnf_packages_content",
                     "netslice_templates_content",
+                    "ns_config_template",
                 ):
                     _id = cherrypy.request.headers.get("Transaction-Id")
                     if not _id:
@@ -1750,7 +1769,13 @@ class Server(object):
                         HTTPStatus.BAD_REQUEST,
                     )
                 if (
-                    item in ("nsd_content", "package_content", "nst_content")
+                    item
+                    in (
+                        "nsd_content",
+                        "package_content",
+                        "nst_content",
+                        "template_content",
+                    )
                     and method == "PUT"
                 ):
                     completed = self.engine.upload_content(
index 18d5ff6..d282bde 100644 (file)
@@ -329,6 +329,82 @@ additional_params_for_vnf = {
     },
 }
 
+vnf_schema = {
+    "type": "array",
+    "minItems": 1,
+    "items": {
+        "type": "object",
+        "properties": {
+            "member-vnf-index": name_schema,
+            "vimAccountId": id_schema,
+            "vdu": {
+                "type": "array",
+                "minItems": 1,
+                "items": ns_instantiate_vdu,
+            },
+            "internal-vld": {
+                "type": "array",
+                "minItems": 1,
+                "items": ns_instantiate_internal_vld,
+            },
+        },
+        "required": ["member-vnf-index"],
+        "minProperties": 2,
+        "additionalProperties": False,
+    },
+}
+
+vld_schema = {
+    "type": "array",
+    "minItems": 1,
+    "items": {
+        "type": "object",
+        "properties": {
+            "name": string_schema,
+            "vim-network-name": {"oneOf": [string_schema, object_schema]},
+            "vim-network-id": {"oneOf": [string_schema, object_schema]},
+            "ns-net": object_schema,
+            "wimAccountId": {"oneOf": [id_schema, bool_schema, null_schema]},
+            "ip-profile": ip_profile_schema,
+            "provider-network": provider_network_schema,
+            "vnfd-connection-point-ref": {
+                "type": "array",
+                "minItems": 1,
+                "items": {
+                    "type": "object",
+                    "properties": {
+                        "member-vnf-index-ref": name_schema,
+                        "vnfd-connection-point-ref": name_schema,
+                        "ip-address": {"oneOf": [ip_schema, ipv6_schema]},
+                        # "mac-address": mac_schema,
+                    },
+                    "required": [
+                        "member-vnf-index-ref",
+                        "vnfd-connection-point-ref",
+                    ],
+                    "minProperties": 3,
+                    "additionalProperties": False,
+                },
+            },
+        },
+        "required": ["name"],
+        "additionalProperties": False,
+    },
+}
+
+ns_config_template = {
+    "title": " ns config template input schema",
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "type": "object",
+    "properties": {
+        "name": string_schema,
+        "nsdId": id_schema,
+        "config": object_schema,
+    },
+    "required": ["name", "nsdId", "config"],
+    "additionalProperties": False,
+}
+
 ns_instantiate = {
     "title": "ns action instantiate input schema",
     "$schema": "http://json-schema.org/draft-04/schema#",
@@ -341,6 +417,7 @@ ns_instantiate = {
         "nsDescription": {"oneOf": [description_schema, null_schema]},
         "nsdId": id_schema,
         "vimAccountId": id_schema,
+        "nsConfigTemplateId": id_schema,
         "wimAccountId": {"oneOf": [id_schema, bool_schema, null_schema]},
         "placement-engine": string_schema,
         "placement-constraints": object_schema,
@@ -352,67 +429,8 @@ ns_instantiate = {
         "timeout_ns_deploy": integer1_schema,
         "nsr_id": id_schema,
         "vduImage": name_schema,
-        "vnf": {
-            "type": "array",
-            "minItems": 1,
-            "items": {
-                "type": "object",
-                "properties": {
-                    "member-vnf-index": name_schema,
-                    "vimAccountId": id_schema,
-                    "vdu": {
-                        "type": "array",
-                        "minItems": 1,
-                        "items": ns_instantiate_vdu,
-                    },
-                    "internal-vld": {
-                        "type": "array",
-                        "minItems": 1,
-                        "items": ns_instantiate_internal_vld,
-                    },
-                },
-                "required": ["member-vnf-index"],
-                "minProperties": 2,
-                "additionalProperties": False,
-            },
-        },
-        "vld": {
-            "type": "array",
-            "minItems": 1,
-            "items": {
-                "type": "object",
-                "properties": {
-                    "name": string_schema,
-                    "vim-network-name": {"oneOf": [string_schema, object_schema]},
-                    "vim-network-id": {"oneOf": [string_schema, object_schema]},
-                    "ns-net": object_schema,
-                    "wimAccountId": {"oneOf": [id_schema, bool_schema, null_schema]},
-                    "ip-profile": ip_profile_schema,
-                    "provider-network": provider_network_schema,
-                    "vnfd-connection-point-ref": {
-                        "type": "array",
-                        "minItems": 1,
-                        "items": {
-                            "type": "object",
-                            "properties": {
-                                "member-vnf-index-ref": name_schema,
-                                "vnfd-connection-point-ref": name_schema,
-                                "ip-address": {"oneOf": [ip_schema, ipv6_schema]},
-                                # "mac-address": mac_schema,
-                            },
-                            "required": [
-                                "member-vnf-index-ref",
-                                "vnfd-connection-point-ref",
-                            ],
-                            "minProperties": 3,
-                            "additionalProperties": False,
-                        },
-                    },
-                },
-                "required": ["name"],
-                "additionalProperties": False,
-            },
-        },
+        "vnf": vnf_schema,
+        "vld": vld_schema,
     },
     "required": ["nsName", "nsdId", "vimAccountId"],
     "additionalProperties": False,
@@ -1223,6 +1241,9 @@ nbi_edit_input_schemas = {
     "vim_accounts": vim_account_edit_schema,
     "sdns": sdn_edit_schema,
     "pdus": pdu_edit_schema,
+    "vnf": vnf_schema,
+    "vld": vld_schema,
+    "additionalParamsForVnf": additional_params_for_vnf,
 }
 
 # NETSLICE SCHEMAS