import os
import shutil
import functools
+import re
# import logging
from deepdiff import DeepDiff
from uuid import uuid4
from re import fullmatch
from zipfile import ZipFile
+from urllib.parse import urlparse
from osm_nbi.validation import (
ValidationError,
pdu_new_schema,
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,
__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
+valid_helm_chart_re = re.compile(
+ r"^[a-z0-9]([-a-z0-9]*[a-z0-9]/)?([a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
+)
+
class DescriptorTopic(BaseTopic):
def __init__(self, db, fs, msg, auth):
- BaseTopic.__init__(self, db, fs, msg, auth)
+ super().__init__(db, fs, msg, auth)
def _validate_input_new(self, indata, storage_params, force=False):
return indata
HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE,
)
file_pkg = self.fs.file_open(file_path, "a+b")
+
if isinstance(indata, dict):
indata_text = yaml.safe_dump(indata, indent=4, default_flow_style=False)
file_pkg.write(indata_text.encode(encoding="utf-8"))
# to preserve current expected behaviour
if "userDefinedData" in indata:
data = indata.pop("userDefinedData")
- if type(data) == dict:
+ if isinstance(data, dict):
indata["_admin"]["userDefinedData"] = data
else:
raise EngineException(
return
_filter = self._get_project_filter(session)
-
# check vnfrs using this vnfd
_filter["vnfd-id"] = _id
+
if self.db.get_list("vnfrs", _filter):
raise EngineException(
"There is at least one VNF instance using this descriptor",
# check NSD referencing this VNFD
del _filter["vnfd-id"]
_filter["vnfd-id"] = descriptor_id
+
if self.db.get_list("nsds", _filter):
raise EngineException(
"There is at least one NS package referencing this descriptor",
self.validate_internal_virtual_links(indata)
self.validate_monitoring_params(indata)
self.validate_scaling_group_descriptor(indata)
+ self.validate_healing_group_descriptor(indata)
+ self.validate_alarm_group_descriptor(indata)
+ self.validate_storage_compute_descriptor(indata)
+ self.validate_helm_chart(indata)
return indata
+ @staticmethod
+ def validate_helm_chart(indata):
+ def is_url(url):
+ result = urlparse(url)
+ return all([result.scheme, result.netloc])
+
+ kdus = indata.get("kdu", [])
+ for kdu in kdus:
+ helm_chart_value = kdu.get("helm-chart")
+ if not helm_chart_value:
+ continue
+ if not (
+ valid_helm_chart_re.match(helm_chart_value) or is_url(helm_chart_value)
+ ):
+ raise EngineException(
+ "helm-chart '{}' is not valid".format(helm_chart_value),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
@staticmethod
def validate_mgmt_interface_connection_point(indata):
if not indata.get("vdu"):
@staticmethod
def validate_scaling_group_descriptor(indata):
all_monitoring_params = set()
+ all_vdu_ids = set()
+ for df in get_iterable(indata.get("df")):
+ for il in get_iterable(df.get("instantiation-level")):
+ for vl in get_iterable(il.get("vdu-level")):
+ all_vdu_ids.add(vl.get("vdu-id"))
+
for ivld in get_iterable(indata.get("int-virtual-link-desc")):
for mp in get_iterable(ivld.get("monitoring-parameters")):
all_monitoring_params.add(mp.get("id"))
for mp in get_iterable(df.get("monitoring-parameter")):
all_monitoring_params.add(mp.get("id"))
+ for df in get_iterable(indata.get("df")):
+ for sa in get_iterable(df.get("scaling-aspect")):
+ for deltas in get_iterable(
+ sa.get("aspect-delta-details").get("deltas")
+ ):
+ for vds in get_iterable(deltas.get("vdu-delta")):
+ sa_vdu_id = vds.get("id")
+ if sa_vdu_id and sa_vdu_id not in all_vdu_ids:
+ raise EngineException(
+ "df[id='{}']:scaling-aspect[id='{}']:aspect-delta-details"
+ "[delta='{}']: "
+ "vdu-id='{}' not defined in vdu".format(
+ df["id"],
+ sa["id"],
+ deltas["id"],
+ sa_vdu_id,
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
for df in get_iterable(indata.get("df")):
for sa in get_iterable(df.get("scaling-aspect")):
for sp in get_iterable(sa.get("scaling-policy")):
http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
)
+ @staticmethod
+ def validate_healing_group_descriptor(indata):
+ all_vdu_ids = set()
+ for df in get_iterable(indata.get("df")):
+ for il in get_iterable(df.get("instantiation-level")):
+ for vl in get_iterable(il.get("vdu-level")):
+ all_vdu_ids.add(vl.get("vdu-id"))
+
+ for df in get_iterable(indata.get("df")):
+ for ha in get_iterable(df.get("healing-aspect")):
+ for hp in get_iterable(ha.get("healing-policy")):
+ hp_monitoring_param = hp.get("vdu-id")
+ if hp_monitoring_param and hp_monitoring_param not in all_vdu_ids:
+ raise EngineException(
+ "df[id='{}']:healing-aspect[id='{}']:healing-policy"
+ "[name='{}']: "
+ "vdu-id='{}' not defined in vdu".format(
+ df["id"],
+ ha["id"],
+ hp["event-name"],
+ hp_monitoring_param,
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ @staticmethod
+ def validate_alarm_group_descriptor(indata):
+ all_monitoring_params = set()
+ for ivld in get_iterable(indata.get("int-virtual-link-desc")):
+ for mp in get_iterable(ivld.get("monitoring-parameters")):
+ all_monitoring_params.add(mp.get("id"))
+
+ for vdu in get_iterable(indata.get("vdu")):
+ for mp in get_iterable(vdu.get("monitoring-parameter")):
+ all_monitoring_params.add(mp.get("id"))
+
+ for df in get_iterable(indata.get("df")):
+ for mp in get_iterable(df.get("monitoring-parameter")):
+ all_monitoring_params.add(mp.get("id"))
+
+ for vdus in get_iterable(indata.get("vdu")):
+ for alarms in get_iterable(vdus.get("alarm")):
+ alarm_monitoring_param = alarms.get("vnf-monitoring-param-ref")
+ if (
+ alarm_monitoring_param
+ and alarm_monitoring_param not in all_monitoring_params
+ ):
+ raise EngineException(
+ "vdu[id='{}']:alarm[id='{}']:"
+ "vnf-monitoring-param-ref='{}' not defined in any monitoring-param".format(
+ vdus["id"],
+ alarms["alarm-id"],
+ alarm_monitoring_param,
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ @staticmethod
+ def validate_storage_compute_descriptor(indata):
+ all_vsd_ids = set()
+ for vsd in get_iterable(indata.get("virtual-storage-desc")):
+ all_vsd_ids.add(vsd.get("id"))
+
+ all_vcd_ids = set()
+ for vcd in get_iterable(indata.get("virtual-compute-desc")):
+ all_vcd_ids.add(vcd.get("id"))
+
+ for vdus in get_iterable(indata.get("vdu")):
+ for vsd_ref in vdus.get("virtual-storage-desc"):
+ if vsd_ref and vsd_ref not in all_vsd_ids:
+ raise EngineException(
+ "vdu[virtual-storage-desc='{}']"
+ "not defined in vnfd".format(
+ vsd_ref,
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ for vdus in get_iterable(indata.get("vdu")):
+ vcd_ref = vdus.get("virtual-compute-desc")
+ if vcd_ref and vcd_ref not in all_vcd_ids:
+ raise EngineException(
+ "vdu[virtual-compute-desc='{}']"
+ "not defined in vnfd".format(
+ vdus["virtual-compute-desc"],
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
def delete_extra(self, session, _id, db_content, not_send_msg=None):
"""
Deletes associate file system storage (via super)
topic_msg = "nsd"
def __init__(self, db, fs, msg, auth):
- DescriptorTopic.__init__(self, db, fs, msg, auth)
+ super().__init__(db, fs, msg, auth)
def pyangbind_validation(self, item, data, force=False):
if self._descriptor_data_is_in_old_format(data):
# 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("virtual-link-desc")):
self.validate_vld_mgmt_network_with_virtual_link_protocol_data(vld, indata)
+ for fg in get_iterable(indata.get("vnffgd")):
+ self.validate_vnffgd_data(fg, indata)
self.validate_vnf_profiles_vnfd_id(indata)
http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
)
+ @staticmethod
+ def validate_vnffgd_data(fg, indata):
+ position_list = []
+ all_vnf_ids = set(get_iterable(fg.get("vnf-profile-id")))
+ for fgposition in get_iterable(fg.get("nfp-position-element")):
+ position_list.append(fgposition["id"])
+
+ for nfpd in get_iterable(fg.get("nfpd")):
+ nfp_position = []
+ for position in get_iterable(nfpd.get("position-desc-id")):
+ nfp_position = position.get("nfp-position-element-id")
+ if position == "nfp-position-element-id":
+ nfp_position = position.get("nfp-position-element-id")
+ if nfp_position[0] not in position_list:
+ raise EngineException(
+ "Error at vnffgd nfpd[id='{}']:nfp-position-element-id='{}' "
+ "does not match any nfp-position-element".format(
+ nfpd["id"], nfp_position[0]
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ for cp in get_iterable(position.get("cp-profile-id")):
+ for cpe in get_iterable(cp.get("constituent-profile-elements")):
+ constituent_base_element_id = cpe.get(
+ "constituent-base-element-id"
+ )
+ if (
+ constituent_base_element_id
+ and constituent_base_element_id not in all_vnf_ids
+ ):
+ raise EngineException(
+ "Error at vnffgd constituent_profile[id='{}']:vnfd-id='{}' "
+ "does not match any constituent-base-element-id".format(
+ cpe["id"], constituent_base_element_id
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
@staticmethod
def validate_vnf_profiles_vnfd_id(indata):
all_vnfd_ids = set(get_iterable(indata.get("vnfd-id")))
# to preserve current expected behaviour
if "userDefinedData" in indata:
data = indata.pop("userDefinedData")
- if type(data) == dict:
+ if isinstance(data, dict):
indata["_admin"]["userDefinedData"] = data
else:
raise EngineException(
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
+
+ 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,
+ )