from http import HTTPStatus
from time import time
from copy import copy, deepcopy
-from validation import validate_input, ValidationError, ns_instantiate, ns_action, ns_scale, nsi_instantiate
-from base_topic import BaseTopic, EngineException, get_iterable
+from osm_nbi.validation import validate_input, ValidationError, ns_instantiate, ns_action, ns_scale, nsi_instantiate
+from osm_nbi.base_topic import BaseTopic, EngineException, get_iterable
# from descriptor_topics import DescriptorTopic
from yaml import safe_dump
from osm_common.dbbase import DbException
+from osm_common.msgbase import MsgException
+from osm_common.fsbase import FsException
from re import match # For checking that additional parameter names are valid Jinja2 identifiers
__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
class NsrTopic(BaseTopic):
topic = "nsrs"
topic_msg = "ns"
+ schema_new = ns_instantiate
- 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_descriptor_dependencies(self, session, descriptor):
"""
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"]["nsState"] = "NOT_INSTANTIATED"
+ return None
def check_conflict_on_del(self, session, _id, db_content):
"""
:param indata: params to be used for the nsr
:param kwargs: used to override the indata descriptor
:param headers: http request headers
- :return: the _id of nsr descriptor created at database
+ :return: the _id of nsr descriptor created at database. Or an exception of type
+ EngineException, ValidationError, DbException, FsException, MsgException.
+ Note: Exceptions are not captured on purpose. They should be captured at called
"""
try:
+ step = "checking quotas"
+ self.check_quota(session)
+
+ step = "validating input parameters"
ns_request = self._remove_envelop(indata)
# Override descriptor with query string kwargs
self._update_input_with_kwargs(ns_request, kwargs)
"id": nsr_id,
"_id": nsr_id,
# "input-parameter": xpath, value,
- "ssh-authorized-key": ns_request.get("key-pair-ref"), # TODO remove
+ "ssh-authorized-key": ns_request.get("ssh_keys"), # TODO remove
}
ns_request["nsr_id"] = nsr_id
# Create vld
member_vnf["vnfd-id-ref"], member_vnf["member-vnf-index"])
# add at database
- BaseTopic.format_on_new(vnfr_descriptor, session["project_id"], make_public=session["public"])
+ self.format_on_new(vnfr_descriptor, session["project_id"], make_public=session["public"])
self.db.create("vnfrs", vnfr_descriptor)
rollback.append({"topic": "vnfrs", "_id": vnfr_id})
nsr_descriptor["constituent-vnfr-ref"].append(vnfr_id)
step = "creating nsr temporal folder"
self.fs.mkdir(nsr_id)
- return nsr_id
- except Exception as e:
- self.logger.exception("Exception {} at NsrTopic.new()".format(e), exc_info=True)
- raise EngineException("Error {}: {}".format(step, e))
- except ValidationError as e:
- raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
+ return nsr_id, None
+ except (ValidationError, EngineException, DbException, MsgException, FsException) as e:
+ raise type(e)("{} while '{}".format(e, step), http_code=e.http_code)
def edit(self, session, _id, indata=None, kwargs=None, content=None):
raise EngineException("Method edit called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
topic = "vnfrs"
topic_msg = None
- 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 delete(self, session, _id, dry_run=False):
raise EngineException("Method delete called directly", HTTPStatus.INTERNAL_SERVER_ERROR)
"terminate": None,
}
- 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_ns_operation(self, session, nsr, operation, indata):
"""
:param indata: descriptor with the parameters of the operation
:return: None
"""
- vnfds = {}
+ vnf_member_index_to_vnfd = {} # map between vnf_member_index to vnf descriptor.
vim_accounts = []
wim_accounts = []
nsd = nsr["nsd"]
def check_valid_vnf_member_index(member_vnf_index):
- # TODO change to vnfR
- for vnf in nsd["constituent-vnfd"]:
- if member_vnf_index == vnf["member-vnf-index"]:
- vnfd_id = vnf["vnfd-id-ref"]
- if vnfd_id not in vnfds:
- vnfds[vnfd_id] = self.db.get_one("vnfds", {"id": vnfd_id})
- return vnfds[vnfd_id]
- else:
+ # Obtain vnf descriptor. The vnfr is used to get the vnfd._id used for this member_vnf_index
+ if vnf_member_index_to_vnfd.get(member_vnf_index):
+ return vnf_member_index_to_vnfd[member_vnf_index]
+ vnfr = self.db.get_one("vnfrs",
+ {"nsr-id-ref": nsr["_id"], "member-vnf-index-ref": member_vnf_index},
+ fail_on_empty=False)
+ if not vnfr:
raise EngineException("Invalid parameter member_vnf_index='{}' is not one of the "
"nsd:constituent-vnfd".format(member_vnf_index))
+ vnfd = self.db.get_one("vnfds", {"_id": vnfr["vnfd-id"]}, fail_on_empty=False)
+ if not vnfd:
+ raise EngineException("vnfd id={} has been deleted!. Operation cannot be performed".
+ format(vnfr["vnfd-id"]))
+ vnf_member_index_to_vnfd[member_vnf_index] = vnfd # add to cache, avoiding a later look for
+ return vnfd
+
+ def check_valid_vdu(vnfd, vdu_id):
+ for vdud in get_iterable(vnfd.get("vdu")):
+ if vdud["id"] == vdu_id:
+ return vdud
+ else:
+ raise EngineException("Invalid parameter vdu_id='{}' not present at vnfd:vdu:id".format(vdu_id))
def _check_vnf_instantiation_params(in_vnfd, vnfd):
for in_ivld in get_iterable(in_vnfd.get("internal-vld")):
for ivld in get_iterable(vnfd.get("internal-vld")):
if in_ivld["name"] == ivld["name"] or in_ivld["name"] == ivld["id"]:
- for in_icp in get_iterable(in_ivld["internal-connection-point"]):
+ for in_icp in get_iterable(in_ivld.get("internal-connection-point")):
for icp in ivld["internal-connection-point"]:
if in_icp["id-ref"] == icp["id-ref"]:
break
# check vnf_member_index
if indata.get("vnf_member_index"):
indata["member_vnf_index"] = indata.pop("vnf_member_index") # for backward compatibility
- if not indata.get("member_vnf_index"):
- raise EngineException("Missing 'member_vnf_index' parameter")
- vnfd = check_valid_vnf_member_index(indata["member_vnf_index"])
+ if indata.get("member_vnf_index"):
+ vnfd = check_valid_vnf_member_index(indata["member_vnf_index"])
+ if indata.get("vdu_id"):
+ vdud = check_valid_vdu(vnfd, indata["vdu_id"])
+ descriptor_configuration = vdud.get("vdu-configuration", {}).get("config-primitive")
+ else:
+ descriptor_configuration = vnfd.get("vnf-configuration", {}).get("config-primitive")
+ else: # use a NSD
+ descriptor_configuration = nsd.get("ns-configuration", {}).get("config-primitive")
# check primitive
- for config_primitive in get_iterable(vnfd.get("vnf-configuration", {}).get("config-primitive")):
+ for config_primitive in get_iterable(descriptor_configuration):
if indata["primitive"] == config_primitive["name"]:
# check needed primitive_params are provided
if indata.get("primitive_params"):
paramd["name"], indata["primitive"]))
# check no extra primitive params are provided
if in_primitive_params_copy:
- raise EngineException("parameter/s '{}' not present at vnfd for primitive '{}'".format(
+ raise EngineException("parameter/s '{}' not present at vnfd /nsd for primitive '{}'".format(
list(in_primitive_params_copy.keys()), indata["primitive"]))
break
else:
- raise EngineException("Invalid primitive '{}' is not present at vnfd".format(indata["primitive"]))
+ raise EngineException("Invalid primitive '{}' is not present at vnfd/nsd".format(indata["primitive"]))
if operation == "scale":
vnfd = check_valid_vnf_member_index(indata["scaleVnfData"]["scaleByStepData"]["member-vnf-index"])
for scaling_group in get_iterable(vnfd.get("scaling-group-descriptor")):
:param headers: http request headers
:return: id of the nslcmops
"""
+ def check_if_nsr_is_not_slice_member(session, nsr_id):
+ nsis = None
+ db_filter = self._get_project_filter(session)
+ db_filter["_admin.nsrs-detailed-list.ANYINDEX.nsrId"] = nsr_id
+ nsis = self.db.get_one("nsis", db_filter, fail_on_empty=False, fail_on_more=False)
+ if nsis:
+ raise EngineException("The NS instance {} cannot be terminate because is used by the slice {}".format(
+ nsr_id, nsis["_id"]), http_code=HTTPStatus.CONFLICT)
+
try:
# Override descriptor with query string kwargs
self._update_input_with_kwargs(indata, kwargs)
nsr = self.db.get_one("nsrs", _filter)
# initial checking
+ if operation == "terminate" and slice_object is False:
+ check_if_nsr_is_not_slice_member(session, nsr["_id"])
if not nsr["_admin"].get("nsState") or nsr["_admin"]["nsState"] == "NOT_INSTANTIATED":
if operation == "terminate" and indata.get("autoremove"):
# NSR must be deleted
- return None # a none in this case is used to indicate not instantiated. It can be removed
+ return None, None # a none in this case is used to indicate not instantiated. It can be removed
if operation != "instantiate":
raise EngineException("ns_instance '{}' cannot be '{}' because it is not instantiated".format(
nsInstanceId, operation), HTTPStatus.CONFLICT)
self._update_vnfrs(session, rollback, nsr, indata)
nslcmop_desc = self._create_nslcmop(nsInstanceId, operation, indata)
+ _id = nslcmop_desc["_id"]
self.format_on_new(nslcmop_desc, session["project_id"], make_public=session["public"])
- _id = self.db.create("nslcmops", nslcmop_desc)
+ self.db.create("nslcmops", nslcmop_desc)
rollback.append({"topic": "nslcmops", "_id": _id})
if not slice_object:
self.msg.write("ns", operation, nslcmop_desc)
- return _id
- except ValidationError as e:
+ return _id, None
+ except ValidationError as e: # TODO remove try Except, it is captured at nbi.py
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)
# except DbException as e:
# raise EngineException("Cannot get ns_instance '{}': {}".format(e), HTTPStatus.NOT_FOUND)
topic = "nsis"
topic_msg = "nsi"
- def __init__(self, db, fs, msg):
- BaseTopic.__init__(self, db, fs, msg)
- self.nsrTopic = NsrTopic(db, fs, msg)
+ def __init__(self, db, fs, msg, auth):
+ BaseTopic.__init__(self, db, fs, msg, auth)
+ self.nsrTopic = NsrTopic(db, fs, msg, auth)
@staticmethod
def _format_ns_request(ns_request):
"""
try:
+ step = "checking quotas"
+ self.check_quota(session)
+
+ step = ""
slice_request = self._remove_envelop(indata)
# Override descriptor with query string kwargs
self._update_input_with_kwargs(slice_request, kwargs)
self._validate_input_new(slice_request, session["force"])
- step = ""
# look for nstd
step = "getting nstd id='{}' from database".format(slice_request.get("nstId"))
_filter = self._get_project_filter(session)
# Is the nss shared and instantiated?
_filter["_admin.nsrs-detailed-list.ANYINDEX.shared"] = True
_filter["_admin.nsrs-detailed-list.ANYINDEX.nsd-id"] = service["nsd-ref"]
+ _filter["_admin.nsrs-detailed-list.ANYINDEX.nss-id"] = service["id"]
nsi = self.db.get_one("nsis", _filter, fail_on_empty=False, fail_on_more=False)
-
if nsi and service.get("is-shared-nss"):
nsrs_detailed_list = nsi["_admin"]["nsrs-detailed-list"]
for nsrs_detailed_item in nsrs_detailed_list:
if nsrs_detailed_item["nsd-id"] == service["nsd-ref"]:
- _id_nsr = nsrs_detailed_item["nsrId"]
- break
+ if nsrs_detailed_item["nss-id"] == service["id"]:
+ _id_nsr = nsrs_detailed_item["nsrId"]
+ break
for netslice_subnet in nsi["_admin"]["netslice-subnet"]:
if netslice_subnet["nss-id"] == service["id"]:
indata_ns = netslice_subnet
indata_ns["nsName"] = slice_request.get("nsiName") + "." + service["id"]
indata_ns["vimAccountId"] = slice_request.get("vimAccountId")
indata_ns["nsDescription"] = service["description"]
- indata_ns["key-pair-ref"] = slice_request.get("key-pair-ref")
+ if slice_request.get("ssh_keys"):
+ indata_ns["ssh_keys"] = slice_request.get("ssh_keys")
if ns_params:
for ns_param in ns_params:
break
# Creates Nsr objects
- _id_nsr = self.nsrTopic.new(rollback, session, indata_ns, kwargs, headers)
+ _id_nsr, _ = self.nsrTopic.new(rollback, session, indata_ns, kwargs, headers)
nsrs_item = {"nsrId": _id_nsr, "shared": service.get("is-shared-nss"), "nsd-id": service["nsd-ref"],
- "nslcmop_instantiate": None}
+ "nss-id": service["id"], "nslcmop_instantiate": None}
indata_ns["nss-id"] = service["id"]
nsrs_list.append(nsrs_item)
nsi_netslice_subnet.append(indata_ns)
# Creating the entry in the database
self.db.create("nsis", nsi_descriptor)
rollback.append({"topic": "nsis", "_id": nsi_id})
- return nsi_id
- except Exception as e:
+ return nsi_id, None
+ except Exception as e: # TODO remove try Except, it is captured at nbi.py
self.logger.exception("Exception {} at NsiTopic.new()".format(e), exc_info=True)
raise EngineException("Error {}: {}".format(step, e))
except ValidationError as e:
"terminate": None
}
- def __init__(self, db, fs, msg):
- BaseTopic.__init__(self, db, fs, msg)
- self.nsi_NsLcmOpTopic = NsLcmOpTopic(self.db, self.fs, self.msg)
+ def __init__(self, db, fs, msg, auth):
+ BaseTopic.__init__(self, db, fs, msg, auth)
+ self.nsi_NsLcmOpTopic = NsLcmOpTopic(self.db, self.fs, self.msg, self.auth)
def _check_nsi_operation(self, session, nsir, operation, indata):
"""
"isCancelPending": False,
"links": {
"self": "/osm/nsilcm/v1/nsi_lcm_op_occs/" + _id,
- "nsInstance": "/osm/nsilcm/v1/netslice_instances/" + netsliceInstanceId,
+ "netsliceInstanceId": "/osm/nsilcm/v1/netslice_instances/" + netsliceInstanceId,
}
}
return nsilcmop
:param rollback: list to append created items at database in case a rollback must to be done
:param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: descriptor with the parameters of the operation. It must contains among others
- nsiInstanceId: _id of the nsir to perform the operation
+ netsliceInstanceId: _id of the nsir to perform the operation
operation: it can be: instantiate, terminate, action, TODO: update, heal
:param kwargs: used to override the indata descriptor
:param headers: http request headers
# Override descriptor with query string kwargs
self._update_input_with_kwargs(indata, kwargs)
operation = indata["lcmOperationType"]
- nsiInstanceId = indata["nsiInstanceId"]
+ netsliceInstanceId = indata["netsliceInstanceId"]
validate_input(indata, self.operation_schema[operation])
- # get nsi from nsiInstanceId
+ # get nsi from netsliceInstanceId
_filter = self._get_project_filter(session)
- _filter["_id"] = nsiInstanceId
+ _filter["_id"] = netsliceInstanceId
nsir = self.db.get_one("nsis", _filter)
del _filter["_id"]
if not nsir["_admin"].get("nsiState") or nsir["_admin"]["nsiState"] == "NOT_INSTANTIATED":
if operation == "terminate" and indata.get("autoremove"):
# NSIR must be deleted
- return None # a none in this case is used to indicate not instantiated. It can be removed
+ return None, None # a none in this case is used to indicate not instantiated. It can be removed
if operation != "instantiate":
raise EngineException("netslice_instance '{}' cannot be '{}' because it is not instantiated".format(
- nsiInstanceId, operation), HTTPStatus.CONFLICT)
+ netsliceInstanceId, operation), HTTPStatus.CONFLICT)
else:
if operation == "instantiate" and not session["force"]:
raise EngineException("netslice_instance '{}' cannot be '{}' because it is already instantiated".
- format(nsiInstanceId, operation), HTTPStatus.CONFLICT)
+ format(netsliceInstanceId, operation), HTTPStatus.CONFLICT)
# Creating all the NS_operation (nslcmop)
# Get service list from db
for index, nsr_item in enumerate(nsrs_list):
nsi = None
if nsr_item.get("shared"):
- _filter["_admin.nsrs-detailed-list.ANYINDEX.shared"] = True,
+ _filter["_admin.nsrs-detailed-list.ANYINDEX.shared"] = True
_filter["_admin.nsrs-detailed-list.ANYINDEX.nsrId"] = nsr_item["nsrId"]
_filter["_admin.nsrs-detailed-list.ANYINDEX.nslcmop_instantiate.ne"] = None
- _filter["_id.ne"] = nsiInstanceId
-
+ _filter["_id.ne"] = netsliceInstanceId
nsi = self.db.get_one("nsis", _filter, fail_on_empty=False, fail_on_more=False)
+ if operation == "terminate":
+ _update = {"_admin.nsrs-detailed-list.{}.nslcmop_instantiate".format(index): None}
+ self.db.set_one("nsis", {"_id": nsir["_id"]}, _update)
+
# looks the first nsi fulfilling the conditions but not being the current NSIR
if nsi:
nsi_admin_shared = nsi["_admin"]["nsrs-detailed-list"]
indata_ns["lcmOperationType"] = operation
indata_ns["nsInstanceId"] = service["_id"]
# Including netslice_id in the ns instantiate Operation
- indata_ns["netsliceInstanceId"] = nsiInstanceId
- del indata_ns["key-pair-ref"]
- # Creating NS_LCM_OP with the flag slice_object=True to not trigger the service instantiation
+ indata_ns["netsliceInstanceId"] = netsliceInstanceId
+ # Creating NS_LCM_OP with the flag slice_object=True to not trigger the service instantiation
# message via kafka bus
- nslcmop = self.nsi_NsLcmOpTopic.new(rollback, session, indata_ns, kwargs, headers,
- slice_object=True)
+ nslcmop, _ = self.nsi_NsLcmOpTopic.new(rollback, session, indata_ns, kwargs, headers,
+ slice_object=True)
nslcmops.append(nslcmop)
if operation == "terminate":
nslcmop = None
indata["nslcmops_ids"] = nslcmops
self._check_nsi_operation(session, nsir, operation, indata)
- nsilcmop_desc = self._create_nsilcmop(session, nsiInstanceId, operation, indata)
+ nsilcmop_desc = self._create_nsilcmop(session, netsliceInstanceId, operation, indata)
self.format_on_new(nsilcmop_desc, session["project_id"], make_public=session["public"])
_id = self.db.create("nsilcmops", nsilcmop_desc)
rollback.append({"topic": "nsilcmops", "_id": _id})
self.msg.write("nsi", operation, nsilcmop_desc)
- return _id
+ return _id, None
except ValidationError as e:
raise EngineException(e, HTTPStatus.UNPROCESSABLE_ENTITY)