X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=blobdiff_plain;f=osm_nbi%2Fdescriptor_topics.py;fp=osm_nbi%2Fdescriptor_topics.py;h=3bdaa03381bec85df94ddfe3d508776209790ff5;hp=24f47a9bf9494cec265bde6fba2e5899df227952;hb=960531ab7417bf6705399dae790899463a659da9;hpb=5347198c2591ffe017f8f0561594441523ee7450 diff --git a/osm_nbi/descriptor_topics.py b/osm_nbi/descriptor_topics.py index 24f47a9..3bdaa03 100644 --- a/osm_nbi/descriptor_topics.py +++ b/osm_nbi/descriptor_topics.py @@ -62,6 +62,7 @@ class DescriptorTopic(BaseTopic): .format(desc_item_id, list_item[desc_item_id], position), HTTPStatus.UNPROCESSABLE_ENTITY) used_ids.append(list_item[desc_item_id]) + _check_unique_id_name(final_content) # 1. validate again with pyangbind # 1.1. remove internal keys @@ -238,9 +239,9 @@ class DescriptorTopic(BaseTopic): break file_pkg.write(indata_text) if content_range_text: - if indata_len != end-start: + if indata_len != end - start: raise EngineException("Mismatch between Content-Range header {}-{} and body length of {}".format( - start, end-1, indata_len), HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE) + start, end - 1, indata_len), HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE) if end != total: # TODO update to UPLOADING return False @@ -361,7 +362,7 @@ class DescriptorTopic(BaseTopic): "onboardingState is {}".format(content["_admin"]["onboardingState"]), http_code=HTTPStatus.CONFLICT) storage = content["_admin"]["storage"] - if path is not None and path != "$DESCRIPTOR": # artifacts + if path is not None and path != "$DESCRIPTOR": # artifacts if not storage.get('pkg-dir'): raise EngineException("Packages does not contains artifacts", http_code=HTTPStatus.BAD_REQUEST) if self.fs.file_exists((storage['folder'], storage['pkg-dir'], *path), 'dir'): @@ -369,7 +370,7 @@ class DescriptorTopic(BaseTopic): return folder_content, "text/plain" # TODO manage folders in http else: - return self.fs.file_open((storage['folder'], storage['pkg-dir'], *path), "rb"),\ + return self.fs.file_open((storage['folder'], storage['pkg-dir'], *path), "rb"), \ "application/octet-stream" # pkgtype accept ZIP TEXT -> result @@ -448,7 +449,7 @@ class DescriptorTopic(BaseTopic): raise EngineException("userDefinedData should be an object, but is '{}' instead" .format(type(data)), http_code=HTTPStatus.BAD_REQUEST) - + if ("operationalState" in indata["_admin"] and content["_admin"]["operationalState"] == indata["_admin"]["operationalState"]): raise EngineException("operationalState already {}".format(content["_admin"]["operationalState"]), @@ -540,86 +541,128 @@ class VnfdTopic(DescriptorTopic): indata = self.pyangbind_validation("vnfds", indata, force) # Cross references validation in the descriptor - if indata.get("vdu"): - if not indata.get("mgmt-interface"): - raise EngineException("'mgmt-interface' is a mandatory field and it is not defined", + self.validate_mgmt_interfaces_connection_points(indata) + + for vdu in get_iterable(indata.get("vdu")): + self.validate_vdu_connection_point_refs(vdu, indata) + self._validate_vdu_charms_in_package(storage_params, vdu, indata) + self._validate_vdu_cloud_init_in_package(storage_params, vdu, indata) + + self._validate_vnf_charms_in_package(storage_params, indata) + + self.validate_internal_vlds(indata) + self.validate_monitoring_params(indata) + self.validate_scaling_group_descriptor(indata) + + return indata + + @staticmethod + def validate_mgmt_interfaces_connection_points(indata): + if not indata.get("vdu"): + return + if not indata.get("mgmt-interface"): + raise EngineException("'mgmt-interface' is a mandatory field and it is not defined", + http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + if indata["mgmt-interface"].get("cp"): + for cp in get_iterable(indata.get("connection-point")): + if cp["name"] == indata["mgmt-interface"]["cp"]: + break + else: + raise EngineException("mgmt-interface:cp='{}' must match an existing connection-point" + .format(indata["mgmt-interface"]["cp"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) - if indata["mgmt-interface"].get("cp"): + + @staticmethod + def validate_vdu_connection_point_refs(vdu, indata): + 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"] == indata["mgmt-interface"]["cp"]: + if cp["name"] == interface["external-connection-point-ref"]: break else: - raise EngineException("mgmt-interface:cp='{}' must match an existing connection-point" - .format(indata["mgmt-interface"]["cp"]), + raise EngineException("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}' " + "must match an existing connection-point" + .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 + else: + raise EngineException("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' " + "must match an existing vdu:internal-connection-point" + .format(vdu["id"], interface["name"], + interface["internal-connection-point-ref"]), 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 - else: - raise EngineException("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{}' " - "must match an existing connection-point" - .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 - else: - raise EngineException("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' " - "must match an existing vdu:internal-connection-point" - .format(vdu["id"], interface["name"], - interface["internal-connection-point-ref"]), - http_code=HTTPStatus.UNPROCESSABLE_ENTITY) - # Validate that if descriptor contains charms, artifacts _admin.storage."pkg-dir" is not none - if vdu.get("vdu-configuration"): - if vdu["vdu-configuration"].get("juju"): - if not self._validate_package_folders(storage_params, 'charms'): - raise EngineException("Charm defined in vnf[id={}]:vdu[id={}] but not present in " - "package".format(indata["id"], vdu["id"])) - # Validate that if descriptor contains cloud-init, artifacts _admin.storage."pkg-dir" is not none - if vdu.get("cloud-init-file"): - if not self._validate_package_folders(storage_params, 'cloud_init', vdu["cloud-init-file"]): - raise EngineException("Cloud-init defined in vnf[id={}]:vdu[id={}] but not present in " - "package".format(indata["id"], vdu["id"])) - # Validate that if descriptor contains charms, artifacts _admin.storage."pkg-dir" is not none - if indata.get("vnf-configuration"): - if indata["vnf-configuration"].get("juju"): - if not self._validate_package_folders(storage_params, 'charms'): - raise EngineException("Charm defined in vnf[id={}] but not present in " - "package".format(indata["id"])) + def _validate_vdu_charms_in_package(self, storage_params, vdu, indata): + if not vdu.get("vdu-configuration"): + return + if vdu["vdu-configuration"].get("juju"): + if not self._validate_package_folders(storage_params, 'charms'): + raise EngineException("Charm defined in vnf[id={}]:vdu[id={}] but not present in " + "package".format(indata["id"], vdu["id"])) + + def _validate_vdu_cloud_init_in_package(self, storage_params, vdu, indata): + if not vdu.get("cloud-init-file"): + return + if not self._validate_package_folders(storage_params, 'cloud_init', vdu["cloud-init-file"]): + raise EngineException("Cloud-init defined in vnf[id={}]:vdu[id={}] but not present in " + "package".format(indata["id"], vdu["id"])) + + def _validate_vnf_charms_in_package(self, storage_params, indata): + if not indata.get("vnf-configuration"): + return + if indata["vnf-configuration"].get("juju"): + if not self._validate_package_folders(storage_params, 'charms'): + raise EngineException("Charm defined in vnf[id={}] but not present in " + "package".format(indata["id"])) + + def _validate_package_folders(self, storage_params, folder, file=None): + if not storage_params or not storage_params.get("pkg-dir"): + return False + else: + if self.fs.file_exists("{}_".format(storage_params["folder"]), 'dir'): + f = "{}_/{}/{}".format(storage_params["folder"], storage_params["pkg-dir"], folder) + else: + f = "{}/{}/{}".format(storage_params["folder"], storage_params["pkg-dir"], folder) + if file: + return self.fs.file_exists("{}/{}".format(f, file), 'file') + else: + if self.fs.file_exists(f, 'dir'): + if self.fs.dir_ls(f): + return True + return False + + @staticmethod + def validate_internal_vlds(indata): 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.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 + if ivld_name and 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) + for icp in get_iterable(ivld.get("internal-connection-point")): icp_mark = False for vdu in get_iterable(indata.get("vdu")): @@ -641,12 +684,15 @@ class VnfdTopic(DescriptorTopic): raise EngineException("internal-vld[id='{}']:ip-profile-ref='{}' does not exist".format( ivld["id"], ivld["ip-profile-ref"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + + @staticmethod + def validate_monitoring_params(indata): for mp in get_iterable(indata.get("monitoring-param")): if mp.get("vdu-monitoring-param"): mp_vmp_mark = False for vdu in get_iterable(indata.get("vdu")): for vmp in get_iterable(vdu.get("monitoring-param")): - if vmp["id"] == mp["vdu-monitoring-param"].get("vdu-monitoring-param-ref") and vdu["id"] ==\ + if vmp["id"] == mp["vdu-monitoring-param"].get("vdu-monitoring-param-ref") and vdu["id"] == \ mp["vdu-monitoring-param"]["vdu-ref"]: mp_vmp_mark = True break @@ -676,6 +722,8 @@ class VnfdTopic(DescriptorTopic): mp["vdu-metric"]["vdu-ref"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + @staticmethod + def validate_scaling_group_descriptor(indata): for sgd in get_iterable(indata.get("scaling-group-descriptor")): for sp in get_iterable(sgd.get("scaling-policy")): for sc in get_iterable(sp.get("scaling-criteria")): @@ -714,23 +762,6 @@ class VnfdTopic(DescriptorTopic): "vnf-configuration:config-primitive:name" .format(sgd["name"], sca["vnf-config-primitive-name-ref"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) - return indata - - def _validate_package_folders(self, storage_params, folder, file=None): - if not storage_params or not storage_params.get("pkg-dir"): - return False - else: - if self.fs.file_exists("{}_".format(storage_params["folder"]), 'dir'): - f = "{}_/{}/{}".format(storage_params["folder"], storage_params["pkg-dir"], folder) - else: - f = "{}/{}/{}".format(storage_params["folder"], storage_params["pkg-dir"], folder) - if file: - return self.fs.file_exists("{}/{}".format(f, file), 'file') - else: - if self.fs.file_exists(f, 'dir'): - if self.fs.dir_ls(f): - return True - return False def delete_extra(self, session, _id, db_content, not_send_msg=None): """ @@ -744,7 +775,7 @@ class VnfdTopic(DescriptorTopic): """ super().delete_extra(session, _id, db_content, not_send_msg) self.db.del_list("vnfpkgops", {"vnfPkgId": _id}) - + def sol005_projection(self, data): data["onboardingState"] = data["_admin"]["onboardingState"] data["operationalState"] = data["_admin"]["operationalState"] @@ -755,7 +786,7 @@ class VnfdTopic(DescriptorTopic): links["vnfd"] = {"href": "/vnfpkgm/v1/vnf_packages/{}/vnfd".format(data["_id"])} links["packageContent"] = {"href": "/vnfpkgm/v1/vnf_packages/{}/package_content".format(data["_id"])} data["_links"] = links - + return super().sol005_projection(data) @@ -797,41 +828,54 @@ class NsdTopic(DescriptorTopic): # Cross references validation in the descriptor # TODO validata that if contains cloud-init-file or charms, have artifacts _admin.storage."pkg-dir" is not none for vld in get_iterable(indata.get("vld")): - if vld.get("mgmt-network") and vld.get("ip-profile-ref"): - raise EngineException("Error at vld[id='{}']:ip-profile-ref" - " You cannot set an ip-profile when mgmt-network is True" - .format(vld["id"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) - for vnfd_cp in get_iterable(vld.get("vnfd-connection-point-ref")): - for constituent_vnfd in get_iterable(indata.get("constituent-vnfd")): - if vnfd_cp["member-vnf-index-ref"] == constituent_vnfd["member-vnf-index"]: - if vnfd_cp.get("vnfd-id-ref") and vnfd_cp["vnfd-id-ref"] != constituent_vnfd["vnfd-id-ref"]: - raise EngineException("Error at vld[id='{}']:vnfd-connection-point-ref[vnfd-id-ref='{}'] " - "does not match constituent-vnfd[member-vnf-index='{}']:vnfd-id-ref" - " '{}'".format(vld["id"], vnfd_cp["vnfd-id-ref"], - constituent_vnfd["member-vnf-index"], - constituent_vnfd["vnfd-id-ref"]), - http_code=HTTPStatus.UNPROCESSABLE_ENTITY) - break - else: - raise EngineException("Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}'] " - "does not match any constituent-vnfd:member-vnf-index" - .format(vld["id"], vnfd_cp["member-vnf-index-ref"]), - http_code=HTTPStatus.UNPROCESSABLE_ENTITY) - # Check VNFFGD + self.validate_vld_mgmt_network_with_ip_profile_ref(vld) + self.validate_vld_connection_point_refs(vld, indata) + for fgd in get_iterable(indata.get("vnffgd")): - for cls in get_iterable(fgd.get("classifier")): - rspref = cls.get("rsp-id-ref") - for rsp in get_iterable(fgd.get("rsp")): - rspid = rsp.get("id") - if rspid and rspref and rspid == rspref: - break - else: - raise EngineException( - "Error at vnffgd[id='{}']:classifier[id='{}']:rsp-id-ref '{}' does not match any rsp:id" - .format(fgd["id"], cls["id"], rspref), - http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + self.validate_fgd_classifiers(fgd) + return indata + @staticmethod + def validate_vld_mgmt_network_with_ip_profile_ref(vld): + if vld.get("mgmt-network") and vld.get("ip-profile-ref"): + raise EngineException("Error at vld[id='{}']:ip-profile-ref" + " You cannot set an ip-profile when mgmt-network is True" + .format(vld["id"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + + @staticmethod + def validate_vld_connection_point_refs(vld, indata): + for vnfd_cp in get_iterable(vld.get("vnfd-connection-point-ref")): + for constituent_vnfd in get_iterable(indata.get("constituent-vnfd")): + if vnfd_cp["member-vnf-index-ref"] == constituent_vnfd["member-vnf-index"]: + if vnfd_cp.get("vnfd-id-ref") and vnfd_cp["vnfd-id-ref"] != constituent_vnfd["vnfd-id-ref"]: + raise EngineException("Error at vld[id='{}']:vnfd-connection-point-ref[vnfd-id-ref='{}'] " + "does not match constituent-vnfd[member-vnf-index='{}']:vnfd-id-ref" + " '{}'".format(vld["id"], vnfd_cp["vnfd-id-ref"], + constituent_vnfd["member-vnf-index"], + constituent_vnfd["vnfd-id-ref"]), + http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + break + else: + raise EngineException("Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}'] " + "does not match any constituent-vnfd:member-vnf-index" + .format(vld["id"], vnfd_cp["member-vnf-index-ref"]), + http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + + @staticmethod + def validate_fgd_classifiers(fgd): + for cls in get_iterable(fgd.get("classifier")): + rspref = cls.get("rsp-id-ref") + for rsp in get_iterable(fgd.get("rsp")): + rspid = rsp.get("id") + if rspid and rspref and rspid == rspref: + break + else: + raise EngineException( + "Error at vnffgd[id='{}']:classifier[id='{}']:rsp-id-ref '{}' does not match any rsp:id" + .format(fgd["id"], cls["id"], rspref), + http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + def _validate_input_edit(self, indata, content, force=False): # not needed to validate with pyangbind becuase it will be validated at check_conflict_on_edit """ @@ -879,36 +923,42 @@ class NsdTopic(DescriptorTopic): """ if session["force"]: return + member_vnfd_index = self._get_descriptor_constituent_vnfds_by_member_vnfd_index(session, descriptor) + + # Cross references validation in the descriptor and vnfd connection point validation + for vld in get_iterable(descriptor.get("vld")): + self.validate_vld_connection_point_refs_vnfd_connection_points(vld, member_vnfd_index) + + def _get_descriptor_constituent_vnfds_by_member_vnfd_index(self, session, descriptor): member_vnfd_index = {} if descriptor.get("constituent-vnfd") and not session["force"]: for vnf in descriptor["constituent-vnfd"]: vnfd_id = vnf["vnfd-id-ref"] - filter_q = self._get_project_filter(session) - filter_q["id"] = vnfd_id - vnf_list = self.db.get_list("vnfds", filter_q) + query_filter = self._get_project_filter(session) + query_filter["id"] = vnfd_id + vnf_list = self.db.get_list("vnfds", query_filter) if not vnf_list: raise EngineException("Descriptor error at 'constituent-vnfd':'vnfd-id-ref'='{}' references a non " "existing vnfd".format(vnfd_id), http_code=HTTPStatus.CONFLICT) - # elif len(vnf_list) > 1: - # raise EngineException("More than one vnfd found for id='{}'".format(vnfd_id), - # http_code=HTTPStatus.CONFLICT) + member_vnfd_index[vnf["member-vnf-index"]] = vnf_list[0] + return member_vnfd_index - # Cross references validation in the descriptor and vnfd connection point validation - for vld in get_iterable(descriptor.get("vld")): - for referenced_vnfd_cp in get_iterable(vld.get("vnfd-connection-point-ref")): - # look if this vnfd contains this connection point - vnfd = member_vnfd_index.get(referenced_vnfd_cp["member-vnf-index-ref"]) - for vnfd_cp in get_iterable(vnfd.get("connection-point")): - if referenced_vnfd_cp.get("vnfd-connection-point-ref") == vnfd_cp["name"]: - break - else: - raise EngineException( - "Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}']:vnfd-" - "connection-point-ref='{}' references a non existing conection-point:name inside vnfd '{}'" - .format(vld["id"], referenced_vnfd_cp["member-vnf-index-ref"], - referenced_vnfd_cp["vnfd-connection-point-ref"], vnfd["id"]), - http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + @staticmethod + def validate_vld_connection_point_refs_vnfd_connection_points(vld, member_vnfd_index): + for referenced_vnfd_cp in get_iterable(vld.get("vnfd-connection-point-ref")): + # look if this vnfd contains this connection point + vnfd = member_vnfd_index.get(referenced_vnfd_cp["member-vnf-index-ref"]) + for vnfd_cp in get_iterable(vnfd.get("connection-point")): + if referenced_vnfd_cp.get("vnfd-connection-point-ref") == vnfd_cp["name"]: + break + else: + raise EngineException( + "Error at vld[id='{}']:vnfd-connection-point-ref[member-vnf-index-ref='{}']:vnfd-" + "connection-point-ref='{}' references a non existing conection-point:name inside vnfd '{}'" + .format(vld["id"], referenced_vnfd_cp["member-vnf-index-ref"], + referenced_vnfd_cp["vnfd-connection-point-ref"], vnfd["id"]), + http_code=HTTPStatus.UNPROCESSABLE_ENTITY) def check_conflict_on_edit(self, session, final_content, edit_content, _id): super().check_conflict_on_edit(session, final_content, edit_content, _id) @@ -943,7 +993,7 @@ class NsdTopic(DescriptorTopic): if self.db.get_list("nsts", _filter): raise EngineException("There is at least one NetSlice Template referencing this descriptor", http_code=HTTPStatus.CONFLICT) - + def sol005_projection(self, data): data["nsdOnboardingState"] = data["_admin"]["onboardingState"] data["nsdOperationalState"] = data["_admin"]["operationalState"] @@ -953,7 +1003,7 @@ class NsdTopic(DescriptorTopic): links["self"] = {"href": "/nsd/v1/ns_descriptors/{}".format(data["_id"])} links["nsd_content"] = {"href": "/nsd/v1/ns_descriptors/{}/nsd_content".format(data["_id"])} data["_links"] = links - + return super().sol005_projection(data)