X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=blobdiff_plain;f=osm_nbi%2Fdescriptor_topics.py;h=fd3e3f492472d527f3aa7c0d2dba9a94944219da;hp=be34d5aa5b5cd4d74329ec53996a9a7f216c60c3;hb=e45aded97f406476e5f03d21f2624a092796f7f9;hpb=40fbcad471ba3a8ce45160bf1cdc950a2aaffa06 diff --git a/osm_nbi/descriptor_topics.py b/osm_nbi/descriptor_topics.py index be34d5a..fd3e3f4 100644 --- a/osm_nbi/descriptor_topics.py +++ b/osm_nbi/descriptor_topics.py @@ -9,6 +9,10 @@ from osm_common.dbbase import DbException, deep_update_rfc7396 from http import HTTPStatus from validation import ValidationError, pdu_new_schema, pdu_edit_schema from 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 +from pyangbind.lib.serialise import pybindJSONDecoder +import pyangbind.lib.pybindJSON as pybindJSON __author__ = "Alfonso Tierno " @@ -19,17 +23,29 @@ class DescriptorTopic(BaseTopic): BaseTopic.__init__(self, db, fs, msg) def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False): - # check that this id is not present - _filter = {"id": final_content["id"]} - if _id: + # 1. validate again with pyangbind + # 1.1. remove internal keys + internal_keys = {} + for k in ("_id", "_admin"): + if k in final_content: + internal_keys[k] = final_content.pop(k) + serialized = self._validate_input_new(final_content, force) + # 1.2. modify final_content with a serialized version + final_content.clear() + final_content.update(serialized) + # 1.3. restore internal keys + for k, v in internal_keys.items(): + final_content[k] = v + + # 2. check that this id is not present + if "id" in edit_content: + _filter = self._get_project_filter(session, write=False, show_all=False) + _filter["id"] = final_content["id"] _filter["_id.neq"] = _id - - _filter.update(self._get_project_filter(session, write=False, show_all=False)) - if self.db.get_one(self.topic, _filter, fail_on_empty=False): - raise EngineException("{} with id '{}' already exists for this project".format(self.topic[:-1], - final_content["id"]), - HTTPStatus.CONFLICT) - # TODO validate with pyangbind. Load and dumps to convert data types + if self.db.get_one(self.topic, _filter, fail_on_empty=False): + raise EngineException("{} with id '{}' already exists for this project".format(self.topic[:-1], + final_content["id"]), + HTTPStatus.CONFLICT) @staticmethod def format_on_new(content, project_id=None, make_public=False): @@ -335,6 +351,29 @@ class DescriptorTopic(BaseTopic): "future versions", http_code=HTTPStatus.NOT_ACCEPTABLE) return self.fs.file_open((storage['folder'], storage['zipfile']), "rb"), accept_zip + def pyangbind_validation(self, item, data, force=False): + try: + if item == "vnfds": + myvnfd = vnfd_im() + pybindJSONDecoder.load_ietf_json({'vnfd:vnfd-catalog': {'vnfd': [data]}}, None, None, obj=myvnfd, + path_helper=True, skip_unknown=force) + out = pybindJSON.dumps(myvnfd, mode="ietf") + elif item == "nsds": + mynsd = nsd_im() + pybindJSONDecoder.load_ietf_json({'nsd:nsd-catalog': {'nsd': [data]}}, None, None, obj=mynsd, + path_helper=True, skip_unknown=force) + out = pybindJSON.dumps(mynsd, mode="ietf") + else: + raise EngineException("Not possible to validate '{}' item".format(item), + http_code=HTTPStatus.INTERNAL_SERVER_ERROR) + + desc_out = self._remove_envelop(yaml.safe_load(out)) + return desc_out + + except Exception as e: + raise EngineException("Error in pyangbind validation: {}".format(str(e)), + http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + class VnfdTopic(DescriptorTopic): topic = "vnfds" @@ -354,8 +393,12 @@ class VnfdTopic(DescriptorTopic): clean_indata = clean_indata['vnfd-catalog'] if clean_indata.get('vnfd'): if not isinstance(clean_indata['vnfd'], list) or len(clean_indata['vnfd']) != 1: - raise EngineException("'vnfd' must be a list only one element") + raise EngineException("'vnfd' must be a list of only one element") clean_indata = clean_indata['vnfd'][0] + elif clean_indata.get('vnfd:vnfd'): + if not isinstance(clean_indata['vnfd:vnfd'], list) or len(clean_indata['vnfd:vnfd']) != 1: + raise EngineException("'vnfd:vnfd' must be a list of only one element") + clean_indata = clean_indata['vnfd:vnfd'][0] return clean_indata def check_conflict_on_del(self, session, _id, force=False): @@ -387,20 +430,20 @@ class VnfdTopic(DescriptorTopic): raise EngineException("There is soame NSD that depends on this VNFD", http_code=HTTPStatus.CONFLICT) def _validate_input_new(self, indata, force=False): - # TODO validate with pyangbind, serialize - + indata = self.pyangbind_validation("vnfds", indata, force) # Cross references validation in the descriptor - 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"]), + if indata.get("vdu"): + 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) for vdu in get_iterable(indata.get("vdu")): for interface in get_iterable(vdu.get("interface")): @@ -520,10 +563,11 @@ class VnfdTopic(DescriptorTopic): "vnf-configuration:config-primitive:name" .format(sgd["name"], sca["vnf-config-primitive-name-ref"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) + # TODO validata that if contains cloud-init-file or charms, have artifacts _admin.storage."pkg-dir" is not none return indata def _validate_input_edit(self, indata, force=False): - # TODO validate with pyangbind, serialize + # not needed to validate with pyangbind becuase it will be validated at check_conflict_on_edit return indata @@ -546,22 +590,21 @@ class NsdTopic(DescriptorTopic): clean_indata = clean_indata['nsd-catalog'] if clean_indata.get('nsd'): if not isinstance(clean_indata['nsd'], list) or len(clean_indata['nsd']) != 1: - raise EngineException("'nsd' must be a list only one element") + raise EngineException("'nsd' must be a list of only one element") clean_indata = clean_indata['nsd'][0] + elif clean_indata.get('nsd:nsd'): + if not isinstance(clean_indata['nsd:nsd'], list) or len(clean_indata['nsd:nsd']) != 1: + raise EngineException("'nsd:nsd' must be a list of only one element") + clean_indata = clean_indata['nsd:nsd'][0] return clean_indata def _validate_input_new(self, indata, force=False): - # transform constituent-vnfd:member-vnf-index to string - if indata.get("constituent-vnfd"): - for constituent_vnfd in indata["constituent-vnfd"]: - if "member-vnf-index" in constituent_vnfd: - constituent_vnfd["member-vnf-index"] = str(constituent_vnfd["member-vnf-index"]) - - # TODO validate with pyangbind, serialize + indata = self.pyangbind_validation("nsds", indata, force) + # TODO validata that if contains cloud-init-file or charms, have artifacts _admin.storage."pkg-dir" is not none return indata def _validate_input_edit(self, indata, force=False): - # TODO validate with pyangbind, serialize + # not needed to validate with pyangbind becuase it will be validated at check_conflict_on_edit return indata def _check_descriptor_dependencies(self, session, descriptor): @@ -584,7 +627,8 @@ class NsdTopic(DescriptorTopic): def check_conflict_on_edit(self, session, final_content, edit_content, _id, force=False): super().check_conflict_on_edit(session, final_content, edit_content, _id, force=force) - self._check_descriptor_dependencies(session, final_content) + if not force: + self._check_descriptor_dependencies(session, final_content) def check_conflict_on_del(self, session, _id, force=False): """