From 335a06aaff7812734181e4a1382ed275bb8469d3 Mon Sep 17 00:00:00 2001 From: gatici Date: Wed, 26 Jul 2023 00:34:04 +0300 Subject: [PATCH] Fix Bug 2158 and Bug 2254 by arranging exception handling Change-Id: Id84e57944258536735eb114d4c77f471d700e1aa Signed-off-by: gatici --- NG-RO/osm_ng_ro/ns_thread.py | 4 +- .../tests/test_vimconn_openstack.py | 195 ++-- .../osm_rovim_openstack/vimconn_openstack.py | 840 +++++++++--------- .../fix_bug_2158_2254-86a20d2b2a6ca5a9.yaml | 23 + 4 files changed, 553 insertions(+), 509 deletions(-) create mode 100644 releasenotes/notes/fix_bug_2158_2254-86a20d2b2a6ca5a9.yaml diff --git a/NG-RO/osm_ng_ro/ns_thread.py b/NG-RO/osm_ng_ro/ns_thread.py index 42a8a591..e64db287 100644 --- a/NG-RO/osm_ng_ro/ns_thread.py +++ b/NG-RO/osm_ng_ro/ns_thread.py @@ -644,7 +644,9 @@ class VimInteractionImage(VimInteractionBase): # FIND vim_image_id = "" if task.get("find_params"): - vim_images = target_vim.get_image_list(**task["find_params"]) + vim_images = target_vim.get_image_list( + task["find_params"].get("filter_dict", {}) + ) if not vim_images: raise NsWorkerExceptionNotFound( diff --git a/RO-VIM-openstack/osm_rovim_openstack/tests/test_vimconn_openstack.py b/RO-VIM-openstack/osm_rovim_openstack/tests/test_vimconn_openstack.py index a3da3305..c2874935 100644 --- a/RO-VIM-openstack/osm_rovim_openstack/tests/test_vimconn_openstack.py +++ b/RO-VIM-openstack/osm_rovim_openstack/tests/test_vimconn_openstack.py @@ -27,15 +27,19 @@ from copy import deepcopy import logging import unittest +import cinderclient.exceptions as cExceptions from mock import MagicMock, patch +from neutronclient.common import exceptions as neExceptions from novaclient import exceptions as nvExceptions from novaclient.exceptions import ClientException, Conflict from osm_ro_plugin.vimconn import ( VimConnConnectionException, VimConnException, VimConnNotFoundException, + VimConnUnexpectedResponse, ) from osm_rovim_openstack.vimconn_openstack import vimconnector +from requests.exceptions import ConnectionError __author__ = "Igor D.C." __date__ = "$23-aug-2017 23:59:59$" @@ -1627,17 +1631,18 @@ class TestNewVmInstance(unittest.TestCase): @patch.object(vimconnector, "update_block_device_mapping") def test_new_shared_volumes(self, mock_update_block_device_mapping): """Create shared volume.""" - self.vimconn.cinder = CopyingMock() - self.vimconn.cinder.volumes.create.return_value.id = volume_id4 - shared_volume_data = {"size": 10, "name": "shared-volume"} - self.vimconn.cinder.volumes.create.side_effect = [ - Volume("avaible", "multiattach", "shared-volume", volume_id4) - ] + + class MyVolume: + name = "my-shared-volume" + id = volume_id4 + + self.vimconn.cinder.volumes.create.return_value = MyVolume() + shared_volume_data = {"size": 10, "name": "my-shared-volume"} result = self.vimconn.new_shared_volumes(shared_volume_data) self.vimconn.cinder.volumes.create.assert_called_once_with( - size=10, name="shared-volume", volume_type="multiattach" + size=10, name="my-shared-volume", volume_type="multiattach" ) - self.assertEqual(result[0], "shared-volume") + self.assertEqual(result[0], "my-shared-volume") self.assertEqual(result[1], volume_id4) @patch.object(vimconnector, "update_block_device_mapping") @@ -3894,7 +3899,9 @@ class TestNewVmInstance(unittest.TestCase): }, ) - def test_delete_floating_ip_by_id_floating_ip_raises_nvexception(self): + def test_delete_floating_ip_by_id__delete_floating_ip_raises_client_exception__operation_is_successful( + self, + ): """netron delete floating ip raises nvExceptions.ClientException.""" created_items = { f"floating_ip:{floating_network_vim_id}": True, @@ -3918,7 +3925,36 @@ class TestNewVmInstance(unittest.TestCase): "Error deleting floating ip: ClientException: Unknown Error (HTTP Client exception occurred.)" ) - def test_delete_floating_ip_by_id_floating_ip_raises_vimconnexception(self): + def test_delete_floating_ip_by_id__delete_floating_ip_raises_connection_error__operation_fails( + self, + ): + """netron delete floating ip raises nvExceptions.ClientException.""" + created_items = { + f"floating_ip:{floating_network_vim_id}": True, + f"port:{port_id}": True, + } + k_id = floating_network_vim_id + k = f"floating_ip:{floating_network_vim_id}" + self.vimconn.neutron.delete_floatingip.side_effect = ConnectionError( + "Connection exception occurred." + ) + with self.assertRaises(VimConnConnectionException): + self.vimconn._delete_floating_ip_by_id(k, k_id, created_items) + self.vimconn.neutron.delete_floatingip.assert_called_once_with(k_id) + self.assertEqual( + created_items, + { + f"floating_ip:{floating_network_vim_id}": True, + f"port:{port_id}": True, + }, + ) + self.vimconn.logger.error.assert_called_once_with( + "Error deleting floating ip: ConnectionError: Connection exception occurred." + ) + + def test_delete_floating_ip_by_id_floating_ip_raises_vimconn_not_found_exception__operation_is_successful( + self, + ): """netron delete floating ip raises VimConnNotFoundException.""" created_items = { f"floating_ip:{floating_network_vim_id}": True, @@ -4053,7 +4089,9 @@ class TestNewVmInstance(unittest.TestCase): ) self.assertEqual(created_items, expected_created_items) - def test_delete_volumes_by_id_with_cinder_delete_volume_raise_exception(self): + def test_delete_volumes_by_id_with_cinder__delete_volume_raise_client_exception__exception_is_not_raised( + self, + ): """cinder delete volume raises exception.""" created_items = { f"floating_ip:{floating_network_vim_id}": True, @@ -4071,8 +4109,8 @@ class TestNewVmInstance(unittest.TestCase): k = f"volume:{volume_id}" k_id = volume_id self.vimconn.cinder.volumes.get.return_value.status = "available" - self.vimconn.cinder.volumes.delete.side_effect = nvExceptions.ClientException( - "Connection aborted." + self.vimconn.cinder.volumes.delete.side_effect = cExceptions.ClientException( + 403, "Connection aborted." ) result = self.vimconn._delete_volumes_by_id_wth_cinder( k, k_id, volumes_to_hold, created_items @@ -4081,7 +4119,42 @@ class TestNewVmInstance(unittest.TestCase): self.vimconn.cinder.volumes.get.assert_called_once_with(k_id) self.vimconn.cinder.volumes.delete.assert_called_once_with(k_id) self.vimconn.logger.error.assert_called_once_with( - "Error deleting volume: ClientException: Unknown Error (HTTP Connection aborted.)" + "Error deleting volume: ClientException: Connection aborted. (HTTP 403)" + ) + self.assertEqual(created_items, expected_created_items) + + def test_delete_volumes_by_id_with_cinder__delete_volume_raise_connection_exception__exception_is_raised( + self, + ): + """cinder delete volume raises exception.""" + created_items = { + f"floating_ip:{floating_network_vim_id}": True, + f"volume:{volume_id2}": True, + f"volume:{volume_id}": True, + f"port:{port_id}": None, + } + expected_created_items = { + f"floating_ip:{floating_network_vim_id}": True, + f"volume:{volume_id2}": True, + f"volume:{volume_id}": True, + f"port:{port_id}": None, + } + volumes_to_hold = [] + k = f"volume:{volume_id}" + k_id = volume_id + self.vimconn.cinder.volumes.get.return_value.status = "available" + self.vimconn.cinder.volumes.delete.side_effect = cExceptions.ConnectionError( + "Connection failed." + ) + with self.assertRaises(VimConnConnectionException): + result = self.vimconn._delete_volumes_by_id_wth_cinder( + k, k_id, volumes_to_hold, created_items + ) + self.assertEqual(result, None) + self.vimconn.cinder.volumes.get.assert_called_once_with(k_id) + self.vimconn.cinder.volumes.delete.assert_called_once_with(k_id) + self.vimconn.logger.error.assert_called_once_with( + "Error deleting volume: ConnectionError: Connection failed." ) self.assertEqual(created_items, expected_created_items) @@ -4105,7 +4178,7 @@ class TestNewVmInstance(unittest.TestCase): result = self.vimconn._delete_volumes_by_id_wth_cinder( k, k_id, volumes_to_hold, created_items ) - self.assertEqual(result, None) + self.assertEqual(result, False) self.vimconn.cinder.volumes.get.assert_not_called() self.vimconn.cinder.volumes.delete.assert_not_called() self.vimconn.logger.error.assert_not_called() @@ -4295,7 +4368,7 @@ class TestNewVmInstance(unittest.TestCase): @patch.object(vimconnector, "_get_item_name_id") @patch.object(vimconnector, "_delete_volumes_by_id_wth_cinder") @patch.object(vimconnector, "_delete_floating_ip_by_id") - def test_delete_created_items_delete_vol_raises( + def test_delete_created_items__delete_vol_raises_connection_error__operation_fails( self, mock_delete_floating_ip_by_id, mock_delete_volumes_by_id_wth_cinder, @@ -4311,15 +4384,16 @@ class TestNewVmInstance(unittest.TestCase): ("floating_ip", f"{floating_network_vim_id}"), ("volume", f"{volume_id}"), ] - mock_delete_volumes_by_id_wth_cinder.side_effect = ConnectionError( - "Connection failed." + mock_delete_volumes_by_id_wth_cinder.side_effect = ( + neExceptions.ConnectionFailed("Connection failed.") ) volumes_to_hold = [] keep_waiting = False - result = self.vimconn._delete_created_items( - created_items, volumes_to_hold, keep_waiting - ) - self.assertEqual(result, False) + with self.assertRaises(VimConnConnectionException): + result = self.vimconn._delete_created_items( + created_items, volumes_to_hold, keep_waiting + ) + self.assertEqual(result, None) self.assertEqual(mock_get_item_name_id.call_count, 2) mock_delete_volumes_by_id_wth_cinder.assert_called_once_with( f"volume:{volume_id}", f"{volume_id}", [], created_items @@ -4336,7 +4410,7 @@ class TestNewVmInstance(unittest.TestCase): @patch.object(vimconnector, "_get_item_name_id") @patch.object(vimconnector, "_delete_volumes_by_id_wth_cinder") @patch.object(vimconnector, "_delete_floating_ip_by_id") - def test_delete_created_items_delete_fip_raises( + def test_delete_created_items__delete_fip_raises_connection_error__operation_fails( self, mock_delete_floating_ip_by_id, mock_delete_volumes_by_id_wth_cinder, @@ -4358,14 +4432,13 @@ class TestNewVmInstance(unittest.TestCase): ) volumes_to_hold = [] keep_waiting = True - result = self.vimconn._delete_created_items( - created_items, volumes_to_hold, keep_waiting - ) - self.assertEqual(result, True) - self.assertEqual(mock_get_item_name_id.call_count, 2) - mock_delete_volumes_by_id_wth_cinder.assert_called_once_with( - f"volume:{volume_id}", f"{volume_id}", [], created_items - ) + with self.assertRaises(VimConnConnectionException): + result = self.vimconn._delete_created_items( + created_items, volumes_to_hold, keep_waiting + ) + self.assertEqual(result, None) + self.assertEqual(mock_get_item_name_id.call_count, 1) + mock_delete_volumes_by_id_wth_cinder.assert_not_called() mock_delete_floating_ip_by_id.assert_called_once_with( f"floating_ip:{floating_network_vim_id}", f"{floating_network_vim_id}", @@ -4378,7 +4451,7 @@ class TestNewVmInstance(unittest.TestCase): @patch.object(vimconnector, "_get_item_name_id") @patch.object(vimconnector, "_delete_volumes_by_id_wth_cinder") @patch.object(vimconnector, "_delete_floating_ip_by_id") - def test_delete_created_items_get_item_name_raises( + def test_delete_created_items_get_item_name_raises_type_error__operation_fails( self, mock_delete_floating_ip_by_id, mock_delete_volumes_by_id_wth_cinder, @@ -4396,19 +4469,16 @@ class TestNewVmInstance(unittest.TestCase): ] volumes_to_hold = [] keep_waiting = False - result = self.vimconn._delete_created_items( - created_items, volumes_to_hold, keep_waiting - ) - self.assertEqual(result, False) - self.assertEqual(mock_get_item_name_id.call_count, 2) + with self.assertRaises(VimConnException): + result = self.vimconn._delete_created_items( + created_items, volumes_to_hold, keep_waiting + ) + self.assertEqual(result, None) + self.assertEqual(mock_get_item_name_id.call_count, 1) mock_delete_volumes_by_id_wth_cinder.assert_not_called() mock_delete_floating_ip_by_id.assert_not_called() _call_logger = self.vimconn.logger.error.call_args_list self.assertEqual(_call_logger[0][0], ("Error deleting 3: Invalid Type",)) - self.assertEqual( - _call_logger[1][0], - (f"Error deleting volume{volume_id}: Invalid attribute",), - ) @patch.object(vimconnector, "_get_item_name_id") @patch.object(vimconnector, "_delete_volumes_by_id_wth_cinder") @@ -4578,16 +4648,14 @@ class TestNewVmInstance(unittest.TestCase): @patch("time.sleep") @patch.object(vimconnector, "_extract_items_wth_keep_flag_from_created_items") - @patch.object(vimconnector, "_format_exception") @patch.object(vimconnector, "_reload_connection") @patch.object(vimconnector, "_delete_vm_ports_attached_to_network") @patch.object(vimconnector, "_delete_created_items") - def test_delete_vminstance_extract_items_wth_keep_raises( + def test_delete_vminstance__extract_items_wth_keep_raises_attributeerror__raise_vimconnexception( self, mock_delete_created_items, mock_delete_vm_ports_attached_to_network, mock_reload_connection, - mock_format_exception, mock_extract_items_wth_keep_flag_from_created_items, mock_sleep, ): @@ -4603,7 +4671,7 @@ class TestNewVmInstance(unittest.TestCase): mock_extract_items_wth_keep_flag_from_created_items.side_effect = AttributeError volumes_to_hold = [] mock_delete_created_items.return_value = False - with self.assertRaises(AttributeError): + with self.assertRaises(VimConnException): self.vimconn.delete_vminstance( vm_id, initial_created_items, volumes_to_hold ) @@ -4612,7 +4680,6 @@ class TestNewVmInstance(unittest.TestCase): self.vimconn.nova.servers.delete.assert_not_called() mock_delete_created_items.assert_not_called() mock_sleep.assert_not_called() - mock_format_exception.assert_not_called() mock_extract_items_wth_keep_flag_from_created_items.assert_called_once_with( initial_created_items ) @@ -4623,7 +4690,7 @@ class TestNewVmInstance(unittest.TestCase): @patch.object(vimconnector, "_reload_connection") @patch.object(vimconnector, "_delete_vm_ports_attached_to_network") @patch.object(vimconnector, "_delete_created_items") - def test_delete_vminstance_delete_created_items_raises( + def test_delete_vminstance__delete_created_items_returns_true__delete_created_items_called_several_times( self, mock_delete_created_items, mock_delete_vm_ports_attached_to_network, @@ -4638,15 +4705,12 @@ class TestNewVmInstance(unittest.TestCase): mock_extract_items_wth_keep_flag_from_created_items.return_value = created_items mock_sleep = MagicMock() volumes_to_hold = [] - err = ConnectionError("ClientException occurred.") - mock_delete_created_items.side_effect = err - with self.assertRaises(ConnectionError) as err: - self.vimconn.delete_vminstance(vm_id, created_items, volumes_to_hold) - self.assertEqual(str(err), "ClientException occurred.") + mock_delete_created_items.side_effect = [True, False] + self.vimconn.delete_vminstance(vm_id, created_items, volumes_to_hold) mock_reload_connection.assert_called_once() mock_delete_vm_ports_attached_to_network.assert_called_once_with(created_items) self.vimconn.nova.servers.delete.assert_called_once_with(vm_id) - mock_delete_created_items.assert_called_once() + self.assertEqual(mock_delete_created_items.call_count, 2) mock_sleep.assert_not_called() mock_extract_items_wth_keep_flag_from_created_items.assert_called_once_with( created_items @@ -4654,16 +4718,14 @@ class TestNewVmInstance(unittest.TestCase): @patch("time.sleep") @patch.object(vimconnector, "_extract_items_wth_keep_flag_from_created_items") - @patch.object(vimconnector, "_format_exception") @patch.object(vimconnector, "_reload_connection") @patch.object(vimconnector, "_delete_vm_ports_attached_to_network") @patch.object(vimconnector, "_delete_created_items") - def test_delete_vminstance_delete_vm_ports_raises( + def test_delete_vminstance__delete_vm_ports_raises_connection_error__raise_vimconnconnectionexception( self, mock_delete_created_items, mock_delete_vm_ports_attached_to_network, mock_reload_connection, - mock_format_exception, mock_extract_items_wth_keep_flag_from_created_items, mock_sleep, ): @@ -4674,10 +4736,9 @@ class TestNewVmInstance(unittest.TestCase): volumes_to_hold = [f"{volume_id}", f"{volume_id2}"] err = ConnectionError("ClientException occurred.") mock_delete_vm_ports_attached_to_network.side_effect = err - mock_delete_created_items.side_effect = err - with self.assertRaises(ConnectionError) as err: + mock_delete_created_items.return_value = False + with self.assertRaises(VimConnConnectionException): self.vimconn.delete_vminstance(vm_id, created_items, volumes_to_hold) - self.assertEqual(str(err), "ClientException occurred.") mock_reload_connection.assert_called_once() mock_delete_vm_ports_attached_to_network.assert_called_once_with(created_items) self.vimconn.nova.servers.delete.assert_not_called() @@ -4689,16 +4750,14 @@ class TestNewVmInstance(unittest.TestCase): @patch("time.sleep") @patch.object(vimconnector, "_extract_items_wth_keep_flag_from_created_items") - @patch.object(vimconnector, "_format_exception") @patch.object(vimconnector, "_reload_connection") @patch.object(vimconnector, "_delete_vm_ports_attached_to_network") @patch.object(vimconnector, "_delete_created_items") - def test_delete_vminstance_nova_server_delete_raises( + def test_delete_vminstance__nova_server_delete_raises_clientexception__raise_vimconn_unexpected_response( self, mock_delete_created_items, mock_delete_vm_ports_attached_to_network, mock_reload_connection, - mock_format_exception, mock_extract_items_wth_keep_flag_from_created_items, mock_sleep, ): @@ -4707,12 +4766,11 @@ class TestNewVmInstance(unittest.TestCase): created_items = deepcopy(created_items_all_true) mock_extract_items_wth_keep_flag_from_created_items.return_value = created_items volumes_to_hold = [f"{volume_id}", f"{volume_id2}"] - err = VimConnConnectionException("ClientException occurred.") + err = nvExceptions.ClientException("ClientException occurred.") self.vimconn.nova.servers.delete.side_effect = err mock_delete_created_items.side_effect = err - with self.assertRaises(VimConnConnectionException) as err: + with self.assertRaises(VimConnUnexpectedResponse): self.vimconn.delete_vminstance(vm_id, created_items, volumes_to_hold) - self.assertEqual(str(err), "ClientException occurred.") mock_reload_connection.assert_called_once() mock_delete_vm_ports_attached_to_network.assert_called_once_with(created_items) self.vimconn.nova.servers.delete.assert_called_once_with(vm_id) @@ -4724,16 +4782,14 @@ class TestNewVmInstance(unittest.TestCase): @patch("time.sleep") @patch.object(vimconnector, "_extract_items_wth_keep_flag_from_created_items") - @patch.object(vimconnector, "_format_exception") @patch.object(vimconnector, "_reload_connection") @patch.object(vimconnector, "_delete_vm_ports_attached_to_network") @patch.object(vimconnector, "_delete_created_items") - def test_delete_vminstance_reload_connection_raises( + def test_delete_vminstance__reload_connection_raises_connection_error__raises_vimconnconnection_exception( self, mock_delete_created_items, mock_delete_vm_ports_attached_to_network, mock_reload_connection, - mock_format_exception, mock_extract_items_wth_keep_flag_from_created_items, mock_sleep, ): @@ -4746,9 +4802,8 @@ class TestNewVmInstance(unittest.TestCase): err = ConnectionError("ClientException occurred.") mock_delete_created_items.return_value = False mock_reload_connection.side_effect = err - with self.assertRaises(ConnectionError) as err: + with self.assertRaises(VimConnConnectionException): self.vimconn.delete_vminstance(vm_id, created_items, volumes_to_hold) - self.assertEqual(str(err), "ClientException occurred.") mock_reload_connection.assert_called_once() mock_delete_vm_ports_attached_to_network.assert_not_called() self.vimconn.nova.servers.delete.assert_not_called() diff --git a/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py b/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py index 9d474371..bca94792 100644 --- a/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py +++ b/RO-VIM-openstack/osm_rovim_openstack/vimconn_openstack.py @@ -41,6 +41,7 @@ import time from typing import Dict, List, Optional, Tuple from cinderclient import client as cClient +import cinderclient.exceptions as cExceptions from glanceclient import client as glClient import glanceclient.exc as gl1Exceptions from keystoneauth1 import session @@ -85,6 +86,16 @@ volume_timeout = 1800 server_timeout = 1800 +def catch_any_exception(func): + def format_exception(*args, **kwargs): + try: + return func(*args, *kwargs) + except Exception as e: + vimconnector._format_exception(e) + + return format_exception + + class SafeDumper(yaml.SafeDumper): def represent_data(self, data): # Openstack APIs use custom subclasses of dict and YAML safe dumper @@ -525,7 +536,8 @@ class vimconnector(vimconn.VimConnector): # Types. Also, abstract vimconnector should call the validation # method before the implemented VIM connectors are called. - def _format_exception(self, exception): + @staticmethod + def _format_exception(exception): """Transform a keystone, nova, neutron exception into a vimconn exception discovering the cause""" message_error = str(exception) tip = "" @@ -535,8 +547,10 @@ class vimconnector(vimconn.VimConnector): ( neExceptions.NetworkNotFoundClient, nvExceptions.NotFound, + nvExceptions.ResourceNotFound, ksExceptions.NotFound, gl1Exceptions.HTTPNotFound, + cExceptions.NotFound, ), ): raise vimconn.VimConnNotFoundException( @@ -551,6 +565,7 @@ class vimconnector(vimconn.VimConnector): ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed, + cExceptions.ConnectionError, ), ): if type(exception).__name__ == "SSLError": @@ -565,6 +580,8 @@ class vimconnector(vimconn.VimConnector): KeyError, nvExceptions.BadRequest, ksExceptions.BadRequest, + gl1Exceptions.BadRequest, + cExceptions.BadRequest, ), ): if message_error == "OS-EXT-SRV-ATTR:host": @@ -582,6 +599,7 @@ class vimconnector(vimconn.VimConnector): nvExceptions.ClientException, ksExceptions.ClientException, neExceptions.NeutronException, + cExceptions.ClientException, ), ): raise vimconn.VimConnUnexpectedResponse( @@ -594,9 +612,10 @@ class vimconnector(vimconn.VimConnector): elif isinstance(exception, vimconn.VimConnException): raise exception else: # () - self.logger.error("General Exception " + message_error, exc_info=True) + logger = logging.getLogger("ro.vim.openstack") + logger.error("General Exception " + message_error, exc_info=True) - raise vimconn.VimConnConnectionException( + raise vimconn.VimConnException( type(exception).__name__ + ": " + message_error ) @@ -673,7 +692,6 @@ class vimconnector(vimconn.VimConnector): Returns the tenant list of dictionaries: [{'name':', 'id':', ...}, ...] """ self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict)) - try: self._reload_connection() @@ -703,7 +721,6 @@ class vimconnector(vimconn.VimConnector): def new_tenant(self, tenant_name, tenant_description): """Adds a new tenant to openstack VIM. Returns the tenant identifier""" self.logger.debug("Adding a new tenant name: %s", tenant_name) - try: self._reload_connection() @@ -729,7 +746,6 @@ class vimconnector(vimconn.VimConnector): def delete_tenant(self, tenant_id): """Delete a tenant from openstack VIM. Returns the old tenant identifier""" self.logger.debug("Deleting tenant %s from VIM", tenant_id) - try: self._reload_connection() @@ -739,6 +755,7 @@ class vimconnector(vimconn.VimConnector): self.keystone.tenants.delete(tenant_id) return tenant_id + except ( ksExceptions.ConnectionError, ksExceptions.ClientException, @@ -828,7 +845,7 @@ class vimconnector(vimconn.VimConnector): "dataplane_physical_net" ) - # if it is non empty list, use the first value. If it is a string use the value directly + # if it is non-empty list, use the first value. If it is a string use the value directly if ( isinstance(provider_physical_network, (tuple, list)) and provider_physical_network @@ -1007,6 +1024,14 @@ class vimconnector(vimconn.VimConnector): if k_item == "l2gwconn": self.neutron.delete_l2_gateway_connection(k_id) + + except (neExceptions.ConnectionFailed, ConnectionError) as e2: + self.logger.error( + "Error deleting l2 gateway connection: {}: {}".format( + type(e2).__name__, e2 + ) + ) + self._format_exception(e2) except Exception as e2: self.logger.error( "Error deleting l2 gateway connection: {}: {}".format( @@ -1031,7 +1056,6 @@ class vimconnector(vimconn.VimConnector): Returns the network list of dictionaries """ self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict)) - try: self._reload_connection() filter_dict_os = filter_dict.copy() @@ -1091,6 +1115,7 @@ class vimconnector(vimconn.VimConnector): return net + @catch_any_exception def delete_network(self, net_id, created_items=None): """ Removes a tenant network from VIM and its associated elements @@ -1114,6 +1139,14 @@ class vimconnector(vimconn.VimConnector): k_item, _, k_id = k.partition(":") if k_item == "l2gwconn": self.neutron.delete_l2_gateway_connection(k_id) + + except (neExceptions.ConnectionFailed, ConnectionError) as e: + self.logger.error( + "Error deleting l2 gateway connection: {}: {}".format( + type(e).__name__, e + ) + ) + self._format_exception(e) except Exception as e: self.logger.error( "Error deleting l2 gateway connection: {}: {}".format( @@ -1126,21 +1159,22 @@ class vimconnector(vimconn.VimConnector): for p in ports["ports"]: try: self.neutron.delete_port(p["id"]) + + except (neExceptions.ConnectionFailed, ConnectionError) as e: + self.logger.error("Error deleting port %s: %s", p["id"], str(e)) + # If there is connection error, it raises. + self._format_exception(e) except Exception as e: self.logger.error("Error deleting port %s: %s", p["id"], str(e)) self.neutron.delete_network(net_id) return net_id - except ( - neExceptions.ConnectionFailed, - neExceptions.NetworkNotFoundClient, - neExceptions.NeutronException, - ksExceptions.ClientException, - neExceptions.NeutronException, - ConnectionError, - ) as e: - self._format_exception(e) + except (neExceptions.NetworkNotFoundClient, neExceptions.NotFound) as e: + # If network to be deleted is not found, it does not raise. + self.logger.warning( + f"Error deleting network: {net_id} is not found, {str(e)}" + ) def refresh_nets_status(self, net_list): """Get the status of the networks @@ -1193,13 +1227,11 @@ class vimconnector(vimconn.VimConnector): def get_flavor(self, flavor_id): """Obtain flavor details from the VIM. Returns the flavor dict details""" self.logger.debug("Getting flavor '%s'", flavor_id) - try: self._reload_connection() flavor = self.nova.flavors.find(id=flavor_id) - # TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema) - return flavor.to_dict() + except ( nvExceptions.NotFound, nvExceptions.ClientException, @@ -1271,6 +1303,7 @@ class vimconnector(vimconn.VimConnector): ) except ( nvExceptions.NotFound, + nvExceptions.BadRequest, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError, @@ -1541,6 +1574,7 @@ class vimconnector(vimconn.VimConnector): flavor_data.get("extended"), ) + @catch_any_exception def new_flavor(self, flavor_data: dict, change_name_if_used: bool = True) -> str: """Adds a tenant flavor to openstack VIM. if change_name_if_used is True, it will change name in case of conflict, @@ -1558,70 +1592,58 @@ class vimconnector(vimconn.VimConnector): retry = 0 max_retries = 3 name_suffix = 0 + name = flavor_data["name"] + while retry < max_retries: + retry += 1 + try: + self._reload_connection() - try: - name = flavor_data["name"] - while retry < max_retries: - retry += 1 - try: - self._reload_connection() + if change_name_if_used: + name = self._change_flavor_name(name, name_suffix, flavor_data) - if change_name_if_used: - name = self._change_flavor_name(name, name_suffix, flavor_data) + ram, vcpus, extra_specs, extended = self._get_flavor_details( + flavor_data + ) + if extended: + self._process_extended_config_of_flavor(extended, extra_specs) - ram, vcpus, extra_specs, extended = self._get_flavor_details( - flavor_data - ) - if extended: - self._process_extended_config_of_flavor(extended, extra_specs) - - # Create flavor - - new_flavor = self.nova.flavors.create( - name=name, - ram=ram, - vcpus=vcpus, - disk=flavor_data.get("disk", 0), - ephemeral=flavor_data.get("ephemeral", 0), - swap=flavor_data.get("swap", 0), - is_public=flavor_data.get("is_public", True), - ) + # Create flavor - # Add metadata - if extra_specs: - new_flavor.set_keys(extra_specs) + new_flavor = self.nova.flavors.create( + name=name, + ram=ram, + vcpus=vcpus, + disk=flavor_data.get("disk", 0), + ephemeral=flavor_data.get("ephemeral", 0), + swap=flavor_data.get("swap", 0), + is_public=flavor_data.get("is_public", True), + ) - return new_flavor.id + # Add metadata + if extra_specs: + new_flavor.set_keys(extra_specs) - except nvExceptions.Conflict as e: - if change_name_if_used and retry < max_retries: - continue + return new_flavor.id - self._format_exception(e) + except nvExceptions.Conflict as e: + if change_name_if_used and retry < max_retries: + continue - except ( - ksExceptions.ClientException, - nvExceptions.ClientException, - ConnectionError, - KeyError, - ) as e: - self._format_exception(e) + self._format_exception(e) + @catch_any_exception def delete_flavor(self, flavor_id): """Deletes a tenant flavor from openstack VIM. Returns the old flavor_id""" try: self._reload_connection() self.nova.flavors.delete(flavor_id) - return flavor_id - # except nvExceptions.BadRequest as e: - except ( - nvExceptions.NotFound, - ksExceptions.ClientException, - nvExceptions.ClientException, - ConnectionError, - ) as e: - self._format_exception(e) + + except (nvExceptions.NotFound, nvExceptions.ResourceNotFound) as e: + # If flavor is not found, it does not raise. + self.logger.warning( + f"Error deleting flavor: {flavor_id} is not found, {str(e.message)}" + ) def new_image(self, image_dict): """ @@ -1704,12 +1726,6 @@ class vimconnector(vimconn.VimConnector): self.glance.images.update(new_image.id, **metadata_to_load) return new_image.id - except ( - nvExceptions.Conflict, - ksExceptions.ClientException, - nvExceptions.ClientException, - ) as e: - self._format_exception(e) except ( HTTPException, gl1Exceptions.HTTPException, @@ -1725,7 +1741,10 @@ class vimconnector(vimconn.VimConnector): "{}: {} for {}".format(type(e).__name__, e, image_dict["location"]), http_code=vimconn.HTTP_Bad_Request, ) + except Exception as e: + self._format_exception(e) + @catch_any_exception def delete_image(self, image_id): """Deletes a tenant image from openstack VIM. Returns the old id""" try: @@ -1733,36 +1752,25 @@ class vimconnector(vimconn.VimConnector): self.glance.images.delete(image_id) return image_id - except ( - nvExceptions.NotFound, - ksExceptions.ClientException, - nvExceptions.ClientException, - gl1Exceptions.CommunicationError, - gl1Exceptions.HTTPNotFound, - ConnectionError, - ) as e: # TODO remove - self._format_exception(e) + except gl1Exceptions.NotFound as e: + # If image is not found, it does not raise. + self.logger.warning( + f"Error deleting image: {image_id} is not found, {str(e)}" + ) + @catch_any_exception def get_image_id_from_path(self, path): """Get the image id from image path in the VIM database. Returns the image_id""" - try: - self._reload_connection() - images = self.glance.images.list() + self._reload_connection() + images = self.glance.images.list() - for image in images: - if image.metadata.get("location") == path: - return image.id + for image in images: + if image.metadata.get("location") == path: + return image.id - raise vimconn.VimConnNotFoundException( - "image with location '{}' not found".format(path) - ) - except ( - ksExceptions.ClientException, - nvExceptions.ClientException, - gl1Exceptions.CommunicationError, - ConnectionError, - ) as e: - self._format_exception(e) + raise vimconn.VimConnNotFoundException( + "image with location '{}' not found".format(path) + ) def get_image_list(self, filter_dict={}): """Obtain tenant images from VIM @@ -1775,7 +1783,6 @@ class vimconnector(vimconn.VimConnector): List can be empty """ self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict)) - try: self._reload_connection() # filter_dict_os = filter_dict.copy() @@ -1802,6 +1809,7 @@ class vimconnector(vimconn.VimConnector): pass return filtered_list + except ( ksExceptions.ClientException, nvExceptions.ClientException, @@ -2185,16 +2193,14 @@ class vimconnector(vimconn.VimConnector): volume_txt += ":keep" created_items[volume_txt] = True + @catch_any_exception def new_shared_volumes(self, shared_volume_data) -> (str, str): - try: - volume = self.cinder.volumes.create( - size=shared_volume_data["size"], - name=shared_volume_data["name"], - volume_type="multiattach", - ) - return (volume.name, volume.id) - except (ConnectionError, KeyError) as e: - self._format_exception(e) + volume = self.cinder.volumes.create( + size=shared_volume_data["size"], + name=shared_volume_data["name"], + volume_type="multiattach", + ) + return volume.name, volume.id def _prepare_shared_volumes( self, @@ -2774,20 +2780,19 @@ class vimconnector(vimconn.VimConnector): flavor_id, str(net_list), ) + server = None + created_items = {} + net_list_vim = [] + # list of external networks to be connected to instance, later on used to create floating_ip + external_network = [] + # List of ports with port-security disabled + no_secured_ports = [] + block_device_mapping = {} + existing_vim_volumes = [] + server_group_id = None + scheduller_hints = {} try: - server = None - created_items = {} - net_list_vim = [] - # list of external networks to be connected to instance, later on used to create floating_ip - external_network = [] - # List of ports with port-security disabled - no_secured_ports = [] - block_device_mapping = {} - existing_vim_volumes = [] - server_group_id = None - scheduller_hints = {} - # Check the Openstack Connection self._reload_connection() @@ -2907,6 +2912,7 @@ class vimconnector(vimconn.VimConnector): """Returns the VM instance information from VIM""" return self._find_nova_server(vm_id) + @catch_any_exception def get_vminstance_console(self, vm_id, console_type="vnc"): """ Get a console for the virtual machine @@ -2922,66 +2928,56 @@ class vimconnector(vimconn.VimConnector): suffix: extra text, e.g. the http path and query string """ self.logger.debug("Getting VM CONSOLE from VIM") + self._reload_connection() + server = self.nova.servers.find(id=vm_id) + + if console_type is None or console_type == "novnc": + console_dict = server.get_vnc_console("novnc") + elif console_type == "xvpvnc": + console_dict = server.get_vnc_console(console_type) + elif console_type == "rdp-html5": + console_dict = server.get_rdp_console(console_type) + elif console_type == "spice-html5": + console_dict = server.get_spice_console(console_type) + else: + raise vimconn.VimConnException( + "console type '{}' not allowed".format(console_type), + http_code=vimconn.HTTP_Bad_Request, + ) - try: - self._reload_connection() - server = self.nova.servers.find(id=vm_id) + console_dict1 = console_dict.get("console") - if console_type is None or console_type == "novnc": - console_dict = server.get_vnc_console("novnc") - elif console_type == "xvpvnc": - console_dict = server.get_vnc_console(console_type) - elif console_type == "rdp-html5": - console_dict = server.get_rdp_console(console_type) - elif console_type == "spice-html5": - console_dict = server.get_spice_console(console_type) - else: - raise vimconn.VimConnException( - "console type '{}' not allowed".format(console_type), - http_code=vimconn.HTTP_Bad_Request, - ) - - console_dict1 = console_dict.get("console") + if console_dict1: + console_url = console_dict1.get("url") - if console_dict1: - console_url = console_dict1.get("url") + if console_url: + # parse console_url + protocol_index = console_url.find("//") + suffix_index = ( + console_url[protocol_index + 2 :].find("/") + protocol_index + 2 + ) + port_index = ( + console_url[protocol_index + 2 : suffix_index].find(":") + + protocol_index + + 2 + ) - if console_url: - # parse console_url - protocol_index = console_url.find("//") - suffix_index = ( - console_url[protocol_index + 2 :].find("/") + protocol_index + 2 - ) - port_index = ( - console_url[protocol_index + 2 : suffix_index].find(":") - + protocol_index - + 2 + if protocol_index < 0 or port_index < 0 or suffix_index < 0: + return ( + -vimconn.HTTP_Internal_Server_Error, + "Unexpected response from VIM", ) - if protocol_index < 0 or port_index < 0 or suffix_index < 0: - return ( - -vimconn.HTTP_Internal_Server_Error, - "Unexpected response from VIM", - ) - - console_dict = { - "protocol": console_url[0:protocol_index], - "server": console_url[protocol_index + 2 : port_index], - "port": console_url[port_index:suffix_index], - "suffix": console_url[suffix_index + 1 :], - } - protocol_index += 2 + console_dict = { + "protocol": console_url[0:protocol_index], + "server": console_url[protocol_index + 2 : port_index], + "port": console_url[port_index:suffix_index], + "suffix": console_url[suffix_index + 1 :], + } + protocol_index += 2 - return console_dict - raise vimconn.VimConnUnexpectedResponse("Unexpected response from VIM") - except ( - nvExceptions.NotFound, - ksExceptions.ClientException, - nvExceptions.ClientException, - nvExceptions.BadRequest, - ConnectionError, - ) as e: - self._format_exception(e) + return console_dict + raise vimconn.VimConnUnexpectedResponse("Unexpected response from VIM") def _delete_ports_by_id_wth_neutron(self, k_id: str) -> None: """Neutron delete ports by id. @@ -2991,6 +2987,10 @@ class vimconnector(vimconn.VimConnector): try: self.neutron.delete_port(k_id) + except (neExceptions.ConnectionFailed, ConnectionError) as e: + self.logger.error("Error deleting port: {}: {}".format(type(e).__name__, e)) + # If there is connection error, raise. + self._format_exception(e) except Exception as e: self.logger.error("Error deleting port: {}: {}".format(type(e).__name__, e)) @@ -3036,7 +3036,7 @@ class vimconnector(vimconn.VimConnector): """ try: if k_id in volumes_to_hold: - return + return False if self.cinder.volumes.get(k_id).status != "available": return True @@ -3045,6 +3045,11 @@ class vimconnector(vimconn.VimConnector): self.cinder.volumes.delete(k_id) created_items[k] = None + except (cExceptions.ConnectionError, ConnectionError) as e: + self.logger.error( + "Error deleting volume: {}: {}".format(type(e).__name__, e) + ) + self._format_exception(e) except Exception as e: self.logger.error( "Error deleting volume: {}: {}".format(type(e).__name__, e) @@ -3061,6 +3066,11 @@ class vimconnector(vimconn.VimConnector): self.neutron.delete_floatingip(k_id) created_items[k] = None + except (neExceptions.ConnectionFailed, ConnectionError) as e: + self.logger.error( + "Error deleting floating ip: {}: {}".format(type(e).__name__, e) + ) + self._format_exception(e) except Exception as e: self.logger.error( "Error deleting floating ip: {}: {}".format(type(e).__name__, e) @@ -3086,6 +3096,11 @@ class vimconnector(vimconn.VimConnector): if k_item == "port": self._delete_ports_by_id_wth_neutron(k_id) + except (neExceptions.ConnectionFailed, ConnectionError) as e: + self.logger.error( + "Error deleting port: {}: {}".format(type(e).__name__, e) + ) + self._format_exception(e) except Exception as e: self.logger.error( "Error deleting port: {}: {}".format(type(e).__name__, e) @@ -3112,6 +3127,16 @@ class vimconnector(vimconn.VimConnector): elif k_item == "floating_ip": self._delete_floating_ip_by_id(k, k_id, created_items) + except ( + cExceptions.ConnectionError, + neExceptions.ConnectionFailed, + ConnectionError, + AttributeError, + TypeError, + ) as e: + self.logger.error("Error deleting {}: {}".format(k, e)) + self._format_exception(e) + except Exception as e: self.logger.error("Error deleting {}: {}".format(k, e)) @@ -3133,6 +3158,7 @@ class vimconnector(vimconn.VimConnector): if len(key.split(":")) == 2 } + @catch_any_exception def delete_vminstance( self, vm_id: str, created_items: dict = None, volumes_to_hold: list = None ) -> None: @@ -3177,14 +3203,9 @@ class vimconnector(vimconn.VimConnector): if keep_waiting: time.sleep(1) elapsed_time += 1 - - except ( - nvExceptions.NotFound, - ksExceptions.ClientException, - nvExceptions.ClientException, - ConnectionError, - ) as e: - self._format_exception(e) + except (nvExceptions.NotFound, nvExceptions.ResourceNotFound) as e: + # If VM does not exist, it does not raise + self.logger.warning(f"Error deleting VM: {vm_id} is not found, {str(e)}") def refresh_vms_status(self, vm_list): """Get the status of the virtual machines and their interfaces/ports @@ -3216,7 +3237,6 @@ class vimconnector(vimconn.VimConnector): self.logger.debug( "refresh_vms status: Getting tenant VM instance information from VIM" ) - for vm_id in vm_list: vm = {} @@ -3329,122 +3349,111 @@ class vimconnector(vimconn.VimConnector): return vm_dict + @catch_any_exception def action_vminstance(self, vm_id, action_dict, created_items={}): """Send and action over a VM instance from VIM Returns None or the console dict if the action was successfully sent to the VIM """ self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict)) - - try: - self._reload_connection() - server = self.nova.servers.find(id=vm_id) - - if "start" in action_dict: - if action_dict["start"] == "rebuild": - server.rebuild() - else: - if server.status == "PAUSED": - server.unpause() - elif server.status == "SUSPENDED": - server.resume() - elif server.status == "SHUTOFF": - server.start() - else: - self.logger.debug( - "ERROR : Instance is not in SHUTOFF/PAUSE/SUSPEND state" - ) - raise vimconn.VimConnException( - "Cannot 'start' instance while it is in active state", - http_code=vimconn.HTTP_Bad_Request, - ) - - elif "pause" in action_dict: - server.pause() - elif "resume" in action_dict: - server.resume() - elif "shutoff" in action_dict or "shutdown" in action_dict: - self.logger.debug("server status %s", server.status) - if server.status == "ACTIVE": - server.stop() + self._reload_connection() + server = self.nova.servers.find(id=vm_id) + if "start" in action_dict: + if action_dict["start"] == "rebuild": + server.rebuild() + else: + if server.status == "PAUSED": + server.unpause() + elif server.status == "SUSPENDED": + server.resume() + elif server.status == "SHUTOFF": + server.start() else: - self.logger.debug("ERROR: VM is not in Active state") - raise vimconn.VimConnException( - "VM is not in active state, stop operation is not allowed", - http_code=vimconn.HTTP_Bad_Request, + self.logger.debug( + "ERROR : Instance is not in SHUTOFF/PAUSE/SUSPEND state" ) - elif "forceOff" in action_dict: - server.stop() # TODO - elif "terminate" in action_dict: - server.delete() - elif "createImage" in action_dict: - server.create_image() - # "path":path_schema, - # "description":description_schema, - # "name":name_schema, - # "metadata":metadata_schema, - # "imageRef": id_schema, - # "disk": {"oneOf":[{"type": "null"}, {"type":"string"}] }, - elif "rebuild" in action_dict: - server.rebuild(server.image["id"]) - elif "reboot" in action_dict: - server.reboot() # reboot_type="SOFT" - elif "console" in action_dict: - console_type = action_dict["console"] - - if console_type is None or console_type == "novnc": - console_dict = server.get_vnc_console("novnc") - elif console_type == "xvpvnc": - console_dict = server.get_vnc_console(console_type) - elif console_type == "rdp-html5": - console_dict = server.get_rdp_console(console_type) - elif console_type == "spice-html5": - console_dict = server.get_spice_console(console_type) - else: raise vimconn.VimConnException( - "console type '{}' not allowed".format(console_type), + "Cannot 'start' instance while it is in active state", http_code=vimconn.HTTP_Bad_Request, ) + elif "pause" in action_dict: + server.pause() + elif "resume" in action_dict: + server.resume() + elif "shutoff" in action_dict or "shutdown" in action_dict: + self.logger.debug("server status %s", server.status) + if server.status == "ACTIVE": + server.stop() + else: + self.logger.debug("ERROR: VM is not in Active state") + raise vimconn.VimConnException( + "VM is not in active state, stop operation is not allowed", + http_code=vimconn.HTTP_Bad_Request, + ) + elif "forceOff" in action_dict: + server.stop() # TODO + elif "terminate" in action_dict: + server.delete() + elif "createImage" in action_dict: + server.create_image() + # "path":path_schema, + # "description":description_schema, + # "name":name_schema, + # "metadata":metadata_schema, + # "imageRef": id_schema, + # "disk": {"oneOf":[{"type": "null"}, {"type":"string"}] }, + elif "rebuild" in action_dict: + server.rebuild(server.image["id"]) + elif "reboot" in action_dict: + server.reboot() # reboot_type="SOFT" + elif "console" in action_dict: + console_type = action_dict["console"] - try: - console_url = console_dict["console"]["url"] - # parse console_url - protocol_index = console_url.find("//") - suffix_index = ( - console_url[protocol_index + 2 :].find("/") + protocol_index + 2 - ) - port_index = ( - console_url[protocol_index + 2 : suffix_index].find(":") - + protocol_index - + 2 - ) - - if protocol_index < 0 or port_index < 0 or suffix_index < 0: - raise vimconn.VimConnException( - "Unexpected response from VIM " + str(console_dict) - ) + if console_type is None or console_type == "novnc": + console_dict = server.get_vnc_console("novnc") + elif console_type == "xvpvnc": + console_dict = server.get_vnc_console(console_type) + elif console_type == "rdp-html5": + console_dict = server.get_rdp_console(console_type) + elif console_type == "spice-html5": + console_dict = server.get_spice_console(console_type) + else: + raise vimconn.VimConnException( + "console type '{}' not allowed".format(console_type), + http_code=vimconn.HTTP_Bad_Request, + ) - console_dict2 = { - "protocol": console_url[0:protocol_index], - "server": console_url[protocol_index + 2 : port_index], - "port": int(console_url[port_index + 1 : suffix_index]), - "suffix": console_url[suffix_index + 1 :], - } + try: + console_url = console_dict["console"]["url"] + # parse console_url + protocol_index = console_url.find("//") + suffix_index = ( + console_url[protocol_index + 2 :].find("/") + protocol_index + 2 + ) + port_index = ( + console_url[protocol_index + 2 : suffix_index].find(":") + + protocol_index + + 2 + ) - return console_dict2 - except Exception: + if protocol_index < 0 or port_index < 0 or suffix_index < 0: raise vimconn.VimConnException( "Unexpected response from VIM " + str(console_dict) ) - return None - except ( - ksExceptions.ClientException, - nvExceptions.ClientException, - nvExceptions.NotFound, - ConnectionError, - ) as e: - self._format_exception(e) - # TODO insert exception vimconn.HTTP_Unauthorized + console_dict2 = { + "protocol": console_url[0:protocol_index], + "server": console_url[protocol_index + 2 : port_index], + "port": int(console_url[port_index + 1 : suffix_index]), + "suffix": console_url[suffix_index + 1 :], + } + + return console_dict2 + except Exception: + raise vimconn.VimConnException( + "Unexpected response from VIM " + str(console_dict) + ) + + return None # ###### VIO Specific Changes ######### def _generate_vlanID(self): @@ -3649,6 +3658,7 @@ class vimconnector(vimconn.VimConnector): return error_value, error_text + @catch_any_exception def new_affinity_group(self, affinity_group_data): """Adds a server group to VIM affinity_group_data contains a dictionary with information, keys: @@ -3657,55 +3667,29 @@ class vimconnector(vimconn.VimConnector): scope: Only nfvi-node allowed Returns the server group identifier""" self.logger.debug("Adding Server Group '%s'", str(affinity_group_data)) + name = affinity_group_data["name"] + policy = affinity_group_data["type"] + self._reload_connection() + new_server_group = self.nova.server_groups.create(name, policy) + return new_server_group.id - try: - name = affinity_group_data["name"] - policy = affinity_group_data["type"] - - self._reload_connection() - new_server_group = self.nova.server_groups.create(name, policy) - - return new_server_group.id - except ( - ksExceptions.ClientException, - nvExceptions.ClientException, - ConnectionError, - KeyError, - ) as e: - self._format_exception(e) - + @catch_any_exception def get_affinity_group(self, affinity_group_id): """Obtain server group details from the VIM. Returns the server group detais as a dict""" self.logger.debug("Getting flavor '%s'", affinity_group_id) - try: - self._reload_connection() - server_group = self.nova.server_groups.find(id=affinity_group_id) - - return server_group.to_dict() - except ( - nvExceptions.NotFound, - nvExceptions.ClientException, - ksExceptions.ClientException, - ConnectionError, - ) as e: - self._format_exception(e) + self._reload_connection() + server_group = self.nova.server_groups.find(id=affinity_group_id) + return server_group.to_dict() + @catch_any_exception def delete_affinity_group(self, affinity_group_id): """Deletes a server group from the VIM. Returns the old affinity_group_id""" self.logger.debug("Getting server group '%s'", affinity_group_id) - try: - self._reload_connection() - self.nova.server_groups.delete(affinity_group_id) - - return affinity_group_id - except ( - nvExceptions.NotFound, - ksExceptions.ClientException, - nvExceptions.ClientException, - ConnectionError, - ) as e: - self._format_exception(e) + self._reload_connection() + self.nova.server_groups.delete(affinity_group_id) + return affinity_group_id + @catch_any_exception def get_vdu_state(self, vm_id, host_is_required=False) -> list: """Getting the state of a VDU. Args: @@ -3717,24 +3701,20 @@ class vimconnector(vimconn.VimConnector): """ self.logger.debug("Getting the status of VM") self.logger.debug("VIM VM ID %s", vm_id) - try: - self._reload_connection() - server_dict = self._find_nova_server(vm_id) - srv_attr = "OS-EXT-SRV-ATTR:host" - host_info = ( - server_dict[srv_attr] if host_is_required else server_dict.get(srv_attr) - ) - vdu_data = [ - server_dict["status"], - server_dict["flavor"]["id"], - host_info, - server_dict["OS-EXT-AZ:availability_zone"], - ] - self.logger.debug("vdu_data %s", vdu_data) - return vdu_data - - except Exception as e: - self._format_exception(e) + self._reload_connection() + server_dict = self._find_nova_server(vm_id) + srv_attr = "OS-EXT-SRV-ATTR:host" + host_info = ( + server_dict[srv_attr] if host_is_required else server_dict.get(srv_attr) + ) + vdu_data = [ + server_dict["status"], + server_dict["flavor"]["id"], + host_info, + server_dict["OS-EXT-AZ:availability_zone"], + ] + self.logger.debug("vdu_data %s", vdu_data) + return vdu_data def check_compute_availability(self, host, server_flavor_details): self._reload_connection() @@ -3792,6 +3772,7 @@ class vimconnector(vimconn.VimConnector): az_check["zone_check"] = True return az_check + @catch_any_exception def migrate_instance(self, vm_id, compute_host=None): """ Migrate a vdu @@ -3805,78 +3786,72 @@ class vimconnector(vimconn.VimConnector): server_flavor_id = instance_state[1] server_hypervisor_name = instance_state[2] server_availability_zone = instance_state[3] - try: - server_flavor = self.nova.flavors.find(id=server_flavor_id).to_dict() - server_flavor_details = [ - server_flavor["ram"], - server_flavor["disk"], - server_flavor["vcpus"], - ] - if compute_host == server_hypervisor_name: - raise vimconn.VimConnException( - "Unable to migrate instance '{}' to the same host '{}'".format( - vm_id, compute_host - ), - http_code=vimconn.HTTP_Bad_Request, - ) - az_status = self.check_availability_zone( - server_availability_zone, - server_flavor_details, - server_hypervisor_name, - compute_host, + server_flavor = self.nova.flavors.find(id=server_flavor_id).to_dict() + server_flavor_details = [ + server_flavor["ram"], + server_flavor["disk"], + server_flavor["vcpus"], + ] + if compute_host == server_hypervisor_name: + raise vimconn.VimConnException( + "Unable to migrate instance '{}' to the same host '{}'".format( + vm_id, compute_host + ), + http_code=vimconn.HTTP_Bad_Request, ) - availability_zone_check = az_status["zone_check"] - available_compute_id = az_status.get("compute_availability") + az_status = self.check_availability_zone( + server_availability_zone, + server_flavor_details, + server_hypervisor_name, + compute_host, + ) + availability_zone_check = az_status["zone_check"] + available_compute_id = az_status.get("compute_availability") - if availability_zone_check is False: - raise vimconn.VimConnException( - "Unable to migrate instance '{}' to a different availability zone".format( - vm_id - ), - http_code=vimconn.HTTP_Bad_Request, - ) - if available_compute_id is not None: - # disk_over_commit parameter for live_migrate method is not valid for Nova API version >= 2.25 - self.nova.servers.live_migrate( - server=vm_id, - host=available_compute_id, - block_migration=True, - ) - state = "MIGRATING" - changed_compute_host = "" - if state == "MIGRATING": - vm_state = self.__wait_for_vm(vm_id, "ACTIVE") - changed_compute_host = self.get_vdu_state( - vm_id, host_is_required=True - )[2] - if vm_state and changed_compute_host == available_compute_id: - self.logger.debug( - "Instance '{}' migrated to the new compute host '{}'".format( - vm_id, changed_compute_host - ) - ) - return state, available_compute_id - else: - raise vimconn.VimConnException( - "Migration Failed. Instance '{}' not moved to the new host {}".format( - vm_id, available_compute_id - ), - http_code=vimconn.HTTP_Bad_Request, + if availability_zone_check is False: + raise vimconn.VimConnException( + "Unable to migrate instance '{}' to a different availability zone".format( + vm_id + ), + http_code=vimconn.HTTP_Bad_Request, + ) + if available_compute_id is not None: + # disk_over_commit parameter for live_migrate method is not valid for Nova API version >= 2.25 + self.nova.servers.live_migrate( + server=vm_id, + host=available_compute_id, + block_migration=True, + ) + state = "MIGRATING" + changed_compute_host = "" + if state == "MIGRATING": + vm_state = self.__wait_for_vm(vm_id, "ACTIVE") + changed_compute_host = self.get_vdu_state(vm_id, host_is_required=True)[ + 2 + ] + if vm_state and changed_compute_host == available_compute_id: + self.logger.debug( + "Instance '{}' migrated to the new compute host '{}'".format( + vm_id, changed_compute_host ) + ) + return state, available_compute_id else: raise vimconn.VimConnException( - "Compute '{}' not available or does not have enough resources to migrate the instance".format( - available_compute_id + "Migration Failed. Instance '{}' not moved to the new host {}".format( + vm_id, available_compute_id ), http_code=vimconn.HTTP_Bad_Request, ) - except ( - nvExceptions.BadRequest, - nvExceptions.ClientException, - nvExceptions.NotFound, - ) as e: - self._format_exception(e) + else: + raise vimconn.VimConnException( + "Compute '{}' not available or does not have enough resources to migrate the instance".format( + available_compute_id + ), + http_code=vimconn.HTTP_Bad_Request, + ) + @catch_any_exception def resize_instance(self, vm_id, new_flavor_id): """ For resizing the vm based on the given @@ -3891,37 +3866,30 @@ class vimconnector(vimconn.VimConnector): 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: + 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( - 400, - message="Server disk resize failed. Resize to lower disk flavor is not allowed", + 409, + message="Cannot 'resize' vm_state is in ERROR", ) - 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) + 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", + ) def confirm_resize(self, vm_id): """ @@ -3951,11 +3919,7 @@ class vimconnector(vimconn.VimConnector): self.logger.warning(str(e.message)) all_ports = self.neutron.list_ports() return all_servers, all_ports - except ( - vimconn.VimConnException, - vimconn.VimConnNotFoundException, - vimconn.VimConnConnectionException, - ) as e: + except Exception as e: raise vimconn.VimConnException( f"Exception in monitoring while getting VMs and ports status: {str(e)}" ) diff --git a/releasenotes/notes/fix_bug_2158_2254-86a20d2b2a6ca5a9.yaml b/releasenotes/notes/fix_bug_2158_2254-86a20d2b2a6ca5a9.yaml new file mode 100644 index 00000000..95715d93 --- /dev/null +++ b/releasenotes/notes/fix_bug_2158_2254-86a20d2b2a6ca5a9.yaml @@ -0,0 +1,23 @@ +####################################################################################### +# 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. +####################################################################################### +--- +fixes: + - | + Fix Bug 2158 If VIM is not reachable NS delete operation should fail and Bug 2254 + VNF scale up operation should fail if operation is not completed by arranging exceptions + + -- 2.25.1