ns_terminate,
ns_action,
ns_scale,
+ ns_update,
nsi_instantiate,
+ ns_migrate,
)
from osm_nbi.base_topic import (
BaseTopic,
)
if vnfd_id not in needed_vnfds:
vnfd = self._get_vnfd_from_db(vnfd_id, session)
+ if "revision" in vnfd["_admin"]:
+ vnfd["revision"] = vnfd["_admin"]["revision"]
+ vnfd.pop("_admin")
needed_vnfds[vnfd_id] = vnfd
nsr_descriptor["vnfd-id"].append(vnfd["_id"])
else:
_filter = self._get_project_filter(session)
_filter["id"] = vnfd_id
vnfd = self.db.get_one("vnfds", _filter, fail_on_empty=True, fail_on_more=True)
- vnfd.pop("_admin")
return vnfd
def _add_nsr_to_db(self, nsr_descriptor, rollback, session):
)
vnfd = self._get_vnfd_from_db(vnf_profile.get("vnfd-id"), session)
+ vnfd.pop("_admin")
for vdu in vnfd.get("vdu", ()):
flavor_data = {}
# Add Affinity or Anti-affinity group information to NSR
vdu_profiles = vnfd.get("df", [[]])[0].get("vdu-profile", ())
- ag_prefix_name = "{}-{}".format(nsr_descriptor["name"][:16], vnf_profile.get("id")[:16])
+ affinity_group_prefix_name = "{}-{}".format(
+ nsr_descriptor["name"][:16], vnf_profile.get("id")[:16]
+ )
for vdu_profile in vdu_profiles:
- ag_data = {}
- for ag in vdu_profile.get("affinity-or-anti-affinity-group", ()):
- ag_data = self._get_affinity_or_anti_affinity_group_data_from_vnfd(vnfd, ag["id"])
- self._add_affinity_or_anti_affinity_group_to_nsr(nsr_descriptor, ag_data, ag_prefix_name)
+ affinity_group_data = {}
+ for affinity_group in vdu_profile.get(
+ "affinity-or-anti-affinity-group", ()
+ ):
+ affinity_group_data = (
+ self._get_affinity_or_anti_affinity_group_data_from_vnfd(
+ vnfd, affinity_group["id"]
+ )
+ )
+ affinity_group_data["member-vnf-index"] = vnf_profile.get("id")
+ self._add_affinity_or_anti_affinity_group_to_nsr(
+ nsr_descriptor,
+ affinity_group_data,
+ affinity_group_prefix_name,
+ )
for vld in nsr_vld:
vld["vnfd-connection-point-ref"] = all_vld_connection_point_data.get(
return nsr_descriptor
- def _get_affinity_or_anti_affinity_group_data_from_vnfd(self, vnfd, ag_id):
+ def _get_affinity_or_anti_affinity_group_data_from_vnfd(
+ self, vnfd, affinity_group_id
+ ):
"""
Gets affinity-or-anti-affinity-group info from df and returns the desired affinity group
"""
- affinity_or_anti_affinity_group = utils.find_in_list(
- vnfd.get("df", [[]])[0].get("affinity-or-anti-affinity-group", ()), lambda ag: ag["id"] == ag_id
+ affinity_group = utils.find_in_list(
+ vnfd.get("df", [[]])[0].get("affinity-or-anti-affinity-group", ()),
+ lambda ag: ag["id"] == affinity_group_id,
)
- ag_data = {}
- if affinity_or_anti_affinity_group and affinity_or_anti_affinity_group.get("id"):
- ag_data["ag-id"] = affinity_or_anti_affinity_group["id"]
- if affinity_or_anti_affinity_group and affinity_or_anti_affinity_group.get("type"):
- ag_data["type"] = affinity_or_anti_affinity_group["type"]
- if affinity_or_anti_affinity_group and affinity_or_anti_affinity_group.get("scope"):
- ag_data["scope"] = affinity_or_anti_affinity_group["scope"]
- return ag_data
-
- def _add_affinity_or_anti_affinity_group_to_nsr(self, nsr_descriptor, ag_data, ag_prefix_name):
+ affinity_group_data = {}
+ if affinity_group:
+ if affinity_group.get("id"):
+ affinity_group_data["ag-id"] = affinity_group["id"]
+ if affinity_group.get("type"):
+ affinity_group_data["type"] = affinity_group["type"]
+ if affinity_group.get("scope"):
+ affinity_group_data["scope"] = affinity_group["scope"]
+ return affinity_group_data
+
+ def _add_affinity_or_anti_affinity_group_to_nsr(
+ self, nsr_descriptor, affinity_group_data, affinity_group_prefix_name
+ ):
"""
Adds affinity-or-anti-affinity-group to nsr checking first it is not already added
"""
- ag = next(
+ affinity_group = next(
(
f
for f in nsr_descriptor["affinity-or-anti-affinity-group"]
- if all(f.get(k) == ag_data[k] for k in ag_data)
+ if all(f.get(k) == affinity_group_data[k] for k in affinity_group_data)
),
None,
)
- if not ag:
- ag_data["id"] = str(len(nsr_descriptor["affinity-or-anti-affinity-group"]))
- ag_data["name"] = "{}-{}-{}".format(ag_prefix_name, ag_data["ag-id"][:32], ag_data.get("id") or 0)
- nsr_descriptor["affinity-or-anti-affinity-group"].append(ag_data)
+ if not affinity_group:
+ affinity_group_data["id"] = str(
+ len(nsr_descriptor["affinity-or-anti-affinity-group"])
+ )
+ affinity_group_data["name"] = "{}-{}".format(
+ affinity_group_prefix_name, affinity_group_data["ag-id"][:32]
+ )
+ nsr_descriptor["affinity-or-anti-affinity-group"].append(
+ affinity_group_data
+ )
def _get_image_data_from_vnfd(self, vnfd, sw_image_id):
sw_image_desc = utils.find_in_list(
"connection-point": [],
"ip-address": None, # mgmt-interface filled by LCM
}
+
+ # Revision backwards compatility. Only specify the revision in the record if
+ # the original VNFD has a revision.
+ if "revision" in vnfd:
+ vnfr_descriptor["revision"] = vnfd["revision"]
+
+
vnf_k8s_namespace = ns_k8s_namespace
if vnf_params:
if vnf_params.get("k8s-namespace"):
# Adding Affinity groups information to vdur
try:
- ags_vdu_profile = utils.find_in_list(
+ vdu_profile_affinity_group = utils.find_in_list(
vnfd.get("df")[0]["vdu-profile"],
lambda a_vdu: a_vdu["id"] == vdu["id"],
)
except Exception:
- ags_vdu_profile = None
-
- if ags_vdu_profile:
- ags_ids = []
- for ag in ags_vdu_profile.get("affinity-or-anti-affinity-group", ()):
- vdu_ag = utils.find_in_list(
- ags_vdu_profile.get("affinity-or-anti-affinity-group", ()),
- lambda ag_fp: ag_fp["id"] == ag["id"],
+ vdu_profile_affinity_group = None
+
+ if vdu_profile_affinity_group:
+ affinity_group_ids = []
+ for affinity_group in vdu_profile_affinity_group.get(
+ "affinity-or-anti-affinity-group", ()
+ ):
+ vdu_affinity_group = utils.find_in_list(
+ vdu_profile_affinity_group.get(
+ "affinity-or-anti-affinity-group", ()
+ ),
+ lambda ag_fp: ag_fp["id"] == affinity_group["id"],
)
- nsr_ags_data = utils.find_in_list(
+ nsr_affinity_group = utils.find_in_list(
nsr_descriptor["affinity-or-anti-affinity-group"],
lambda nsr_ag: (
- nsr_ag.get("ag-id") == vdu_ag.get("id")
+ nsr_ag.get("ag-id") == vdu_affinity_group.get("id")
+ and nsr_ag.get("member-vnf-index")
+ == vnfr_descriptor.get("member-vnf-index-ref")
),
)
- ags_ids.append(nsr_ags_data["id"])
- vdur["affinity-or-anti-affinity-group-id"] = ags_ids
+ # Update Affinity Group VIM name if VDU instantiation parameter is present
+ if vnf_params and vnf_params.get("affinity-or-anti-affinity-group"):
+ vnf_params_affinity_group = utils.find_in_list(
+ vnf_params["affinity-or-anti-affinity-group"],
+ lambda vnfp_ag: (
+ vnfp_ag.get("id") == vdu_affinity_group.get("id")
+ ),
+ )
+ if vnf_params_affinity_group.get("vim-affinity-group-id"):
+ nsr_affinity_group[
+ "vim-affinity-group-id"
+ ] = vnf_params_affinity_group["vim-affinity-group-id"]
+ affinity_group_ids.append(nsr_affinity_group["id"])
+ vdur["affinity-or-anti-affinity-group-id"] = affinity_group_ids
if vdu_instantiation_level:
count = vdu_instantiation_level.get("number-of-instances")
operation_schema = { # mapping between operation and jsonschema to validate
"instantiate": ns_instantiate,
"action": ns_action,
+ "update": ns_update,
"scale": ns_scale,
"terminate": ns_terminate,
+ "migrate": ns_migrate,
}
def __init__(self, db, fs, msg, auth):
"""
Check that user has enter right parameters for the operation
:param session: contains "username", "admin", "force", "public", "project_id", "set_project"
- :param operation: it can be: instantiate, terminate, action, TODO: update, heal
+ :param operation: it can be: instantiate, terminate, action, update. TODO: heal
:param indata: descriptor with the parameters of the operation
:return: None
"""
self._check_action_ns_operation(indata, nsr)
elif operation == "scale":
self._check_scale_ns_operation(indata, nsr)
+ elif operation == "update":
+ self._check_update_ns_operation(indata, nsr)
elif operation == "instantiate":
self._check_instantiate_ns_operation(indata, nsr, session)
)
)
+ def _check_update_ns_operation(self, indata, nsr) -> None:
+ """Validates the ns-update request according to updateType
+
+ If updateType is CHANGE_VNFPKG:
+ - it checks the vnfInstanceId, whether it's available under ns instance
+ - it checks the vnfdId whether it matches with the vnfd-id in the vnf-record of specified VNF.
+ Otherwise exception will be raised.
+ If updateType is REMOVE_VNF:
+ - it checks if the vnfInstanceId is available in the ns instance
+ - Otherwise exception will be raised.
+
+ Args:
+ indata: includes updateType such as CHANGE_VNFPKG,
+ nsr: network service record
+
+ Raises:
+ EngineException:
+ a meaningful error if given update parameters are not proper such as
+ "Error in validating ns-update request: <ID> does not match
+ with the vnfd-id of vnfinstance
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY"
+
+ """
+ try:
+ if indata["updateType"] == "CHANGE_VNFPKG":
+ # vnfInstanceId, nsInstanceId, vnfdId are mandatory
+ vnf_instance_id = indata["changeVnfPackageData"]["vnfInstanceId"]
+ ns_instance_id = indata["nsInstanceId"]
+ vnfd_id_2update = indata["changeVnfPackageData"]["vnfdId"]
+
+ if vnf_instance_id not in nsr["constituent-vnfr-ref"]:
+
+ raise EngineException(
+ f"Error in validating ns-update request: vnf {vnf_instance_id} does not "
+ f"belong to NS {ns_instance_id}",
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ # Getting vnfrs through the ns_instance_id
+ vnfrs = self.db.get_list("vnfrs", {"nsr-id-ref": ns_instance_id})
+ constituent_vnfd_id = next(
+ (
+ vnfr["vnfd-id"]
+ for vnfr in vnfrs
+ if vnfr["id"] == vnf_instance_id
+ ),
+ None,
+ )
+
+ # Check the given vnfd-id belongs to given vnf instance
+ if constituent_vnfd_id and (vnfd_id_2update != constituent_vnfd_id):
+
+ raise EngineException(
+ f"Error in validating ns-update request: vnfd-id {vnfd_id_2update} does not "
+ f"match with the vnfd-id: {constituent_vnfd_id} of VNF instance: {vnf_instance_id}",
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ # Validating the ns update timeout
+ if (
+ indata.get("timeout_ns_update")
+ and indata["timeout_ns_update"] < 300
+ ):
+ raise EngineException(
+ "Error in validating ns-update request: {} second is not enough "
+ "to upgrade the VNF instance: {}".format(
+ indata["timeout_ns_update"], vnf_instance_id
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+ elif indata["updateType"] == "REMOVE_VNF":
+ vnf_instance_id = indata["removeVnfInstanceId"]
+ ns_instance_id = indata["nsInstanceId"]
+ if vnf_instance_id not in nsr["constituent-vnfr-ref"]:
+ raise EngineException(
+ "Invalid VNF Instance Id. '{}' is not "
+ "present in the NS '{}'".format(vnf_instance_id, ns_instance_id)
+ )
+
+ except (
+ DbException,
+ AttributeError,
+ IndexError,
+ KeyError,
+ ValueError,
+ ) as e:
+ raise type(e)(
+ "Ns update request could not be processed with error: {}.".format(e)
+ )
+
def _check_scale_ns_operation(self, indata, nsr):
vnfd = self._get_vnfd_from_vnf_member_index(
indata["scaleVnfData"]["scaleByStepData"]["member-vnf-index"], nsr["_id"]
"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)
+
+ ## Backwards compatibility: if there is no revision, get it from the one and only VNFD entry
+ if "revision" in vnfr:
+ vnfd_revision = vnfr["vnfd-id"] + ":" + str(vnfr["revision"])
+ vnfd = self.db.get_one("vnfds_revisions", {"_id": vnfd_revision}, fail_on_empty=False)
+ else:
+ 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(
"""
Creates a ns-lcm-opp content to be stored at database.
:param nsr_id: internal id of the instance
- :param operation: instantiate, terminate, scale, action, ...
+ :param operation: instantiate, terminate, scale, action, update ...
:param params: user parameters for the operation
:return: dictionary following SOL005 format
"""
: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
nsInstanceId: _id of the nsr to perform the operation
- operation: it can be: instantiate, terminate, action, TODO: update, heal
+ operation: it can be: instantiate, terminate, action, update TODO: heal
:param kwargs: used to override the indata descriptor
:param headers: http request headers
:return: id of the nslcmops