exc_info=True,
)
raise NsException(e)
+
+ def verticalscale_task(
+ self, vdu, vnf, vdu_index, action_id, nsr_id, task_index, extra_dict
+ ):
+ target_vim, vim_info = next(k_v for k_v in vdu["vim_info"].items())
+ self._assign_vim(target_vim)
+ target_record = "vnfrs:{}:vdur.{}".format(vnf["_id"], vdu_index)
+ target_record_id = "vnfrs:{}:vdur.{}".format(vnf["_id"], vdu["id"])
+ deployment_info = {
+ "action_id": action_id,
+ "nsr_id": nsr_id,
+ "task_index": task_index,
+ }
+
+ task = Ns._create_task(
+ deployment_info=deployment_info,
+ target_id=target_vim,
+ item="verticalscale",
+ action="EXEC",
+ target_record=target_record,
+ target_record_id=target_record_id,
+ extra_dict=extra_dict,
+ )
+ return task
+
+ def verticalscale(self, session, indata, version, nsr_id, *args, **kwargs):
+ task_index = 0
+ extra_dict = {}
+ now = time()
+ action_id = indata.get("action_id", str(uuid4()))
+ step = ""
+ logging_text = "Task deploy nsr_id={} action_id={} ".format(nsr_id, action_id)
+ self.logger.debug(logging_text + "Enter")
+ try:
+ VnfFlavorData = indata.get("changeVnfFlavorData")
+ vnf_instance_id = VnfFlavorData["vnfInstanceId"]
+ step = "Getting vnfrs from db"
+ db_vnfr = self.db.get_one("vnfrs", {"_id": vnf_instance_id})
+ vduid = VnfFlavorData["additionalParams"]["vduid"]
+ vduCountIndex = VnfFlavorData["additionalParams"]["vduCountIndex"]
+ virtualMemory = VnfFlavorData["additionalParams"]["virtualMemory"]
+ numVirtualCpu = VnfFlavorData["additionalParams"]["numVirtualCpu"]
+ sizeOfStorage = VnfFlavorData["additionalParams"]["sizeOfStorage"]
+ flavor_dict = {
+ "name": vduid + "-flv",
+ "ram": virtualMemory,
+ "vcpus": numVirtualCpu,
+ "disk": sizeOfStorage,
+ }
+ db_new_tasks = []
+ step = "Creating Tasks for vertical scaling"
+ with self.write_lock:
+ for vdu_index, vdu in enumerate(db_vnfr["vdur"]):
+ if (
+ vdu["vdu-id-ref"] == vduid
+ and vdu["count-index"] == vduCountIndex
+ ):
+ extra_dict["params"] = {
+ "vim_vm_id": vdu["vim-id"],
+ "flavor_dict": flavor_dict,
+ }
+ task = self.verticalscale_task(
+ vdu,
+ db_vnfr,
+ vdu_index,
+ action_id,
+ nsr_id,
+ task_index,
+ extra_dict,
+ )
+ db_new_tasks.append(task)
+ task_index += 1
+ break
+ self.upload_all_tasks(
+ db_new_tasks=db_new_tasks,
+ now=now,
+ )
+ self.logger.debug(
+ logging_text + "Exit. Created {} tasks".format(len(db_new_tasks))
+ )
+ return (
+ {"status": "ok", "nsr_id": nsr_id, "action_id": action_id},
+ action_id,
+ True,
+ )
+ except Exception as e:
+ if isinstance(e, (DbException, NsException)):
+ self.logger.error(
+ logging_text + "Exit Exception while '{}': {}".format(step, e)
+ )
+ else:
+ e = traceback_format_exc()
+ self.logger.critical(
+ logging_text + "Exit Exception while '{}': {}".format(step, e),
+ exc_info=True,
+ )
+ raise NsException(e)
return "FAILED", ro_vim_item_update, db_task_update
+class VimInteractionResize(VimInteractionBase):
+ def exec(self, ro_task, task_index, task_depends):
+ task = ro_task["tasks"][task_index]
+ task_id = task["task_id"]
+ db_task_update = {"retries": 0}
+ created = False
+ target_flavor_uuid = None
+ created_items = {}
+ refreshed_vim_info = {}
+ target_vim = self.my_vims[ro_task["target_id"]]
+
+ try:
+ if task.get("params"):
+ vim_vm_id = task["params"].get("vim_vm_id")
+ flavor_dict = task["params"].get("flavor_dict")
+ self.logger.info("flavor_dict %s", flavor_dict)
+
+ try:
+ target_flavor_uuid = target_vim.get_flavor_id_from_data(flavor_dict)
+ except Exception as e:
+ self.logger.info("Cannot find any flavor matching %s.", str(e))
+ try:
+ target_flavor_uuid = target_vim.new_flavor(flavor_dict)
+ except Exception as e:
+ self.logger.error("Error creating flavor at VIM %s.", str(e))
+
+ if target_flavor_uuid is not None:
+ resized_status = target_vim.resize_instance(
+ vim_vm_id, target_flavor_uuid
+ )
+
+ if resized_status:
+ # Refresh VM to get new vim_info
+ vm_to_refresh_list = [vim_vm_id]
+ vim_dict = target_vim.refresh_vms_status(vm_to_refresh_list)
+ refreshed_vim_info = vim_dict[vim_vm_id]
+
+ ro_vim_item_update = {
+ "vim_id": vim_vm_id,
+ "vim_status": "DONE",
+ "created": created,
+ "created_items": created_items,
+ "vim_details": None,
+ "vim_message": None,
+ }
+
+ if refreshed_vim_info and refreshed_vim_info.get("status") not in (
+ "ERROR",
+ "VIM_ERROR",
+ ):
+ ro_vim_item_update["vim_details"] = refreshed_vim_info["vim_info"]
+
+ self.logger.debug(
+ "task={} {} resize done".format(task_id, ro_task["target_id"])
+ )
+ return "DONE", ro_vim_item_update, db_task_update
+ except (vimconn.VimConnException, NsWorkerException) as e:
+ self.logger.error(
+ "task={} vim={} Resize:" " {}".format(task_id, ro_task["target_id"], e)
+ )
+ ro_vim_item_update = {
+ "vim_status": "VIM_ERROR",
+ "created": created,
+ "vim_message": str(e),
+ }
+
+ return "FAILED", ro_vim_item_update, db_task_update
+
+
class NsWorker(threading.Thread):
REFRESH_BUILD = 5 # 5 seconds
REFRESH_ACTIVE = 60 # 1 minute
"migrate": VimInteractionMigration(
self.db, self.my_vims, self.db_vims, self.logger
),
+ "verticalscale": VimInteractionResize(
+ self.db, self.my_vims, self.db_vims, self.logger
+ ),
}
self.time_last_task_processed = None
# lists of tasks to delete because nsrs or vnfrs has been deleted from db
},
},
},
+ "verticalscale": {
+ "<ID>": {
+ "METHODS": ("POST"),
+ "ROLE_PERMISSION": "verticalscale:id:",
+ "<ID>": {
+ "METHODS": ("GET",),
+ "ROLE_PERMISSION": "verticalscale:id:id:",
+ },
+ },
+ },
}
},
}
"recreate:id:post": self.ns.recreate,
"recreate:id:id:get": self.ns.recreate_status,
"migrate:id:post": self.ns.migrate,
+ "verticalscale:id:post": self.ns.verticalscale,
}
def _format_in(self, kwargs):
self.assertEqual(task.get("nsr_id"), nsr_id)
self.assertEqual(task.get("target_id"), target_vim)
self.assertDictEqual(task, expected_result)
+
+ @patch("osm_ng_ro.ns.Ns._assign_vim")
+ def test_verticalscale_task(self, assign_vim):
+ self.ns = Ns()
+ extra_dict = {}
+ vdu_index = "1"
+ action_id = "bb937f49-3870-4169-b758-9732e1ff40f3"
+ nsr_id = "993166fe-723e-4680-ac4b-b1af2541ae31"
+ task_index = 1
+ target_record_id = (
+ "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:"
+ "vdur.bb9c43f9-10a2-4569-a8a8-957c3528b6d1"
+ )
+
+ expected_result = {
+ "target_id": "vim:f9f370ac-0d44-41a7-9000-457f2332bc35",
+ "action_id": "bb937f49-3870-4169-b758-9732e1ff40f3",
+ "nsr_id": "993166fe-723e-4680-ac4b-b1af2541ae31",
+ "task_id": "bb937f49-3870-4169-b758-9732e1ff40f3:1",
+ "status": "SCHEDULED",
+ "action": "EXEC",
+ "item": "verticalscale",
+ "target_record": "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:vdur.1",
+ "target_record_id": target_record_id,
+ "params": {
+ "vim_vm_id": "f37b18ef-3caa-4dc9-ab91-15c669b16396",
+ "flavor_dict": "flavor_dict",
+ },
+ }
+ vdu = {
+ "id": "bb9c43f9-10a2-4569-a8a8-957c3528b6d1",
+ "vim_info": {
+ "vim:f9f370ac-0d44-41a7-9000-457f2332bc35": {"interfaces": []}
+ },
+ }
+ vnf = {"_id": "665b4165-ce24-4320-bf19-b9a45bade49f"}
+ extra_dict["params"] = {
+ "vim_vm_id": "f37b18ef-3caa-4dc9-ab91-15c669b16396",
+ "flavor_dict": "flavor_dict",
+ }
+ task = self.ns.verticalscale_task(
+ vdu, vnf, vdu_index, action_id, nsr_id, task_index, extra_dict
+ )
+
+ self.assertDictEqual(task, expected_result)
import unittest
from unittest.mock import MagicMock, patch
-from osm_ng_ro.ns_thread import VimInteractionAffinityGroup, VimInteractionNet
+from osm_ng_ro.ns_thread import (
+ VimInteractionAffinityGroup,
+ VimInteractionNet,
+ VimInteractionResize,
+)
from osm_ro_plugin.vimconn import VimConnConnectionException, VimConnException
self.assertEqual(result[1].get("vim_message"), "DELETED")
self.assertEqual(result[1].get("created"), False)
self.assertEqual(result[1].get("vim_status"), "DELETED")
+
+
+class TestVimInteractionResize(unittest.TestCase):
+ def setUp(self):
+ module_name = "osm_ro_plugin"
+ self.target_vim = MagicMock(name=f"{module_name}.vimconn.VimConnector")
+ self.task_depends = None
+
+ patches = [patch(f"{module_name}.vimconn.VimConnector", self.target_vim)]
+
+ # Enabling mocks and add cleanups
+ for mock in patches:
+ mock.start()
+ self.addCleanup(mock.stop)
+
+ def test__exec_resize_done(self):
+ """
+ create verticalscale task
+ """
+ db = "test_db"
+ logger = "test_logger"
+ my_vims = "test-vim"
+ db_vims = {
+ 0: {
+ "config": {},
+ },
+ }
+ target_record_id = (
+ "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:"
+ "vdur.bb9c43f9-10a2-4569-a8a8-957c3528b6d1"
+ )
+
+ instance = VimInteractionResize(db, logger, my_vims, db_vims)
+ with patch.object(instance, "my_vims", [self.target_vim]), patch.object(
+ instance, "logger", logging
+ ), patch.object(instance, "db_vims", db_vims):
+ ro_task = {
+ "target_id": 0,
+ "tasks": {
+ "task_index_1": {
+ "target_id": 0,
+ "action_id": "bb937f49-3870-4169-b758-9732e1ff40f3",
+ "nsr_id": "993166fe-723e-4680-ac4b-b1af2541ae31",
+ "task_id": "bb937f49-3870-4169-b758-9732e1ff40f3:0",
+ "status": "SCHEDULED",
+ "action": "EXEC",
+ "item": "verticalscale",
+ "target_record": "vnfrs:665b4165-ce24-4320-bf19-b9a45bade49f:vdur.0",
+ "target_record_id": target_record_id,
+ "params": {
+ "vim_vm_id": "f37b18ef-3caa-4dc9-ab91-15c669b16396",
+ "flavor_dict": "flavor_dict",
+ },
+ }
+ },
+ }
+ task_index = "task_index_1"
+ result = instance.exec(ro_task, task_index, self.task_depends)
+ self.assertEqual(result[0], "DONE")
+ self.assertEqual(result[1].get("vim_status"), "DONE")
"""
# TODO: Add support for migration
raise vimconn.VimConnNotImplemented("Not implemented")
+
+ def resize_instance(self, vm_id, flavor_id=None):
+ """
+ resize a vdu
+ param:
+ vm_id: ID of an instance
+ flavor_id: flavor to resize the vdu
+ """
+ # TODO: Add support for resize
+ raise vimconn.VimConnNotImplemented("Not implemented")
# TODO: Add support for migration
raise vimconn.VimConnNotImplemented("Not implemented")
+ def resize_instance(self, vm_id, flavor_id=None):
+ """
+ resize a vdu
+ param:
+ vm_id: ID of an instance
+ flavor_id: flavor id to resize the vdu
+ """
+ # TODO: Add support for resize
+ raise vimconn.VimConnNotImplemented("Not implemented")
+
if __name__ == "__main__":
# Init logger
"""
# TODO: Add support for migration
raise vimconn.VimConnNotImplemented("Not implemented")
+
+ def resize_instance(self, vm_id, flavor_id=None):
+ """
+ resize a vdu
+ param:
+ vm_id: ID of an instance
+ flavor_id: flavor_id to resize the vdu to
+ """
+ # TODO: Add support for resize
+ raise vimconn.VimConnNotImplemented("Not implemented")
nvExceptions.NotFound,
) as e:
self._format_exception(e)
+
+ def resize_instance(self, vm_id, new_flavor_id):
+ """
+ For resizing the vm based on the given
+ flavor details
+ param:
+ vm_id : ID of an instance
+ new_flavor_id : Flavor id to be resized
+ Return the status of a resized instance
+ """
+ self._reload_connection()
+ self.logger.debug("resize the flavor of an instance")
+ instance_status, old_flavor_id, compute_host, az = self.get_vdu_state(vm_id)
+ old_flavor_disk = self.nova.flavors.find(id=old_flavor_id).to_dict()["disk"]
+ new_flavor_disk = self.nova.flavors.find(id=new_flavor_id).to_dict()["disk"]
+ try:
+ if instance_status == "ACTIVE" or instance_status == "SHUTOFF":
+ if old_flavor_disk > new_flavor_disk:
+ raise nvExceptions.BadRequest(
+ 400,
+ message="Server disk resize failed. Resize to lower disk flavor is not allowed",
+ )
+ else:
+ self.nova.servers.resize(server=vm_id, flavor=new_flavor_id)
+ vm_state = self.__wait_for_vm(vm_id, "VERIFY_RESIZE")
+ if vm_state:
+ instance_resized_status = self.confirm_resize(vm_id)
+ return instance_resized_status
+ else:
+ raise nvExceptions.BadRequest(
+ 409,
+ message="Cannot 'resize' vm_state is in ERROR",
+ )
+
+ else:
+ self.logger.debug("ERROR : Instance is not in ACTIVE or SHUTOFF state")
+ raise nvExceptions.BadRequest(
+ 409,
+ message="Cannot 'resize' instance while it is in vm_state resized",
+ )
+ except (
+ nvExceptions.BadRequest,
+ nvExceptions.ClientException,
+ nvExceptions.NotFound,
+ ) as e:
+ self._format_exception(e)
+
+ def confirm_resize(self, vm_id):
+ """
+ Confirm the resize of an instance
+ param:
+ vm_id: ID of an instance
+ """
+ self._reload_connection()
+ self.nova.servers.confirm_resize(server=vm_id)
+ if self.get_vdu_state(vm_id)[0] == "VERIFY_RESIZE":
+ self.__wait_for_vm(vm_id, "ACTIVE")
+ instance_status = self.get_vdu_state(vm_id)[0]
+ return instance_status
"""
# TODO: Add support for migration
raise vimconn.VimConnNotImplemented("Not implemented")
+
+ def resize_instance(self, vm_id, flavor_id=None):
+ """
+ resize a vdu
+ param:
+ vm_id: ID of an instance
+ flavor_id: flavor_id to resize the vdu to
+ """
+ # TODO: Add support for resize
+ raise vimconn.VimConnNotImplemented("Not implemented")
"""
# TODO: Add support for migration
raise vimconn.VimConnNotImplemented("Should have implemented this")
+
+ def resize_instance(self, vm_id, flavor_id=None):
+ """
+ resize a vdu
+ param:
+ vm_id: ID of an instance
+ flavor_id: flavor_id to resize the vdu to
+ """
+ # TODO: Add support for resize
+ raise vimconn.VimConnNotImplemented("Should have implemented this")
"""
raise VimConnNotImplemented("Should have implemented this")
+ def resize_instance(self, vm_id, flavor_id=None):
+ """
+ resize a vdu
+ param:
+ vm_id: ID of an instance
+ flavor_id: flavor_id to resize the vdu to
+ """
+ raise VimConnNotImplemented("Should have implemented this")
+
# NOT USED METHODS in current version. Deprecated
@deprecated
def host_vim2gui(self, host, server_dict):
--- /dev/null
+#######################################################################################
+# Copyright ETSI Contributors and Others.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#######################################################################################
+---
+features:
+ - |
+ Feature 10911 - Vertical scaling of VM instances from OSM
+ This feature enables resizing of virtual machine instances of VNFs.
+ Support added for Openstack VIM type.