From ca7ece02d870a16bdcd47dab7c5ac30c82c58545 Mon Sep 17 00:00:00 2001 From: elumalai Date: Tue, 12 Apr 2022 12:47:32 +0530 Subject: [PATCH] Feature 10916 Remove VNF Instance from NS - NS Update Added feature support to remove a VNF using NS Update Added unit test cases Change-Id: Ib8f8309544502fd8e580b8e62bcabb7dee64fddc Signed-off-by: elumalai --- osm_lcm/lcm.py | 2 + osm_lcm/ns.py | 132 +++++++++++++++++++++++++-- osm_lcm/tests/test_db_descriptors.py | 54 +++++++++++ osm_lcm/tests/test_ns.py | 25 +++++ 4 files changed, 204 insertions(+), 9 deletions(-) diff --git a/osm_lcm/lcm.py b/osm_lcm/lcm.py index 731488c..82947e9 100644 --- a/osm_lcm/lcm.py +++ b/osm_lcm/lcm.py @@ -485,6 +485,7 @@ class Lcm: elif command == "deleted": return # TODO cleaning of task just in case should be done elif command in ( + "vnf_terminated", "terminated", "instantiated", "scaled", @@ -493,6 +494,7 @@ class Lcm: "migrated", ): # "scaled-cooldown-time" return + elif topic == "nsi": # netslice LCM processes (instantiate, terminate, etc) if command == "instantiate": # self.logger.debug("Instantiating Network Slice {}".format(nsilcmop["netsliceInstanceId"])) diff --git a/osm_lcm/ns.py b/osm_lcm/ns.py index 5c2d4c3..19b405b 100644 --- a/osm_lcm/ns.py +++ b/osm_lcm/ns.py @@ -5299,6 +5299,105 @@ class NsLcm(LcmBase): self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_action") return nslcmop_operation_state, detailed_status + async def terminate_vdus( + self, db_vnfr, member_vnf_index, db_nsr, update_db_nslcmops, stage, logging_text + ): + """This method terminates VDUs + + Args: + db_vnfr: VNF instance record + member_vnf_index: VNF index to identify the VDUs to be removed + db_nsr: NS instance record + update_db_nslcmops: Nslcmop update record + """ + vca_scaling_info = [] + scaling_info = {"scaling_group_name": "vdu_autoscale", "vdu": [], "kdu": []} + scaling_info["scaling_direction"] = "IN" + scaling_info["vdu-delete"] = {} + scaling_info["kdu-delete"] = {} + db_vdur = db_vnfr.get("vdur") + vdur_list = copy(db_vdur) + count_index = 0 + for index, vdu in enumerate(vdur_list): + vca_scaling_info.append( + { + "osm_vdu_id": vdu["vdu-id-ref"], + "member-vnf-index": member_vnf_index, + "type": "delete", + "vdu_index": count_index, + }) + scaling_info["vdu-delete"][vdu["vdu-id-ref"]] = count_index + scaling_info["vdu"].append( + { + "name": vdu.get("name") or vdu.get("vdu-name"), + "vdu_id": vdu["vdu-id-ref"], + "interface": [], + }) + for interface in vdu["interfaces"]: + scaling_info["vdu"][index]["interface"].append( + { + "name": interface["name"], + "ip_address": interface["ip-address"], + "mac_address": interface.get("mac-address"), + }) + self.logger.info("NS update scaling info{}".format(scaling_info)) + stage[2] = "Terminating VDUs" + if scaling_info.get("vdu-delete"): + # scale_process = "RO" + if self.ro_config.get("ng"): + await self._scale_ng_ro( + logging_text, db_nsr, update_db_nslcmops, db_vnfr, scaling_info, stage + ) + + async def remove_vnf( + self, nsr_id, nslcmop_id, vnf_instance_id + ): + """This method is to Remove VNF instances from NS. + + Args: + nsr_id: NS instance id + nslcmop_id: nslcmop id of update + vnf_instance_id: id of the VNF instance to be removed + + Returns: + result: (str, str) COMPLETED/FAILED, details + """ + try: + db_nsr_update = {} + logging_text = "Task ns={} update ".format(nsr_id) + check_vnfr_count = len(self.db.get_list("vnfrs", {"nsr-id-ref": nsr_id})) + self.logger.info("check_vnfr_count {}".format(check_vnfr_count)) + if check_vnfr_count > 1: + stage = ["", "", ""] + step = "Getting nslcmop from database" + self.logger.debug(step + " after having waited for previous tasks to be completed") + # db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id}) + db_nsr = self.db.get_one("nsrs", {"_id": nsr_id}) + db_vnfr = self.db.get_one("vnfrs", {"_id": vnf_instance_id}) + member_vnf_index = db_vnfr["member-vnf-index-ref"] + """ db_vnfr = self.db.get_one( + "vnfrs", {"member-vnf-index-ref": member_vnf_index, "nsr-id-ref": nsr_id}) """ + + update_db_nslcmops = self.db.get_one("nslcmops", {"_id": nslcmop_id}) + await self.terminate_vdus(db_vnfr, member_vnf_index, db_nsr, update_db_nslcmops, stage, logging_text) + + constituent_vnfr = db_nsr.get("constituent-vnfr-ref") + constituent_vnfr.remove(db_vnfr.get("_id")) + db_nsr_update["constituent-vnfr-ref"] = db_nsr.get("constituent-vnfr-ref") + self.update_db_2("nsrs", nsr_id, db_nsr_update) + self.db.del_one("vnfrs", {"_id": db_vnfr.get("_id")}) + self.update_db_2("nsrs", nsr_id, db_nsr_update) + return "COMPLETED", "Done" + else: + step = "Terminate VNF Failed with" + raise LcmException("{} Cannot terminate the last VNF in this NS.".format( + vnf_instance_id)) + except (LcmException, asyncio.CancelledError): + raise + except Exception as e: + self.logger.debug("Error removing VNF {}".format(e)) + return "FAILED", "Error removing VNF {}".format(e) + async def _ns_charm_upgrade( self, ee_id, @@ -5371,7 +5470,7 @@ class NsLcm(LcmBase): db_nsr_update = {} error_description_nslcmop = "" exc = None - change_type = "" + change_type = "updated" detailed_status = "" try: @@ -5597,7 +5696,24 @@ class NsLcm(LcmBase): ) elif update_type == "REMOVE_VNF": # This part is included in https://osm.etsi.org/gerrit/11876 - pass + vnf_instance_id = db_nslcmop["operationParams"]["removeVnfInstanceId"] + db_vnfr = self.db.get_one("vnfrs", {"_id": vnf_instance_id}) + member_vnf_index = db_vnfr["member-vnf-index-ref"] + step = "Removing VNF" + (result, detailed_status) = await self.remove_vnf(nsr_id, nslcmop_id, vnf_instance_id) + if result == "FAILED": + nslcmop_operation_state = result + error_description_nslcmop = detailed_status + db_nslcmop_update["detailed-status"] = detailed_status + change_type = "vnf_terminated" + if not nslcmop_operation_state: + nslcmop_operation_state = "COMPLETED" + self.logger.debug( + logging_text + + " task Done with result {} {}".format( + nslcmop_operation_state, detailed_status + ) + ) # If nslcmop_operation_state is None, so any operation is not failed. # All operations are executed in overall. @@ -5650,16 +5766,14 @@ class NsLcm(LcmBase): if nslcmop_operation_state: try: - await self.msg.aiowrite( - "ns", - "updated", - { + msg = { "nsr_id": nsr_id, "nslcmop_id": nslcmop_id, "operationState": nslcmop_operation_state, - }, - loop=self.loop, - ) + } + if change_type in ("vnf_terminated"): + msg.update({"vnf_member_index": member_vnf_index}) + await self.msg.aiowrite("ns", change_type, msg, loop=self.loop) except Exception as e: self.logger.error( logging_text + "kafka_write notification Exception {}".format(e) diff --git a/osm_lcm/tests/test_db_descriptors.py b/osm_lcm/tests/test_db_descriptors.py index 457ea30..2e001c0 100644 --- a/osm_lcm/tests/test_db_descriptors.py +++ b/osm_lcm/tests/test_db_descriptors.py @@ -316,6 +316,39 @@ db_nslcmops_text = """ operationState: COMPLETED startTime: 1575034637.0445576 statusEnteredTime: 1575034663.8484545 + +- _admin: + created: 1566823354.4148262 + modified: 1566823354.4148262 + projects_read: + - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4 + projects_write: + - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4 + worker: 86434c2948e2 + operations: + - member_vnf_index: '1' + primitive: touch + primitive_params: /home/ubuntu/last-touch-1 + operationState: COMPLETED + detailed-status: Done + _id: a639fac7-e0bb-4225-8ecb-c1f8efcc125f + detailed-status: done + id: a639fac7-e0bb-4225-8ecb-c1f8efcc125f + isAutomaticInvocation: false + isCancelPending: false + lcmOperationType: update + links: + nsInstance: /osm/nslcm/v1/ns_instances/f48163a6-c807-47bc-9682-f72caef5af85 + self: /osm/nslcm/v1/ns_lcm_op_occs/a639fac7-e0bb-4225-8ecb-c1f8efcc125f + nsInstanceId: f48163a6-c807-47bc-9682-f72caef5af85 + operationParams: + lcmOperationType: update + nsInstanceId: f48163a6-c807-47bc-9682-f72caef5af85 + removeVnfInstanceId: 88d90b0c-faff-4b9f-bccd-017f33985984 + updateType: REMOVE_VNF + operationState: FAILED + startTime: 1566823354.414689 + statusEnteredTime: 1566824534.5112448 """ db_nsrs_text = """ @@ -2340,6 +2373,22 @@ db_nslcmops_scale_text = """ detailed-status: done """ +ro_update_action_text = """ +action_id: e62fc036-6e6f-4a6f-885e-bc12e2fbe75d +details: progress 1/1 +nsr_id: 31dbfa80-80a8-4f2a-a557-626904df3402 +status: DONE +tasks: +- action: DELETE + action_id: e62fc036-6e6f-4a6f-885e-bc12e2fbe75d + item: vdu + nsr_id: 31dbfa80-80a8-4f2a-a557-626904df3402 + status: FINISHED + target_record: vnfrs:5bbe7015-ae98-4e09-9316-76f3bf218353:vdur.0.vim_info.vim:2a3dc443-415b-4865-8420-f804b993c5a3 + target_record_id: vnfrs:5bbe7015-ae98-4e09-9316-76f3bf218353:vdur.e03e2281-c70e-44ef-ac3b-052b81efd31d + task_id: e62fc036-6e6f-4a6f-885e-bc12e2fbe75d:0 +""" + test_ids = { # contains the ids of ns and operations of every test "TEST-A": { @@ -2363,4 +2412,9 @@ test_ids = { "instantiate": "4013bbd2-b151-40ee-bcef-7e24ce5432f6", "terminate": None, }, + "TEST-UPDATE": { + "ns": "f48163a6-c807-47bc-9682-f72caef5af85", + "vnf": "88d90b0c-faff-4b9f-bccd-017f33985984", + "removeVnf": "a639fac7-e0bb-4225-8ecb-c1f8efcc125f", + }, } diff --git a/osm_lcm/tests/test_ns.py b/osm_lcm/tests/test_ns.py index 86e2136..7613a88 100644 --- a/osm_lcm/tests/test_ns.py +++ b/osm_lcm/tests/test_ns.py @@ -120,6 +120,13 @@ class TestMyNS(asynctest.TestCase): def _ro_status(self, *args, **kwargs): print("Args > {}".format(args)) print("kwargs > {}".format(kwargs)) + if args: + if "update" in args: + ro_ns_desc = yaml.load( + descriptors.ro_update_action_text, Loader=yaml.Loader + ) + while True: + yield ro_ns_desc if kwargs.get("delete"): ro_ns_desc = yaml.load( descriptors.ro_delete_action_text, Loader=yaml.Loader @@ -783,6 +790,24 @@ class TestMyNS(asynctest.TestCase): or expected_kdu_model in nsr_kdu_model_result ) + # Test remove_vnf() and related methods + @asynctest.fail_on(active_handles=True) # all async tasks must be completed + async def test_remove_vnf(self): + # Test REMOVE_VNF + nsr_id = descriptors.test_ids["TEST-UPDATE"]["ns"] + nslcmop_id = descriptors.test_ids["TEST-UPDATE"]["removeVnf"] + vnf_instance_id = descriptors.test_ids["TEST-UPDATE"]["vnf"] + self.my_ns.RO.status = asynctest.CoroutineMock(self.my_ns.RO.status, side_effect=self._ro_status("update")) + await self.my_ns.update(nsr_id, nslcmop_id) + expected_value = "COMPLETED" + return_value = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + self.assertEqual(return_value, expected_value) + with self.assertRaises(Exception) as context: + self.db.get_one("vnfrs", {"_id": vnf_instance_id}) + self.assertTrue("database exception Not found entry with filter" in str(context.exception)) + # async def test_instantiate_pdu(self): # nsr_id = descriptors.test_ids["TEST-A"]["ns"] # nslcmop_id = descriptors.test_ids["TEST-A"]["instantiate"] -- 2.17.1