From: kayal2001 Date: Tue, 25 Jun 2024 09:56:24 +0000 (+0530) Subject: Feature 11009 Ns Config Template as first class citizens in OSM - NBI X-Git-Tag: release-v16.0-start~13 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=f71c2e8adefd0ac3d238280ada16b287792f6b14;p=osm%2FNBI.git Feature 11009 Ns Config Template as first class citizens in OSM - NBI Change-Id: I20edf909b461edc73bebdf9bcad0d2a321b0818f Signed-off-by: kayal2001 --- diff --git a/osm_nbi/base_topic.py b/osm_nbi/base_topic.py index 820bd8e..5b5818c 100644 --- a/osm_nbi/base_topic.py +++ b/osm_nbi/base_topic.py @@ -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 diff --git a/osm_nbi/descriptor_topics.py b/osm_nbi/descriptor_topics.py index 70b5d23..28c923e 100644 --- a/osm_nbi/descriptor_topics.py +++ b/osm_nbi/descriptor_topics.py @@ -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, + ) diff --git a/osm_nbi/engine.py b/osm_nbi/engine.py index c4c8eb2..fe7d8db 100644 --- a/osm_nbi/engine.py +++ b/osm_nbi/engine.py @@ -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 } diff --git a/osm_nbi/instance_topics.py b/osm_nbi/instance_topics.py index 9d2da99..df4a1d1 100644 --- a/osm_nbi/instance_topics.py +++ b/osm_nbi/instance_topics.py @@ -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 diff --git a/osm_nbi/nbi.py b/osm_nbi/nbi.py index e1d3c11..fe0e053 100644 --- a/osm_nbi/nbi.py +++ b/osm_nbi/nbi.py @@ -60,6 +60,9 @@ URL: /osm GET POST /nsd_content O5 O5 /nsd O /artifacts[/] O + /ns_config_template O O + / O O + /template_content O O /pnf_descriptors 5 5 / 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:", + "": { + "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"), "": { @@ -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( diff --git a/osm_nbi/validation.py b/osm_nbi/validation.py index 18d5ff6..d282bde 100644 --- a/osm_nbi/validation.py +++ b/osm_nbi/validation.py @@ -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