Validate flavor image min ram when resize volume-backed instance

When resize instance, the flavors returned may not meet the image
minimum memory requirement, resizing instance ignores the minimum
memory limit of the image, which may cause the resizing be
successfully, but the instance fails to start because the memory is
too small to run the system.

Related-Bug: 2007968
Change-Id: I132e444eedc10b950a2fc9ed259cd6d9aa9bed65
This commit is contained in:
zhong.zhou 2024-02-03 23:58:41 +08:00
parent b434b42761
commit f3eb76e57b
3 changed files with 48 additions and 2 deletions

View File

@ -4269,6 +4269,12 @@ class API:
if volume_backed:
self._validate_flavor_image_numa_pci(
image, new_flavor, validate_pci=True)
# The server that image-backed already has the verification of
# image min_ram when calling _validate_flavor_image_nostatus.
# Here, the verification is added for the server that
# volume-backed.
if new_flavor['memory_mb'] < int(image.get('min_ram', 0)):
raise exception.FlavorMemoryTooSmall()
else:
self._validate_flavor_image_nostatus(
context, image, new_flavor, root_bdm=None,

View File

@ -13,6 +13,7 @@
import fixtures
from nova.tests.fixtures import libvirt as fakelibvirt
from nova.tests.functional.api import client
from nova.tests.functional.libvirt import base
@ -56,5 +57,11 @@ class Bug2007968RegressionTest(base.ServersTestBase):
# This can cause the instance for boot from volume to be allowed to
# get to the resize verify status, but the instance's application runs
# abnormally due to insufficient memory, and it may be killed by OOM.
server = self._resize_server(server, new_flavor)
self.assertEqual(server['status'], 'VERIFY_RESIZE')
# After the fix, compute api will directly raise FlavorMemoryTooSmall
# and will not continue the resize.
ex = self.assertRaises(client.OpenStackApiException,
self._resize_server, server, new_flavor)
self.assertEqual(400, ex.response.status_code)
self.assertIn('Flavor\'s memory is too small for requested image.',
ex.response.text)

View File

@ -2376,6 +2376,39 @@ class _ComputeAPIUnitTestMixIn(object):
mock_resize.assert_not_called()
mock_save.assert_not_called()
@mock.patch('nova.compute.utils.is_volume_backed_instance',
return_value=True)
@mock.patch('nova.servicegroup.api.API.service_is_up',
new=mock.Mock(return_value=True))
@mock.patch.object(objects.Instance, 'save')
@mock.patch.object(compute_api.API, '_record_action_start')
@mock.patch.object(quotas_obj.Quotas, 'limit_check_project_and_user')
@mock.patch.object(quotas_obj.Quotas, 'count_as_dict')
@mock.patch.object(flavors, 'get_flavor_by_flavor_id')
def test_resize_vol_backed_smaller_min_ram(self, mock_get_flavor,
mock_count, mock_limit,
mock_record, mock_save,
mock_is_vol_backed):
mock_resize = self.useFixture(fixtures.MockPatchObject(
self.compute_api.compute_task_api, 'resize_instance')).mock
# Resize down from 512 MB to 64 MB.
params = dict(image_ref='', system_metadata={'min_ram': 512})
fake_inst = self._create_instance_obj(params=params)
new_flavor = self._create_flavor(id=200, flavorid=200,
name='new_flavor', disabled=False,
memory_mb=64)
mock_get_flavor.return_value = new_flavor
self.assertRaises(exception.FlavorMemoryTooSmall,
self.compute_api.resize, self.context,
fake_inst, flavor_id=new_flavor.id)
mock_get_flavor.assert_called_once_with(200, read_deleted='no')
# Should never reach these.
mock_count.assert_not_called()
mock_limit.assert_not_called()
mock_record.assert_not_called()
mock_resize.assert_not_called()
mock_save.assert_not_called()
@mock.patch('nova.servicegroup.api.API.service_is_up',
new=mock.Mock(return_value=True))
@mock.patch.object(objects.Instance, 'save')