From b827de931cbe2452bcca76b8b24371b4a8afe2aa Mon Sep 17 00:00:00 2001 From: "k4.rahul" Date: Mon, 2 May 2022 16:35:02 +0000 Subject: [PATCH] Feature 10922: Stop, start and rebuild Added support for feature start stop rebuild of VNF instances Added unit test case for feature start stop rebuild of VNF instances Change-Id: I4a56fc116c998d0698be7e97118ce85207814b4b Signed-off-by: k4.rahul --- osm_lcm/ng_ro.py | 36 +++++++ osm_lcm/ns.py | 95 +++++++++++++++++- osm_lcm/tests/test_db_descriptors.py | 138 +++++++++++++++++++++++++++ osm_lcm/tests/test_ns.py | 32 +++++++ 4 files changed, 300 insertions(+), 1 deletion(-) diff --git a/osm_lcm/ng_ro.py b/osm_lcm/ng_ro.py index 0acccc4..5dd3cc4 100644 --- a/osm_lcm/ng_ro.py +++ b/osm_lcm/ng_ro.py @@ -156,6 +156,42 @@ class NgRoClient: except asyncio.TimeoutError: raise NgRoException("Timeout", http_code=504) + async def operate(self, nsr_id, target, operation_type): + """ + Performs start/stop/rebuil of VNFs + :param nsr_id: NS Instance Id + :param target: payload data for migrate operation + :param operation_type: start/stop/rebuil of VNFs + :return: dictionary with the information or raises NgRoException on Error + """ + try: + if isinstance(target, str): + target = self._parse_yaml(target) + payload_req = yaml.safe_dump(target) + + url = "{}/ns/v1/{operation_type}/{nsr_id}".format( + self.endpoint_url, operation_type=operation_type, nsr_id=nsr_id + ) + async with aiohttp.ClientSession(loop=self.loop) as session: + self.logger.debug("NG-RO POST %s %s", url, payload_req) + # timeout = aiohttp.ClientTimeout(total=self.timeout_large) + async with session.post( + url, headers=self.headers_req, data=payload_req + ) as response: + response_text = await response.read() + self.logger.debug( + "POST {} [{}] {}".format( + url, response.status, response_text[:100] + ) + ) + if response.status >= 300: + raise NgRoException(response_text, http_code=response.status) + return self._parse_yaml(response_text, response=True) + except (aiohttp.ClientOSError, aiohttp.ClientError) as e: + raise NgRoException(e, http_code=504) + except asyncio.TimeoutError: + raise NgRoException("Timeout", http_code=504) + async def status(self, nsr_id, action_id): try: url = "{}/ns/v1/deploy/{nsr_id}/{action_id}".format( diff --git a/osm_lcm/ns.py b/osm_lcm/ns.py index 5345d78..2e9c1bc 100644 --- a/osm_lcm/ns.py +++ b/osm_lcm/ns.py @@ -133,7 +133,7 @@ class NsLcm(LcmBase): 10 * 60 ) # timeout for some progress in a primitive execution timeout_migrate = 1800 # default global timeout for migrating vnfs - + timeout_operate = 1800 # default global timeout for migrating vnfs SUBOPERATION_STATUS_NOT_FOUND = -1 SUBOPERATION_STATUS_NEW = -2 SUBOPERATION_STATUS_SKIP = -3 @@ -5913,6 +5913,26 @@ class NsLcm(LcmBase): ) ) + elif update_type == "OPERATE_VNF": + vnf_id = db_nslcmop["operationParams"]["operateVnfData"]["vnfInstanceId"] + operation_type = db_nslcmop["operationParams"]["operateVnfData"]["changeStateTo"] + additional_param = db_nslcmop["operationParams"]["operateVnfData"]["additionalParam"] + (result, detailed_status) = await self.rebuild_start_stop( + nsr_id, nslcmop_id, vnf_id, additional_param, operation_type + ) + if result == "FAILED": + nslcmop_operation_state = result + error_description_nslcmop = detailed_status + db_nslcmop_update["detailed-status"] = detailed_status + 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. if not nslcmop_operation_state: @@ -7202,6 +7222,79 @@ class NsLcm(LcmBase): job["vnfr_id"] = vnfr_id return job_list + async def rebuild_start_stop(self, nsr_id, nslcmop_id, vnf_id, additional_param, operation_type): + logging_text = "Task ns={} {}={} ".format(nsr_id, operation_type, nslcmop_id) + self.logger.info(logging_text + "Enter") + stage = ["Preparing the environment", ""] + # database nsrs record + db_nsr_update = {} + vdu_vim_name = None + vim_vm_id = None + # in case of error, indicates what part of scale was failed to put nsr at error status + start_deploy = time() + try: + db_vnfr = self.db.get_one("vnfrs", {"_id": vnf_id}) + vim_account_id = db_vnfr.get("vim-account-id") + vim_info_key = "vim:" + vim_account_id + vdur = find_in_list( + db_vnfr["vdur"], lambda vdu: vdu["count-index"] == additional_param["count-index"] + ) + if vdur: + vdu_vim_name = vdur["name"] + vim_vm_id = vdur["vim_info"][vim_info_key]["vim_id"] + target_vim, _ = next(k_v for k_v in vdur["vim_info"].items()) + self.logger.info("vdu_vim_name >> {} ".format(vdu_vim_name)) + # wait for any previous tasks in process + stage[1] = "Waiting for previous operations to terminate" + self.logger.info(stage[1]) + await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id) + + stage[1] = "Reading from database." + self.logger.info(stage[1]) + self._write_ns_status( + nsr_id=nsr_id, + ns_state=None, + current_operation=operation_type.upper(), + current_operation_id=nslcmop_id + ) + self._write_op_status(op_id=nslcmop_id, stage=stage, queuePosition=0) + + # read from db: ns + stage[1] = "Getting nsr={} from db.".format(nsr_id) + db_nsr_update["operational-status"] = operation_type + self.update_db_2("nsrs", nsr_id, db_nsr_update) + # Payload for RO + desc = { + operation_type: { + "vim_vm_id": vim_vm_id, + "vnf_id": vnf_id, + "vdu_index": additional_param["count-index"], + "vdu_id": vdur["id"], + "target_vim": target_vim, + "vim_account_id": vim_account_id + } + } + stage[1] = "Sending rebuild request to RO... {}".format(desc) + self._write_op_status(op_id=nslcmop_id, stage=stage, queuePosition=0) + self.logger.info("ro nsr id: {}".format(nsr_id)) + result_dict = await self.RO.operate(nsr_id, desc, operation_type) + self.logger.info("response from RO: {}".format(result_dict)) + action_id = result_dict["action_id"] + await self._wait_ng_ro( + nsr_id, action_id, nslcmop_id, start_deploy, self.timeout_operate + ) + return "COMPLETED", "Done" + except (ROclient.ROClientException, DbException, LcmException) as e: + self.logger.error("Exit Exception {}".format(e)) + exc = e + except asyncio.CancelledError: + self.logger.error("Cancelled Exception while '{}'".format(stage)) + exc = "Operation was cancelled" + except Exception as e: + exc = traceback.format_exc() + self.logger.critical("Exit Exception {} {}".format(type(e).__name__, e), exc_info=True) + return "FAILED", "Error in operate VNF {}".format(exc) + def get_vca_cloud_and_credentials(self, vim_account_id: str) -> (str, str): """ Get VCA Cloud and VCA Cloud Credentials for the VIM account diff --git a/osm_lcm/tests/test_db_descriptors.py b/osm_lcm/tests/test_db_descriptors.py index 2e001c0..dbb3e1d 100644 --- a/osm_lcm/tests/test_db_descriptors.py +++ b/osm_lcm/tests/test_db_descriptors.py @@ -349,6 +349,81 @@ db_nslcmops_text = """ operationState: FAILED startTime: 1566823354.414689 statusEnteredTime: 1566824534.5112448 + +- _id: 1bd4b60a-e15d-49e5-b75e-2b3224f15dda + id: 1bd4b60a-e15d-49e5-b75e-2b3224f15dda + operationState: COMPLETED + queuePosition: 0 + stage: '' + errorMessage: '' + detailedStatus: + statusEnteredTime: 1652349205.9499352 + nsInstanceId: 52f0b3ac-1574-481f-a48f-528fc02912f7 + lcmOperationType: update + startTime: 1652349205.7415159 + isAutomaticInvocation: false + operationParams: + updateType: OPERATE_VNF + operateVnfData: + vnfInstanceId: a6df8aa0-1271-4dfc-85a5-e0484fea303f + changeStateTo: start + additionalParam: + run-day1: false + vdu-id: mgmtVM + count-index: 0 + lcmOperationType: update + nsInstanceId: 52f0b3ac-1574-481f-a48f-528fc02912f7 + isCancelPending: false + links: + self: "/osm/nslcm/v1/ns_lcm_op_occs/1bd4b60a-e15d-49e5-b75e-2b3224f15dda" + nsInstance: "/osm/nslcm/v1/ns_instances/52f0b3ac-1574-481f-a48f-528fc02912f7" + _admin: + created: 1652349205.7415788 + modified: 1652349205.9499364 + projects_read: + - e38990e1-6724-4292-ab6f-2ecc109f9af4 + projects_write: + - e38990e1-6724-4292-ab6f-2ecc109f9af4 + worker: fbf6b5aa99e2 + detailed-status: Done + +- _id: 6eace44b-2ef4-4de5-b15f-63f2e8898bfb + id: 6eace44b-2ef4-4de5-b15f-63f2e8898bfb + operationState: Error + queuePosition: 0 + stage: '' + errorMessage: '' + detailedStatus: + statusEnteredTime: 1652349205.9499352 + nsInstanceId: 52f0b3ac-1574-481f-a48f-528fc02912f7 + lcmOperationType: update + startTime: 1652349205.7415159 + isAutomaticInvocation: false + operationParams: + updateType: OPERATE_VNF + operateVnfData: + vnfInstanceId: a6df8aa0-1271-4dfc-85a5-e0484fea303f + changeStateTo: stop + additionalParam: + run-day1: false + vdu-id: mgmtVM + count-index: 0 + lcmOperationType: update + nsInstanceId: 52f0b3ac-1574-481f-a48f-528fc02912f7 + isCancelPending: false + links: + self: "/osm/nslcm/v1/ns_lcm_op_occs/1bd4b60a-e15d-49e5-b75e-2b3224f15dda" + nsInstance: "/osm/nslcm/v1/ns_instances/52f0b3ac-1574-481f-a48f-528fc02912f7" + _admin: + created: 1652349205.7415788 + modified: 1652349205.9499364 + projects_read: + - e38990e1-6724-4292-ab6f-2ecc109f9af4 + projects_write: + - e38990e1-6724-4292-ab6f-2ecc109f9af4 + worker: fbf6b5aa99e2 + detailed-status: Done + """ db_nsrs_text = """ @@ -2310,6 +2385,63 @@ db_vnfrs_text = """ vim-account-id: 74337dcb-ef54-41e7-bd2d-8c0d7fcd326f vnfd-id: d96b1cdf-5ad6-49f7-bf65-907ada989293 vnfd-ref: native-kdu_knf + +- _id: a6df8aa0-1271-4dfc-85a5-e0484fea303f + id: a6df8aa0-1271-4dfc-85a5-e0484fea303f + nsr-id-ref: 52f0b3ac-1574-481f-a48f-528fc02912f7 + member-vnf-index-ref: '1' + additionalParamsForVnf: + created-time: 1652105830.965044 + vnfd-ref: ha_proxy_charm-vnf + vnfd-id: 8b42078a-9d42-4def-8b5d-7dd0f041d078 + vim-account-id: dff4014e-bb5e-441a-a28d-6dd5d86c7175 + vca-id: + vdur: + - _id: 392e010d-3a39-4516-acc0-76993c19691f + alt-image-ids: + - '1' + - '2' + - '3' + cloud-init: 8b42078a-9d42-4def-8b5d-7dd0f041d078:file:cloud-config.txt + count-index: 0 + id: 392e010d-3a39-4516-acc0-76993c19691f + internal-connection-point: + - connection-point-id: mgmtVM-eth0-int + id: mgmtVM-eth0-int + name: mgmtVM-eth0-int + - connection-point-id: dataVM-xe0-int + id: dataVM-xe0-int + name: dataVM-xe0-int + ip-address: 10.45.28.134 + ns-flavor-id: '0' + ns-image-id: '0' + ssh-access-required: true + vdu-id-ref: mgmtVM + vdu-name: mgmtVM + vim_info: + vim:05357241-1a01-416f-9e02-af20f65f51cd: + vim_id: 1f8c18e3-b3aa-484c-a211-e88d6654f24a + vim_status: ACTIVE + vim_name: test_ns_ch-1-mgmtVM-0 + status: ACTIVE + vim-id: 1f8c18e3-b3aa-484c-a211-e88d6654f24a + name: test_ns_ch-1-mgmtVM-0 + vim_details: + vim_id: 1f8c18e3-b3aa-484c-a211-e88d6654f24a + vim_status: DONE + vim_message: + ip-address: 10.45.28.134 + _admin: + created: 1652105830.9652078 + modified: 1652105830.9652078 + projects_read: + - e38990e1-6724-4292-ab6f-2ecc109f9af4 + projects_write: + - e38990e1-6724-4292-ab6f-2ecc109f9af4 + nsState: INSTANTIATED + vdu: + status: DONE + vim-id: 1f8c18e3-b3aa-484c-a211-e88d6654f24a """ db_nslcmops_scale_text = """ @@ -2417,4 +2549,10 @@ test_ids = { "vnf": "88d90b0c-faff-4b9f-bccd-017f33985984", "removeVnf": "a639fac7-e0bb-4225-8ecb-c1f8efcc125f", }, + "TEST-OP-VNF": { + "ns": "f48163a6-c807-47bc-9682-f72caef5af85", + "nslcmops": "1bd4b60a-e15d-49e5-b75e-2b3224f15dda", + "nslcmops1": "6eace44b-2ef4-4de5-b15f-63f2e8898bfb", + "vnfrs": "a6df8aa0-1271-4dfc-85a5-e0484fea303f", + }, } diff --git a/osm_lcm/tests/test_ns.py b/osm_lcm/tests/test_ns.py index 5e248a2..466976c 100644 --- a/osm_lcm/tests/test_ns.py +++ b/osm_lcm/tests/test_ns.py @@ -390,6 +390,38 @@ class TestMyNS(asynctest.TestCase): # await self.test_instantiate() # # this will check that the initial-congig-primitive 'not_to_be_called' is not called + @asynctest.fail_on(active_handles=True) + async def test_start_stop_rebuild_pass(self): + nsr_id = descriptors.test_ids["TEST-OP-VNF"]["ns"] + nslcmop_id = descriptors.test_ids["TEST-OP-VNF"]["nslcmops"] + vnf_id = descriptors.test_ids["TEST-OP-VNF"]["vnfrs"] + additional_param = {"count-index": "0"} + operation_type = "start" + await self.my_ns.rebuild_start_stop( + nsr_id, nslcmop_id, vnf_id, additional_param, operation_type + ) + expected_value = "COMPLETED" + return_value = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + self.assertEqual(return_value, expected_value) + + @asynctest.fail_on(active_handles=True) + async def test_start_stop_rebuild_fail(self): + nsr_id = descriptors.test_ids["TEST-OP-VNF"]["ns"] + nslcmop_id = descriptors.test_ids["TEST-OP-VNF"]["nslcmops1"] + vnf_id = descriptors.test_ids["TEST-OP-VNF"]["vnfrs"] + additional_param = {"count-index": "0"} + operation_type = "stop" + await self.my_ns.rebuild_start_stop( + nsr_id, nslcmop_id, vnf_id, additional_param, operation_type + ) + expected_value = "Error" + return_value = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + self.assertEqual(return_value, expected_value) + # Test scale() and related methods @asynctest.fail_on(active_handles=True) # all async tasks must be completed async def test_scale(self): -- 2.17.1