Feature 10911-Vertical scaling of VM instances from OSM 84/11984/12
authorgovindarajul <Jayaprakash.g@tataelxsi.co.in>
Mon, 2 May 2022 14:32:41 +0000 (20:02 +0530)
committergarciadeblas <gerardo.garciadeblas@telefonica.com>
Wed, 22 Jun 2022 13:41:29 +0000 (15:41 +0200)
Added implementation code for Vertical Scaling of VDUs in OSM LCM

Review comments addressed
Change-Id: If36df196ecbadc6f1f348bd9d43f3b7cf0365602
Signed-off-by: govindarajul <Jayaprakash.g@tataelxsi.co.in>
osm_lcm/lcm.py
osm_lcm/ng_ro.py
osm_lcm/ns.py
osm_lcm/tests/test_db_descriptors.py
osm_lcm/tests/test_ns.py

index 9378b1d..d6c10e8 100644 (file)
@@ -472,6 +472,15 @@ class Lcm:
                 task = asyncio.ensure_future(self.ns.migrate(nsr_id, nslcmop_id))
                 self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "ns_migrate", task)
                 return
+            elif command == "verticalscale":
+                nslcmop = params
+                nslcmop_id = nslcmop["_id"]
+                nsr_id = nslcmop["nsInstanceId"]
+                task = asyncio.ensure_future(self.ns.vertical_scale(nsr_id, nslcmop_id))
+                self.logger.debug("nsr_id,nslcmop_id,task {},{},{}".format(nsr_id, nslcmop_id, task))
+                self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "ns_verticalscale", task)
+                self.logger.debug("LCM task registered {},{},{} ".format(nsr_id, nslcmop_id, task))
+                return
             elif command == "show":
                 nsr_id = params
                 try:
@@ -504,6 +513,7 @@ class Lcm:
                 "actioned",
                 "updated",
                 "migrated",
+                "verticalscaled",
             ):  # "scaled-cooldown-time"
                 return
 
index 5dd3cc4..03819c8 100644 (file)
@@ -335,6 +335,38 @@ class NgRoClient:
         except asyncio.TimeoutError:
             raise NgRoException("Timeout", http_code=504)
 
+    async def vertical_scale(self, nsr_id, target):
+        """
+        Performs migration of VNFs
+        :param nsr_id: NS Instance Id
+        :param target: payload data for migrate operation
+        :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/verticalscale/{nsr_id}".format(self.endpoint_url, nsr_id=nsr_id)
+            async with aiohttp.ClientSession(loop=self.loop) as session:
+                self.logger.debug("NG-RO POST %s %s", url, payload_req)
+                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)
+
     @staticmethod
     def _parse_yaml(descriptor, response=False):
         try:
index 33e1d18..6aed304 100644 (file)
@@ -134,6 +134,7 @@ class NsLcm(LcmBase):
     )  # 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
+    timeout_verticalscale = 1800   # default global timeout for Vertical Sclaing
     SUBOPERATION_STATUS_NOT_FOUND = -1
     SUBOPERATION_STATUS_NEW = -2
     SUBOPERATION_STATUS_SKIP = -3
@@ -8374,3 +8375,98 @@ class NsLcm(LcmBase):
             await asyncio.sleep(15, loop=self.loop)
         else:  # timeout_ns_deploy
             raise NgRoException("Timeout waiting ns to deploy")
+
+    async def vertical_scale(self, nsr_id, nslcmop_id):
+        """
+        Vertical Scale the VDUs in a NS
+
+        :param: nsr_id: NS Instance ID
+        :param: nslcmop_id: nslcmop ID of migrate
+
+        """
+        # Try to lock HA task here
+        task_is_locked_by_me = self.lcm_tasks.lock_HA("ns", "nslcmops", nslcmop_id)
+        if not task_is_locked_by_me:
+            return
+        logging_text = "Task ns={} vertical scale ".format(nsr_id)
+        self.logger.debug(logging_text + "Enter")
+        # get all needed from database
+        db_nslcmop = None
+        db_nslcmop_update = {}
+        nslcmop_operation_state = None
+        db_nsr_update = {}
+        target = {}
+        exc = None
+        # in case of error, indicates what part of scale was failed to put nsr at error status
+        start_deploy = time()
+
+        try:
+            # wait for any previous tasks in process
+            step = "Waiting for previous operations to terminate"
+            await self.lcm_tasks.waitfor_related_HA('ns', 'nslcmops', nslcmop_id)
+
+            self._write_ns_status(
+                nsr_id=nsr_id,
+                ns_state=None,
+                current_operation="VerticalScale",
+                current_operation_id=nslcmop_id
+            )
+            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})
+            operationParams = db_nslcmop.get("operationParams")
+            target = {}
+            target.update(operationParams)
+            desc = await self.RO.vertical_scale(nsr_id, target)
+            self.logger.debug("RO return > {}".format(desc))
+            action_id = desc["action_id"]
+            await self._wait_ng_ro(
+                nsr_id, action_id, nslcmop_id, start_deploy, self.timeout_verticalscale
+            )
+        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(step))
+            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)
+        finally:
+            self._write_ns_status(
+                nsr_id=nsr_id,
+                ns_state=None,
+                current_operation="IDLE",
+                current_operation_id=None,
+            )
+            if exc:
+                db_nslcmop_update[
+                    "detailed-status"
+                ] = "FAILED {}: {}".format(step, exc)
+                nslcmop_operation_state = "FAILED"
+            else:
+                nslcmop_operation_state = "COMPLETED"
+                db_nslcmop_update["detailed-status"] = "Done"
+                db_nsr_update["detailed-status"] = "Done"
+
+            self._write_op_status(
+                op_id=nslcmop_id,
+                stage="",
+                error_message="",
+                operation_state=nslcmop_operation_state,
+                other_update=db_nslcmop_update,
+            )
+            if nslcmop_operation_state:
+                try:
+                    msg = {
+                        "nsr_id": nsr_id,
+                        "nslcmop_id": nslcmop_id,
+                        "operationState": nslcmop_operation_state,
+                    }
+                    await self.msg.aiowrite("ns", "verticalscaled", msg, loop=self.loop)
+                except Exception as e:
+                    self.logger.error(
+                        logging_text + "kafka_write notification Exception {}".format(e)
+                    )
+            self.logger.debug(logging_text + "Exit")
+            self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_verticalscale")
index dbb3e1d..9304908 100644 (file)
@@ -424,6 +424,77 @@ db_nslcmops_text = """
       worker: fbf6b5aa99e2
     detailed-status: Done
 
+-   _admin:
+        created: 1566823354.4148262
+        modified: 1566823354.4148262
+        projects_read:
+        - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+        projects_write:
+        - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+        worker: 86434c2948e2
+    _id: 8b838aa8-53a3-4955-80bd-fbba6a7957ed
+    detailed-status: 'FAILED executing proxy charm initial primitives for member_vnf_index=1
+        vdu_id=None: charm error executing primitive verify-ssh-credentials for member_vnf_index=1
+        vdu_id=None: ''timeout after 600 seconds'''
+    id: 8b838aa8-53a3-4955-80bd-fbba6a7957ed
+    isAutomaticInvocation: false
+    isCancelPending: false
+    lcmOperationType: scale
+    links:
+        nsInstance: /osm/nslcm/v1/ns_instances/f48163a6-c807-47bc-9682-f72caef5af85
+        self: /osm/nslcm/v1/ns_lcm_op_occs/8b838aa8-53a3-4955-80bd-fbba6a7957ed
+    nsInstanceId: f48163a6-c807-47bc-9682-f72caef5af85
+    operationParams:
+        additionalParamsForVnf:
+        -   additionalParams:
+                touch_filename: /home/ubuntu/first-touch-1
+                touch_filename2: /home/ubuntu/second-touch-1
+            member-vnf-index: '1'
+        lcmOperationType: instantiate
+        nsDescription: default description
+        nsInstanceId: f48163a6-c807-47bc-9682-f72caef5af85
+        nsName: ALF
+        nsdId: 8c2f8b95-bb1b-47ee-8001-36dc090678da
+        vimAccountId: ea958ba5-4e58-4405-bf42-6e3be15d4c3a
+    operationState: FAILED
+    startTime: 1566823354.414689
+    statusEnteredTime: 1566824534.5112448
+
+-   _admin:
+        created: 1566823354.4148262
+        modified: 1566823354.4148262
+        projects_read:
+        - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+        projects_write:
+        - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+        worker: 86434c2948e2
+    _id: a21af1d4-7f1a-4f7b-b666-222315113a62
+    detailed-status: 'FAILED executing proxy charm initial primitives for member_vnf_index=1
+        vdu_id=None: charm error executing primitive verify-ssh-credentials for member_vnf_index=1
+        vdu_id=None: ''timeout after 600 seconds'''
+    id: a21af1d4-7f1a-4f7b-b666-222315113a62
+    isAutomaticInvocation: false
+    isCancelPending: false
+    lcmOperationType: scale
+    links:
+        nsInstance: /osm/nslcm/v1/ns_instances/f48163a6-c807-47bc-9682-f72caef5af85
+        self: /osm/nslcm/v1/ns_lcm_op_occs/a21af1d4-7f1a-4f7b-b666-222315113a62
+    nsInstanceId: f48163a6-c807-47bc-9682-f72caef5af85
+    operationParams:
+        additionalParamsForVnf:
+        -   additionalParams:
+                touch_filename: /home/ubuntu/first-touch-1
+                touch_filename2: /home/ubuntu/second-touch-1
+            member-vnf-index: '1'
+        lcmOperationType: instantiate
+        nsDescription: default description
+        nsInstanceId: f48163a6-c807-47bc-9682-f72caef5af85
+        nsName: ALF
+        nsdId: 8c2f8b95-bb1b-47ee-8001-36dc090678da
+        vimAccountId: ea958ba5-4e58-4405-bf42-6e3be15d4c3a
+    operationState: COMPLETED
+    startTime: 1566823354.414689
+    statusEnteredTime: 1566824534.5112448
 """
 
 db_nsrs_text = """
@@ -2555,4 +2626,9 @@ test_ids = {
         "nslcmops1": "6eace44b-2ef4-4de5-b15f-63f2e8898bfb",
         "vnfrs": "a6df8aa0-1271-4dfc-85a5-e0484fea303f",
     },
+    "TEST-V-SCALE": {
+        "ns": "f48163a6-c807-47bc-9682-f72caef5af85",
+        "instantiate-1": "8b838aa8-53a3-4955-80bd-fbba6a7957ed",
+        "instantiate": "a21af1d4-7f1a-4f7b-b666-222315113a62",
+    },
 }
index 466976c..d7192c9 100644 (file)
@@ -841,6 +841,39 @@ class TestMyNS(asynctest.TestCase):
                 self.db.get_one("vnfrs", {"_id": vnf_instance_id})
             self.assertTrue("database exception Not found entry with filter" in str(context.exception))
 
+    # test vertical scale executes sucessfully
+    # @patch("osm_lcm.ng_ro.status.response")
+    @asynctest.fail_on(active_handles=True)
+    async def test_vertical_scaling(self):
+        nsr_id = descriptors.test_ids["TEST-V-SCALE"]["ns"]
+        nslcmop_id = descriptors.test_ids["TEST-V-SCALE"]["instantiate"]
+
+        # calling the vertical scale fucntion
+        # self.my_ns.RO.status = asynctest.CoroutineMock(self.my_ns.RO.status, side_effect=self._ro_status("update"))
+        mock_wait_ng_ro = asynctest.CoroutineMock()
+        with patch("osm_lcm.ns.NsLcm._wait_ng_ro", mock_wait_ng_ro):
+            await self.my_ns.vertical_scale(nsr_id, nslcmop_id)
+            return_value = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get(
+                "operationState"
+            )
+            expected_value = "COMPLETED"
+            self.assertEqual(return_value, expected_value)
+
+    # test vertical scale executes fail
+    @asynctest.fail_on(active_handles=True)
+    async def test_vertical_scaling_fail(self):
+        # get th nsr nad nslcmops id from descriptors
+        nsr_id = descriptors.test_ids["TEST-V-SCALE"]["ns"]
+        nslcmop_id = descriptors.test_ids["TEST-V-SCALE"]["instantiate-1"]
+
+        # calling the vertical scale fucntion
+        await self.my_ns.vertical_scale(nsr_id, nslcmop_id)
+        return_value = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get(
+            "operationState"
+        )
+        expected_value = "FAILED"
+        self.assertEqual(return_value, expected_value)
+
     # async def test_instantiate_pdu(self):
     #     nsr_id = descriptors.test_ids["TEST-A"]["ns"]
     #     nslcmop_id = descriptors.test_ids["TEST-A"]["instantiate"]