X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=blobdiff_plain;f=osm_nbi%2Fdescriptor_topics.py;h=0ad3e833291e6c8e9376746ccdf38be908271553;hp=26aea635bb78e21f20a318c83c108b206b52c7c4;hb=2325828219f5009508f34b34a43dfcdf58d0aa0b;hpb=df718ae72b8109633b04984ea4dfa574319f13ab diff --git a/osm_nbi/descriptor_topics.py b/osm_nbi/descriptor_topics.py index 26aea63..0ad3e83 100644 --- a/osm_nbi/descriptor_topics.py +++ b/osm_nbi/descriptor_topics.py @@ -17,6 +17,7 @@ import tarfile import yaml import json import importlib +import copy # import logging from hashlib import md5 from osm_common.dbbase import DbException, deep_update_rfc7396 @@ -32,6 +33,7 @@ etsi_nfv_nsd = importlib.import_module("osm_im.etsi-nfv-nsd") from osm_im.nst import nst as nst_im from pyangbind.lib.serialise import pybindJSONDecoder import pyangbind.lib.pybindJSON as pybindJSON +from osm_nbi import utils __author__ = "Alfonso Tierno " @@ -42,7 +44,7 @@ class DescriptorTopic(BaseTopic): BaseTopic.__init__(self, db, fs, msg, auth) def check_conflict_on_edit(self, session, final_content, edit_content, _id): - super().check_conflict_on_edit(session, final_content, edit_content, _id) + final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id) def _check_unique_id_name(descriptor, position=""): for desc_key, desc_item in descriptor.items(): @@ -73,24 +75,29 @@ class DescriptorTopic(BaseTopic): internal_keys[k] = final_content.pop(k) storage_params = internal_keys["_admin"].get("storage") serialized = self._validate_input_new(final_content, storage_params, session["force"]) + # 1.2. modify final_content with a serialized version - final_content.clear() - final_content.update(serialized) + final_content = copy.deepcopy(serialized) # 1.3. restore internal keys for k, v in internal_keys.items(): final_content[k] = v if session["force"]: - return + return final_content + # 2. check that this id is not present if "id" in edit_content: _filter = self._get_project_filter(session) + _filter["id"] = final_content["id"] _filter["_id.neq"] = _id + 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) + return final_content + @staticmethod def format_on_new(content, project_id=None, make_public=False): BaseTopic.format_on_new(content, project_id=project_id, make_public=make_public) @@ -303,7 +310,7 @@ class DescriptorTopic(BaseTopic): self._update_input_with_kwargs(indata, kwargs) deep_update_rfc7396(current_desc, indata) - self.check_conflict_on_edit(session, current_desc, indata, _id=_id) + current_desc = self.check_conflict_on_edit(session, current_desc, indata, _id=_id) current_desc["_admin"]["modified"] = time() self.db.replace(self.topic, _id, current_desc) self.fs.dir_rename(temp_folder, _id) @@ -460,19 +467,13 @@ class VnfdTopic(DescriptorTopic): raise EngineException("ERROR: Unsupported descriptor format. Please, use an ETSI SOL006 descriptor.", http_code=HTTPStatus.UNPROCESSABLE_ENTITY) try: - virtual_compute_descriptors = data.get('virtual-compute-desc') - virtual_storage_descriptors = data.get('virtual-storage-desc') myvnfd = etsi_nfv_vnfd.etsi_nfv_vnfd() pybindJSONDecoder.load_ietf_json({'etsi-nfv-vnfd:vnfd': data}, None, None, obj=myvnfd, path_helper=True, skip_unknown=force) out = pybindJSON.dumps(myvnfd, mode="ietf") desc_out = self._remove_envelop(yaml.safe_load(out)) desc_out = self._remove_yang_prefixes_from_descriptor(desc_out) - if virtual_compute_descriptors: - desc_out['virtual-compute-desc'] = virtual_compute_descriptors - if virtual_storage_descriptors: - desc_out['virtual-storage-desc'] = virtual_storage_descriptors - return desc_out + return utils.deep_update_dict(data, desc_out) except Exception as e: raise EngineException("Error in pyangbind validation: {}".format(str(e)), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) @@ -499,7 +500,7 @@ class VnfdTopic(DescriptorTopic): return clean_indata def check_conflict_on_edit(self, session, final_content, edit_content, _id): - super().check_conflict_on_edit(session, final_content, edit_content, _id) + final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id) # set type of vnfd contains_pdu = False @@ -514,6 +515,7 @@ class VnfdTopic(DescriptorTopic): elif contains_vdu: final_content["_admin"]["type"] = "vnfd" # if neither vud nor pdu do not fill type + return final_content def check_conflict_on_del(self, session, _id, db_content): """ @@ -559,8 +561,8 @@ class VnfdTopic(DescriptorTopic): for vdu in get_iterable(indata.get("vdu")): self.validate_vdu_internal_connection_points(vdu) - self._validate_vdu_charms_in_package(storage_params, vdu, indata) self._validate_vdu_cloud_init_in_package(storage_params, vdu, indata) + self._validate_vdu_charms_in_package(storage_params, indata) self._validate_vnf_charms_in_package(storage_params, indata) @@ -619,14 +621,20 @@ class VnfdTopic(DescriptorTopic): http_code=HTTPStatus.UNPROCESSABLE_ENTITY) # TODO: Validate k8s-cluster-net points to a valid k8s-cluster:nets ? - def _validate_vdu_charms_in_package(self, storage_params, vdu, indata): - if not vdu.get("vdu-configuration"): - return - for vdu_configuration in get_iterable(indata.get("vdu-configuration")): - if vdu_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, indata): + for df in indata["df"]: + if "lcm-operations-configuration" in df and "operate-vnf-op-config" in df["lcm-operations-configuration"]: + configs = df["lcm-operations-configuration"]["operate-vnf-op-config"].get("day1-2", []) + vdus = df["vdu-profile"] + for vdu in vdus: + for config in configs: + if config["id"] == vdu["id"] and utils.find_in_list( + config.get("execution-environment-list", []), + lambda ee: "juju" in ee + ): + 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_cloud_init_in_package(self, storage_params, vdu, indata): if not vdu.get("cloud-init-file"): @@ -636,13 +644,21 @@ class VnfdTopic(DescriptorTopic): "package".format(indata["id"], vdu["id"])) def _validate_vnf_charms_in_package(self, storage_params, indata): - if not indata.get("vnf-configuration"): - return - for vnf_configuration in get_iterable(indata.get("vnf-configuration")): - if 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"])) + # Get VNF configuration through new container + for deployment_flavor in indata.get('df', []): + if "lcm-operations-configuration" not in deployment_flavor: + return + if "operate-vnf-op-config" not in deployment_flavor["lcm-operations-configuration"]: + return + for day_1_2_config in deployment_flavor["lcm-operations-configuration"]["operate-vnf-op-config"]["day1-2"]: + if day_1_2_config["id"] == indata["id"]: + if utils.find_in_list( + day_1_2_config.get("execution-environment-list", []), + lambda ee: "juju" in ee + ): + 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"): @@ -753,19 +769,25 @@ class VnfdTopic(DescriptorTopic): http_code=HTTPStatus.UNPROCESSABLE_ENTITY) for sca in get_iterable(sa.get("scaling-config-action")): - if not indata.get("vnf-configuration"): - raise EngineException("'vnf-configuration' not defined in the descriptor but it is referenced " - "by df[id='{}']:scaling-aspect[id='{}']:scaling-config-action" + if "lcm-operations-configuration" not in df \ + or "operate-vnf-op-config" not in df["lcm-operations-configuration"] \ + or not utils.find_in_list( + df["lcm-operations-configuration"]["operate-vnf-op-config"].get("day1-2", []), + lambda config: config["id"] == indata["id"]): + raise EngineException("'day1-2 configuration' not defined in the descriptor but it is " + "referenced by df[id='{}']:scaling-aspect[id='{}']:scaling-config-action" .format(df["id"], sa["id"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) - for configuration in get_iterable(indata["vnf-configuration"]): + for configuration in get_iterable( + df["lcm-operations-configuration"]["operate-vnf-op-config"].get("day1-2", []) + ): for primitive in get_iterable(configuration.get("config-primitive")): if primitive["name"] == sca["vnf-config-primitive-name-ref"]: break else: raise EngineException("df[id='{}']:scaling-aspect[id='{}']:scaling-config-action:vnf-" "config-primitive-name-ref='{}' does not match any " - "vnf-configuration:config-primitive:name" + "day1-2 configuration:config-primitive:name" .format(df["id"], sa["id"], sca["vnf-config-primitive-name-ref"]), http_code=HTTPStatus.UNPROCESSABLE_ENTITY) @@ -808,12 +830,15 @@ class NsdTopic(DescriptorTopic): raise EngineException("ERROR: Unsupported descriptor format. Please, use an ETSI SOL006 descriptor.", http_code=HTTPStatus.UNPROCESSABLE_ENTITY) try: + nsd_vnf_profiles = data.get('df', [{}])[0].get('vnf-profile', []) mynsd = etsi_nfv_nsd.etsi_nfv_nsd() pybindJSONDecoder.load_ietf_json({'nsd': {'nsd': [data]}}, None, None, obj=mynsd, path_helper=True, skip_unknown=force) out = pybindJSON.dumps(mynsd, mode="ietf") desc_out = self._remove_envelop(yaml.safe_load(out)) desc_out = self._remove_yang_prefixes_from_descriptor(desc_out) + if nsd_vnf_profiles: + desc_out['df'][0]['vnf-profile'] = nsd_vnf_profiles return desc_out except Exception as e: raise EngineException("Error in pyangbind validation: {}".format(str(e)), @@ -968,10 +993,12 @@ class NsdTopic(DescriptorTopic): 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) + final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id) self._check_descriptor_dependencies(session, final_content) + return final_content + def check_conflict_on_del(self, session, _id, db_content): """ Check that there is not any NSR that uses this NSD. Only NSRs belonging to this project are considered. Note @@ -1075,9 +1102,10 @@ class NstTopic(DescriptorTopic): "existing nsd".format(nsd_id), http_code=HTTPStatus.CONFLICT) def check_conflict_on_edit(self, session, final_content, edit_content, _id): - super().check_conflict_on_edit(session, final_content, edit_content, _id) + final_content = super().check_conflict_on_edit(session, final_content, edit_content, _id) self._check_descriptor_dependencies(session, final_content) + return final_content def check_conflict_on_del(self, session, _id, db_content): """