from hashlib import md5
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 time import time
+from osm_nbi.validation import ValidationError, pdu_new_schema, pdu_edit_schema
+from osm_nbi.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 osm_im.nst import nst as nst_im
class DescriptorTopic(BaseTopic):
- def __init__(self, db, fs, msg):
- BaseTopic.__init__(self, db, fs, msg)
+ def __init__(self, db, fs, msg, auth):
+ 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)
+
+ def _check_unique_id_name(descriptor, position=""):
+ for desc_key, desc_item in descriptor.items():
+ if isinstance(desc_item, list) and desc_item:
+ used_ids = []
+ desc_item_id = None
+ for index, list_item in enumerate(desc_item):
+ if isinstance(list_item, dict):
+ _check_unique_id_name(list_item, "{}.{}[{}]"
+ .format(position, desc_key, index))
+ # Base case
+ if index == 0 and (list_item.get("id") or list_item.get("name")):
+ desc_item_id = "id" if list_item.get("id") else "name"
+ if desc_item_id and list_item.get(desc_item_id):
+ if list_item[desc_item_id] in used_ids:
+ position = "{}.{}[{}]".format(position, desc_key, index)
+ raise EngineException("Error: identifier {} '{}' is not unique and repeats at '{}'"
+ .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
internal_keys = {}
content["_admin"]["operationalState"] = "DISABLED"
content["_admin"]["usageState"] = "NOT_IN_USE"
- def delete_extra(self, session, _id):
+ def delete_extra(self, session, _id, db_content, not_send_msg=None):
+ """
+ Deletes file system storage associated with the descriptor
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+ :param _id: server internal id
+ :param db_content: The database content of the descriptor
+ :param not_send_msg: To not send message (False) or store content (list) instead
+ :return: None if ok or raises EngineException with the problem
+ """
self.fs.file_delete(_id, ignore_non_exist=True)
self.fs.file_delete(_id + "_", ignore_non_exist=True) # remove temp folder
:param indata: data to be inserted
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :return: _id: identity of the inserted data.
+ :return: _id, None: identity of the inserted data; and None as there is not any operation
"""
try:
+ # Check Quota
+ self.check_quota(session)
+
# _remove_envelop
if indata:
if "userDefinedData" in indata:
self.format_on_new(content, session["project_id"], make_public=session["public"])
_id = self.db.create(self.topic, content)
rollback.append({"topic": self.topic, "_id": _id})
- return _id
+ self._send_msg("created", {"_id": _id})
+ return _id, None
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
indata = json.load(content)
else:
error_text = "Invalid yaml format "
- indata = yaml.load(content)
+ indata = yaml.load(content, Loader=yaml.SafeLoader)
current_desc["_admin"]["storage"] = storage
current_desc["_admin"]["onboardingState"] = "ONBOARDED"
deep_update_rfc7396(current_desc, indata)
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)
indata["_id"] = _id
- self._send_msg("created", indata)
+ self._send_msg("edited", indata)
# TODO if descriptor has changed because kwargs update content and remove cached zip
# TODO if zip is not present creates one
topic = "vnfds"
topic_msg = "vnfd"
- def __init__(self, db, fs, msg):
- DescriptorTopic.__init__(self, db, fs, msg)
+ def __init__(self, db, fs, msg, auth):
+ DescriptorTopic.__init__(self, db, fs, msg, auth)
@staticmethod
def _remove_envelop(indata=None):
final_content["_admin"]["type"] = "vnfd"
# if neither vud nor pdu do not fill type
- def check_conflict_on_del(self, session, _id):
+ def check_conflict_on_del(self, session, _id, db_content):
"""
Check that there is not any NSD that uses this VNFD. Only NSDs belonging to this project are considered. Note
that VNFD can be public and be used by NSD of other projects. Also check there are not deployments, or vnfr
that uses this vnfd
:param session: contains "username", "admin", "force", "public", "project_id", "set_project"
- :param _id: vnfd inernal id
+ :param _id: vnfd internal id
+ :param db_content: The database content of the _id.
:return: None or raises EngineException with the conflict
"""
if session["force"]:
return
- descriptor = self.db.get_one("vnfds", {"_id": _id})
+ descriptor = db_content
descriptor_id = descriptor.get("id")
if not descriptor_id: # empty vnfd not uploaded
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 some VNFR that depends on this VNFD", http_code=HTTPStatus.CONFLICT)
+ raise EngineException("There is at least one VNF using this descriptor", http_code=HTTPStatus.CONFLICT)
+
+ # check NSD referencing this VNFD
del _filter["vnfd-id"]
- # check NSD using this VNFD
_filter["constituent-vnfd.ANYINDEX.vnfd-id-ref"] = descriptor_id
if self.db.get_list("nsds", _filter):
- raise EngineException("There is at least a NSD that depends on this VNFD", http_code=HTTPStatus.CONFLICT)
+ raise EngineException("There is at least one NSD referencing this descriptor",
+ http_code=HTTPStatus.CONFLICT)
def _validate_input_new(self, indata, storage_params, force=False):
indata = self.pyangbind_validation("vnfds", indata, force)
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
.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
topic = "nsds"
topic_msg = "nsd"
- def __init__(self, db, fs, msg):
- DescriptorTopic.__init__(self, db, fs, msg)
+ def __init__(self, db, fs, msg, auth):
+ DescriptorTopic.__init__(self, db, fs, msg, auth)
@staticmethod
def _remove_envelop(indata=None):
"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
+ 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)
return indata
def _validate_input_edit(self, indata, force=False):
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"])
- if not vnfd:
- 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"], referenced_vnfd_cp["member-vnf-index-ref"]),
- http_code=HTTPStatus.UNPROCESSABLE_ENTITY)
for vnfd_cp in get_iterable(vnfd.get("connection-point")):
if referenced_vnfd_cp.get("vnfd-connection-point-ref") == vnfd_cp["name"]:
break
self._check_descriptor_dependencies(session, final_content)
- def check_conflict_on_del(self, session, _id):
+ 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
that NSD can be public and be used by other projects.
:param session: contains "username", "admin", "force", "public", "project_id", "set_project"
- :param _id: vnfd inernal id
+ :param _id: nsd 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("id")
+ if not descriptor_id: # empty nsd not uploaded
+ return
+
+ # check NSD used by NS
_filter = self._get_project_filter(session)
- _filter["nsdId"] = _id
+ _filter["nsd-id"] = _id
if self.db.get_list("nsrs", _filter):
- raise EngineException("There is some NSR that depends on this NSD", http_code=HTTPStatus.CONFLICT)
+ raise EngineException("There is at least one NS using this descriptor", http_code=HTTPStatus.CONFLICT)
+
+ # check NSD referenced by NST
+ del _filter["nsd-id"]
+ _filter["netslice-subnet.ANYINDEX.nsd-ref"] = descriptor_id
+ if self.db.get_list("nsts", _filter):
+ raise EngineException("There is at least one NetSlice Template referencing this descriptor",
+ http_code=HTTPStatus.CONFLICT)
class NstTopic(DescriptorTopic):
topic = "nsts"
topic_msg = "nst"
- def __init__(self, db, fs, msg):
- DescriptorTopic.__init__(self, db, fs, msg)
+ def __init__(self, db, fs, msg, auth):
+ DescriptorTopic.__init__(self, db, fs, msg, auth)
@staticmethod
def _remove_envelop(indata=None):
self._check_descriptor_dependencies(session, final_content)
- def check_conflict_on_del(self, session, _id):
+ def check_conflict_on_del(self, session, _id, db_content):
"""
Check that there is not any NSIR that uses this NST. Only NSIRs belonging to this project are considered. Note
that NST can be public and be used by other projects.
:param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param _id: nst internal id
+ :param db_content: The database content of the _id.
:return: None or raises EngineException with the conflict
"""
# TODO: Check this method
return
# Get Network Slice Template from Database
_filter = self._get_project_filter(session)
- _filter["_id"] = _id
- nst = self.db.get_one("nsts", _filter)
-
- # Search NSIs using NST via nst-ref
- _filter = self._get_project_filter(session)
- _filter["nst-ref"] = nst["id"]
- nsis_list = self.db.get_list("nsis", _filter)
- for nsi_item in nsis_list:
- if nsi_item["_admin"].get("nsiState") != "TERMINATED":
- raise EngineException("There is some NSIS that depends on this NST", http_code=HTTPStatus.CONFLICT)
+ _filter["_admin.nst-id"] = _id
+ if self.db.get_list("nsis", _filter):
+ raise EngineException("there is at least one Netslice Instance using this descriptor",
+ http_code=HTTPStatus.CONFLICT)
class PduTopic(BaseTopic):
schema_new = pdu_new_schema
schema_edit = pdu_edit_schema
- def __init__(self, db, fs, msg):
- BaseTopic.__init__(self, db, fs, msg)
+ def __init__(self, db, fs, msg, auth):
+ BaseTopic.__init__(self, db, fs, msg, auth)
@staticmethod
def format_on_new(content, project_id=None, make_public=False):
content["_admin"]["operationalState"] = "ENABLED"
content["_admin"]["usageState"] = "NOT_IN_USE"
- def check_conflict_on_del(self, session, _id):
+ def check_conflict_on_del(self, session, _id, db_content):
+ """
+ Check that there is not any vnfr that uses this PDU
+ :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+ :param _id: pdu internal id
+ :param db_content: The database content of the _id.
+ :return: None or raises EngineException with the conflict
+ """
if session["force"]:
return
- # TODO Is it needed to check descriptors _admin.project_read/project_write??
- _filter = {"vdur.pdu-id": _id}
+
+ _filter = self._get_project_filter(session)
+ _filter["vdur.pdu-id"] = _id
if self.db.get_list("vnfrs", _filter):
- raise EngineException("There is some NSR that uses this PDU", http_code=HTTPStatus.CONFLICT)
+ raise EngineException("There is at least one VNF using this PDU", http_code=HTTPStatus.CONFLICT)