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 <alfonso.tiernosepulveda@telefonica.com>"
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):
BaseTopic.format_on_new(content, project_id=project_id, make_public=make_public)
content["_admin"]["onboardingState"] = "CREATED"
content["_admin"]["operationalState"] = "DISABLED"
- content["_admin"]["usageSate"] = "NOT_IN_USE"
+ content["_admin"]["usageState"] = "NOT_IN_USE"
def delete(self, session, _id, force=False, dry_run=False):
"""
"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"
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_edit(self, session, final_content, edit_content, _id, force=False):
+ super().check_conflict_on_edit(session, final_content, edit_content, _id, force=force)
+
+ # set type of vnfd
+ contains_pdu = False
+ contains_vdu = False
+ for vdu in get_iterable(final_content.get("vdu")):
+ if vdu.get("pdu-type"):
+ contains_pdu = True
+ else:
+ contains_vdu = True
+ if contains_pdu:
+ final_content["_admin"]["type"] = "hnfd" if contains_vdu else "pnfd"
+ elif contains_vdu:
+ final_content["_admin"]["type"] = "vnfd"
+ # if neither vud nor pdu do not fill type
+
def check_conflict_on_del(self, session, _id, force=False):
"""
Check that there is not any NSD that uses this VNFD. Only NSDs belonging to this project are considered. Note
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 indata.get("mgmt-interface"):
+ 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"].get("cp")),
+ raise EngineException("mgmt-interface:cp='{}' must match an existing connection-point"
+ .format(indata["mgmt-interface"]["cp"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
- else:
- raise EngineException("'mgmt-interface' is a mandatory field and it is not defined",
- http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
for vdu in get_iterable(indata.get("vdu")):
for interface in get_iterable(vdu.get("interface")):
if interface.get("external-connection-point-ref"):
for cp in get_iterable(indata.get("connection-point")):
- if cp["name"] == interface.get("external-connection-point-ref"):
+ if cp["name"] == interface["external-connection-point-ref"]:
break
else:
- raise EngineException("vdu[id='{}']:interface[name='{}']:external-connection-point-ref='{} "
+ 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)
- for interface in get_iterable(vdu.get("interface")):
- if interface.get("internal-connection-point-ref"):
+
+ elif interface.get("internal-connection-point-ref"):
for internal_cp in get_iterable(vdu.get("internal-connection-point")):
- if interface.get("internal-connection-point-ref") == internal_cp.get("id"):
+ if interface["internal-connection-point-ref"] == internal_cp.get("id"):
break
else:
- raise EngineException("vdu[id='{}']:interface[name='{}']:internal-connection-point-ref='{}' is "
- "not associated to an internal-connection-point".format
- (vdu["id"], interface["name"],
- interface["internal-connection-point-ref"]),
+ 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 ivld in get_iterable(indata.get("internal-vld")):
for icp in get_iterable(ivld.get("internal-connection-point")):
if icp_mark:
break
else:
- raise EngineException("'internal-vld='{}':internal-connection-point='{}'' must match an existing "
- "internal-connection-point".format(ivld["id"], icp["id-ref"]),
+ raise EngineException("internal-vld[id='{}']:internal-connection-point='{}' must match an existing "
+ "vdu:internal-connection-point".format(ivld["id"], icp["id-ref"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
if ivld.get("ip-profile-ref"):
- for ip_prof in get_iterable(indata["ip-profiles"]):
+ for ip_prof in get_iterable(indata.get("ip-profiles")):
if ip_prof["name"] == get_iterable(ivld.get("ip-profile-ref")):
break
else:
- raise EngineException("internal-vld='{}':ip-profile-ref='{}' does not exist".format(
+ raise EngineException("internal-vld[id='{}']:ip-profile-ref='{}' does not exist".format(
ivld["id"], ivld["ip-profile-ref"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
for mp in get_iterable(indata.get("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"]["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
break
else:
raise EngineException("monitoring-param:vdu-monitoring-param:vdu-monitoring-param-ref='{}' not "
- "defined in VDU vdu[id='{}'] or does not exist an VDU with this id"
+ "defined at vdu[id='{}'] or vdu does not exist"
.format(mp["vdu-monitoring-param"]["vdu-monitoring-param-ref"],
mp["vdu-monitoring-param"]["vdu-ref"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
if mp_vm_mark:
break
else:
- raise EngineException("monitoring-param:vdu-metric:vdu-metric-name-ref='{}' not defined in VDU "
- "vdu[id='{}'] or or does not exist an VDU with this id"
+ raise EngineException("monitoring-param:vdu-metric:vdu-metric-name-ref='{}' not defined at "
+ "vdu[id='{}'] or vdu does not exist"
.format(mp["vdu-metric"]["vdu-metric-name-ref"],
mp["vdu-metric"]["vdu-ref"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
if mp["id"] == get_iterable(sc.get("vnf-monitoring-param-ref")):
break
else:
- raise EngineException("scaling-group-descriptor:name='{}':scaling-criteria:name='{}':"
- "vnf-monitoring-param-ref='{}' not defined in any 'monitoring-param"
- "['id']'".format(sgd["name"], sc["name"], sc["vnf-monitoring-param-ref"]),
+ raise EngineException("scaling-group-descriptor[name='{}']:scaling-criteria[name='{}']:"
+ "vnf-monitoring-param-ref='{}' not defined in any monitoring-param"
+ .format(sgd["name"], sc["name"], sc["vnf-monitoring-param-ref"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
for sgd_vdu in get_iterable(sgd.get("vdu")):
sgd_vdu_mark = False
if sgd_vdu_mark:
break
else:
- raise EngineException("scaling-group-descriptor[name='{}']:vdu not defined and"
- "it is mandatory in any 'scaling-group-descriptor'"
- .format(sgd["name"]),
+ raise EngineException("scaling-group-descriptor[name='{}']:vdu-id-ref={} does not match any vdu"
+ .format(sgd["name"], sgd_vdu["vdu-id-ref"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
for sca in get_iterable(sgd.get("scaling-config-action")):
- sca_vcp_mark = False
- if indata.get("vnf-configuration"):
- for primitive in get_iterable(indata["vnf-configuration"].get("config-primitive")):
- if primitive["name"] == sca["vnf-config-primitive-name-ref"]:
- sca_vcp_mark = True
- break
- else:
- raise EngineException("scaling-group-descriptor:name='{}':scaling-config-action:vnf-config-"
- "primitive-name-ref='{}' not defined in "
- "'vnf-configuration:config-primitive'"
- .format(sgd["name"], sca["vnf-config-primitive-name-ref"]),
- http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+ if not indata.get("vnf-configuration"):
+ raise EngineException("'vnf-configuration' not defined in the descriptor but it is referenced by "
+ "scaling-group-descriptor[name='{}']:scaling-config-action"
+ .format(sgd["name"]),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
+ for primitive in get_iterable(indata["vnf-configuration"].get("config-primitive")):
+ if primitive["name"] == sca["vnf-config-primitive-name-ref"]:
+ break
else:
- raise EngineException("'vnf-configuration' not defined in the descriptor but it is referenced",
+ raise EngineException("scaling-group-descriptor[name='{}']:scaling-config-action:vnf-config-"
+ "primitive-name-ref='{}' does not match any "
+ "vnf-configuration:config-primitive:name"
+ .format(sgd["name"], sca["vnf-config-primitive-name-ref"]),
http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
- if sca_vcp_mark:
- break
+ # 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
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):
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):
"""
@staticmethod
def format_on_new(content, project_id=None, make_public=False):
- BaseTopic.format_on_new(content, project_id=None, make_public=make_public)
+ BaseTopic.format_on_new(content, project_id=project_id, make_public=make_public)
content["_admin"]["onboardingState"] = "CREATED"
- content["_admin"]["operationalState"] = "DISABLED"
- content["_admin"]["usageSate"] = "NOT_IN_USE"
+ content["_admin"]["operationalState"] = "ENABLED"
+ content["_admin"]["usageState"] = "NOT_IN_USE"
def check_conflict_on_del(self, session, _id, force=False):
if force: