Add get_flavor_id method to nova_helper

The get_flavor_id method is extracted from resize_instance to
improve code reusability. That method will be used in the
pre_condition of resize action.

Add comprehensive unit tests for the get_flavor_id method in
nova_helper.

Assisted-By: Claude (Sonnet 4.5)
Change-Id: I31c60521e98ea223bd1b66d3fe66f3212e1036a9
Signed-off-by: Alfredo Moralejo <amoralej@redhat.com>
This commit is contained in:
Alfredo Moralejo
2026-02-23 12:28:55 +01:00
parent 67b0be383e
commit 64ecc88972
2 changed files with 155 additions and 9 deletions

View File

@@ -569,6 +569,28 @@ class NovaHelper:
flavors = self.connection.compute.flavors(is_public=None)
return [Flavor.from_openstacksdk(f) for f in flavors]
def get_flavor_id(self, flavor):
"""Get the flavor id for a given flavor name or id.
:param flavor: the name or id of the flavor to get
:returns: the flavor id if found
:raises: ComputeResourceNotFound if no flavor was found
:raises: NovaClientError if there is any problem while calling the
Nova api
"""
try:
flavor_obj = self._get_flavor(flavor)
return flavor_obj.id
except exception.ComputeResourceNotFound:
flavor_id = next((f.id for f in self.get_flavor_list() if
f.flavor_name == flavor), None)
if flavor_id:
return flavor_id
msg = f"{flavor} of type Flavor"
raise exception.ComputeResourceNotFound(msg)
@nova_retries
@handle_nova_error("Flavor")
def _get_flavor(self, flavor):
@@ -873,18 +895,13 @@ class NovaHelper:
flavor_id = None
try:
try:
flavor_obj = self._get_flavor(flavor)
flavor_id = flavor_obj.id
except exception.ComputeResourceNotFound:
flavor_id = next((f.id for f in self.get_flavor_list() if
f.flavor_name == flavor), None)
flavor_id = self.get_flavor_id(flavor)
except exception.ComputeResourceNotFound:
LOG.debug("Flavor not found: %s, could not resize", flavor)
return False
except exception.NovaClientError as e:
LOG.debug("Nova client exception occurred while resizing "
"instance %s. Exception: %s", instance_id, e)
if not flavor_id:
LOG.debug("Flavor not found: %s, could not resize", flavor)
return False
instance_status = instance.vm_state

View File

@@ -1308,6 +1308,135 @@ class TestNovaHelper(test_utils.NovaResourcesMixin, base.TestCase):
# Verify that the result is cached
self.assertIsNotNone(nova_util._is_pinned_az_available)
def test_get_flavor_id_by_id(self, mock_cinder):
"""Test get_flavor_id returns id when flavor is found by ID."""
nova_util = nova_helper.NovaHelper()
flavor_id = 'flavor-123'
flavor = self.create_openstacksdk_flavor(
id=flavor_id, name='m1.small'
)
self.mock_connection.compute.get_flavor.return_value = flavor
result = nova_util.get_flavor_id(flavor_id)
self.assertEqual(flavor_id, result)
self.mock_connection.compute.get_flavor.assert_called_once_with(
flavor_id
)
def test_get_flavor_id_by_name(self, mock_cinder):
"""Test get_flavor_id returns id when flavor is found by name."""
nova_util = nova_helper.NovaHelper()
flavor_name = 'm1.small'
flavor_id = 'flavor-123'
flavor = self.create_openstacksdk_flavor(
id=flavor_id, name=flavor_name
)
# First attempt to get by ID fails (NotFoundException)
self.mock_connection.compute.get_flavor.side_effect = (
sdk_exc.NotFoundException()
)
# get_flavor_list returns list with the flavor
self.mock_connection.compute.flavors.return_value = [flavor]
result = nova_util.get_flavor_id(flavor_name)
self.assertEqual(flavor_id, result)
self.mock_connection.compute.get_flavor.assert_called_once_with(
flavor_name
)
self.mock_connection.compute.flavors.assert_called_once_with(
is_public=None
)
def test_get_flavor_id_not_found_by_id_or_name(self, mock_cinder):
"""Test get_flavor_id raises exception when flavor is not found."""
nova_util = nova_helper.NovaHelper()
flavor_name = 'nonexistent-flavor'
# First attempt to get by ID fails
self.mock_connection.compute.get_flavor.side_effect = (
sdk_exc.NotFoundException()
)
# get_flavor_list returns empty list
self.mock_connection.compute.flavors.return_value = []
self.assertRaisesRegex(
exception.ComputeResourceNotFound,
f"{flavor_name} of type Flavor",
nova_util.get_flavor_id,
flavor_name
)
def test_get_flavor_id_not_found_in_list(self, mock_cinder):
"""Test get_flavor_id when flavor name not in returned list."""
nova_util = nova_helper.NovaHelper()
flavor_name = 'm1.small'
# First attempt to get by ID fails
self.mock_connection.compute.get_flavor.side_effect = (
sdk_exc.NotFoundException()
)
# get_flavor_list returns flavors but none match the name
other_flavor = self.create_openstacksdk_flavor(
id='other-id', name='m1.large'
)
self.mock_connection.compute.flavors.return_value = [other_flavor]
self.assertRaisesRegex(
exception.ComputeResourceNotFound,
f"{flavor_name} of type Flavor",
nova_util.get_flavor_id,
flavor_name
)
def test_get_flavor_id_sdk_exception(self, mock_cinder):
"""Test get_flavor_id raises NovaClientError on SDK exception."""
nova_util = nova_helper.NovaHelper()
flavor_id = 'flavor-123'
# SDK raises a generic exception
self.mock_connection.compute.get_flavor.side_effect = (
sdk_exc.SDKException("Connection error")
)
self.assertRaises(
exception.NovaClientError,
nova_util.get_flavor_id,
flavor_id
)
def test_get_flavor_id_by_name_multiple_flavors(self, mock_cinder):
"""Test get_flavor_id finds correct flavor by name in list."""
nova_util = nova_helper.NovaHelper()
flavor_name = 'm1.medium'
target_id = 'flavor-456'
# Create multiple flavors
flavor1 = self.create_openstacksdk_flavor(
id='flavor-123', name='m1.small'
)
flavor2 = self.create_openstacksdk_flavor(
id=target_id, name=flavor_name
)
flavor3 = self.create_openstacksdk_flavor(
id='flavor-789', name='m1.large'
)
# First attempt to get by ID fails
self.mock_connection.compute.get_flavor.side_effect = (
sdk_exc.NotFoundException()
)
# get_flavor_list returns multiple flavors
self.mock_connection.compute.flavors.return_value = [
flavor1, flavor2, flavor3
]
result = nova_util.get_flavor_id(flavor_name)
self.assertEqual(target_id, result)
class TestNovaRetries(base.TestCase):
"""Test suite for the nova_retries decorator."""