X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FNBI.git;a=blobdiff_plain;f=osm_nbi%2Finstance_topics.py;h=2df6a7d15b93bf78a26fd84f74c0672b93e1a440;hp=b4ce2706a6abbc81e00fad9fc9997c73fdc0d14f;hb=6380e7cbe3ffd525453b5d8ef54c18a9cc14fa5d;hpb=cee2ebfe3b4a0e4fe7a566eeb6e3f959649e1fce diff --git a/osm_nbi/instance_topics.py b/osm_nbi/instance_topics.py index b4ce270..2df6a7d 100644 --- a/osm_nbi/instance_topics.py +++ b/osm_nbi/instance_topics.py @@ -26,7 +26,9 @@ from osm_nbi.validation import ( ns_terminate, ns_action, ns_scale, + ns_update, nsi_instantiate, + ns_migrate, ) from osm_nbi.base_topic import ( BaseTopic, @@ -597,13 +599,26 @@ class NsrTopic(BaseTopic): # 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( @@ -614,38 +629,50 @@ class NsrTopic(BaseTopic): 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( @@ -997,28 +1024,46 @@ class NsrTopic(BaseTopic): # 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") @@ -1111,8 +1156,10 @@ class NsLcmOpTopic(BaseTopic): 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): @@ -1122,7 +1169,7 @@ class NsLcmOpTopic(BaseTopic): """ 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 """ @@ -1130,6 +1177,8 @@ class NsLcmOpTopic(BaseTopic): 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) @@ -1222,6 +1271,96 @@ class NsLcmOpTopic(BaseTopic): ) ) + 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: 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"] @@ -1958,7 +2097,7 @@ class NsLcmOpTopic(BaseTopic): """ 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 """ @@ -2014,7 +2153,7 @@ class NsLcmOpTopic(BaseTopic): :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