Merge "Update Nova bdm with updated swap info"

This commit is contained in:
Zuul 2024-12-12 05:48:06 +00:00 committed by Gerrit Code Review
commit 8de8fe8c1a
4 changed files with 242 additions and 4 deletions

View File

@ -5530,6 +5530,50 @@ class ComputeManager(manager.Manager):
'Error: %s', bdm.attachment_id, str(e),
instance_uuid=bdm.instance_uuid)
def _update_bdm_for_swap_to_finish_resize(
self, context, instance, confirm=True):
"""This updates bdm.swap with new swap info"""
bdms = instance.get_bdms()
if not (instance.old_flavor and instance.new_flavor):
return bdms
if instance.old_flavor.swap == instance.new_flavor.swap:
return bdms
old_swap = instance.old_flavor.swap
new_swap = instance.new_flavor.swap
if not confirm:
# revert flavor on _finish_revert_resize
old_swap = instance.new_flavor.swap
new_swap = instance.old_flavor.swap
# add swap
if old_swap == 0 and new_swap:
# (auniyal)old_swap = 0 means we did not have swap bdm
# for this instance.
# and as there is a new_swap, its a swap addition
new_swap_bdm = block_device.create_blank_bdm(new_swap, 'swap')
bdm_obj = objects.BlockDeviceMapping(
context, instance_uuid=instance.uuid, **new_swap_bdm)
bdm_obj.update_or_create()
return instance.get_bdms()
# update swap
for bdm in bdms:
if bdm.guest_format == 'swap' and bdm.device_type == 'disk':
if new_swap > 0:
LOG.info('Adding swap BDM.', instance=instance)
bdm.volume_size = new_swap
bdm.save()
break
elif new_swap == 0:
LOG.info('Deleting swap BDM.', instance=instance)
bdm.destroy()
bdms.objects.remove(bdm)
break
return bdms
@wrap_exception()
@reverts_task_state
@wrap_instance_event(prefix='compute')
@ -5872,8 +5916,9 @@ class ComputeManager(manager.Manager):
):
"""Inner version of finish_revert_resize."""
with self._error_out_instance_on_exception(context, instance):
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
context, instance.uuid)
bdms = self._update_bdm_for_swap_to_finish_resize(
context, instance, confirm=False)
self._notify_about_instance_usage(
context, instance, "resize.revert.start")
compute_utils.notify_about_instance_action(context, instance,
@ -6815,8 +6860,7 @@ class ComputeManager(manager.Manager):
The caller must revert the instance's allocations if the migration
process failed.
"""
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
context, instance.uuid)
bdms = self._update_bdm_for_swap_to_finish_resize(context, instance)
with self._error_out_instance_on_exception(context, instance):
image_meta = objects.ImageMeta.from_dict(image)

View File

@ -3283,6 +3283,73 @@ class ServerMovingTests(integrated_helpers.ProviderUsageBaseTestCase):
self._test_resize_to_same_host_instance_fails(
'_finish_resize', 'compute_finish_resize')
def _verify_swap_resize_in_bdm(self, server_id, swap_size):
"""Verify swap dev in BDM"""
ctxt = context.get_admin_context()
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
ctxt, server_id)
if swap_size != 0:
self.assertIn('swap', [bdm.guest_format for bdm in bdms])
swaps = [
bdm.volume_size for bdm in bdms if bdm.guest_format == 'swap']
self.assertEqual(len(swaps), 1)
self.assertIn(swap_size, swaps)
else:
self.assertNotIn('swap', [bdm.guest_format for bdm in bdms])
def _test_swap_resize(self, swap1, swap2, confirm=True):
fl_1 = self._create_flavor(swap=swap1)
fl_2 = self._create_flavor(swap=swap2)
server = self._create_server(flavor_id=fl_1, networks=[])
# before resize
self.assertEqual(server['flavor']['swap'], swap1)
server = self._resize_server(server, fl_2)
self.assertEqual(server['flavor']['swap'], swap2)
self._verify_swap_resize_in_bdm(server['id'], swap2)
if confirm:
server = self._confirm_resize(server)
# after resize
self.assertEqual(server['flavor']['swap'], swap2)
# verify block device mapping
self._verify_swap_resize_in_bdm(server['id'], swap2)
else:
server = self._revert_resize(server)
# after revert
self.assertEqual(server['flavor']['swap'], swap1)
# verify block device mapping
self._verify_swap_resize_in_bdm(server['id'], swap1)
def test_swap_expand_0_to_0_confirm(self):
self._test_swap_resize(0, 0)
def test_swap_expand_0_to_1024_confirm(self):
self._test_swap_resize(0, 1024)
def test_swap_expand_0_to_1024_revert(self):
self._test_swap_resize(0, 1024, confirm=False)
def test_swap_expand_1024_to_2048_confirm(self):
self._test_swap_resize(1024, 2048)
def test_swap_expand_1024_to_2048_revert(self):
self._test_swap_resize(1024, 2048, confirm=False)
def test_swap_expand_2048_to_2048_confirm(self):
self._test_swap_resize(2048, 2048)
def test_swap_shrink_1024_to_0_confirm(self):
self._test_swap_resize(1024, 0)
def test_swap_shrink_1024_to_0_revert(self):
self._test_swap_resize(1024, 0, confirm=False)
def test_swap_shrink_2048_to_1024_confirm(self):
self._test_swap_resize(2048, 1024)
def test_swap_shrink_2048_to_1024_revert(self):
self._test_swap_resize(2048, 1024, confirm=False)
def _server_created_with_host(self):
hostname = self.compute1.host
server_req = self._build_server(

View File

@ -14658,3 +14658,120 @@ class ComputeManagerSetHostEnabledTestCase(test.NoDBTestCase):
self.assertIn('An error occurred while updating '
'COMPUTE_STATUS_DISABLED trait',
m_exc.call_args_list[0][0][0])
class ComputeManagerBDMUpdateTestCase(test.TestCase):
def setUp(self):
super(ComputeManagerBDMUpdateTestCase, self).setUp()
self.compute = manager.ComputeManager()
self.context = context.RequestContext(fakes.FAKE_USER_ID,
fakes.FAKE_PROJECT_ID)
self.instance = mock.Mock()
@mock.patch('nova.block_device.create_blank_bdm')
@mock.patch('nova.objects.BlockDeviceMapping')
def test_no_flavor_change(self, mock_bdm_obj, mock_create_bdm):
self.instance.get_bdms.return_value = []
self.instance.old_flavor = None
self.instance.new_flavor = None
bdms = self.compute._update_bdm_for_swap_to_finish_resize(
self.context, self.instance)
self.assertEqual(bdms, [])
self.instance.get_bdms.assert_called_once()
@mock.patch('nova.block_device.create_blank_bdm')
@mock.patch('nova.objects.BlockDeviceMapping.create')
def test_no_swap_change(self, mock_bdm_obj, mock_create_bdm):
self.instance.old_flavor = mock.Mock(swap=1024)
self.instance.new_flavor = mock.Mock(swap=1024)
existing_swap_bdm = objects.BlockDeviceMapping(
guest_format='swap',
device_type='disk',
volume_size=1024)
bdms = objects.BlockDeviceMappingList(objects=[existing_swap_bdm])
self.instance.get_bdms.return_value = bdms
new_bdms = self.compute._update_bdm_for_swap_to_finish_resize(
self.context, self.instance)
self.assertEqual(new_bdms, bdms)
self.instance.get_bdms.assert_called_once()
@mock.patch('nova.block_device.create_blank_bdm')
@mock.patch('nova.objects.BlockDeviceMapping')
def test_add_new_swap_bdm(self, mock_bdm_obj, mock_create_bdm):
self.instance.old_flavor = mock.Mock(swap=0)
self.instance.new_flavor = mock.Mock(swap=1024)
self.instance.get_bdms.return_value = []
new_swap_bdm = {
'guest_format': 'swap',
'device_type': 'disk',
'volume_size': 1024}
mock_create_bdm.return_value = new_swap_bdm
mock_bdm_instance = mock_bdm_obj.return_value
self.compute._update_bdm_for_swap_to_finish_resize(
self.context, self.instance)
mock_bdm_obj.assert_called_once_with(
self.context, instance_uuid=self.instance.uuid, **new_swap_bdm
)
mock_bdm_instance.update_or_create.assert_called_once()
# called twice
self.assertEqual(self.instance.get_bdms.call_count, 2)
@mock.patch('nova.block_device.create_blank_bdm')
@mock.patch('nova.objects.BlockDeviceMapping.create')
@mock.patch('nova.objects.BlockDeviceMapping.save')
def test_update_swap_bdm(
self, mock_bdm_save, mock_bdm_create,
mock_create_blank_bdm):
self.instance.old_flavor = mock.Mock(swap=1024) # Existing swap size
self.instance.new_flavor = mock.Mock(swap=2048) # New swap size
existing_swap_bdm = objects.BlockDeviceMapping(
guest_format='swap',
device_type='disk',
volume_size=1024)
bdms = objects.BlockDeviceMappingList(objects=[existing_swap_bdm])
self.instance.get_bdms.return_value = bdms
self.compute._update_bdm_for_swap_to_finish_resize(
self.context, self.instance)
# assert bdm get saved in DB
existing_swap_bdm.save.assert_called_once()
# here we are returning same bdms object, not freh from DB
self.assertEqual(existing_swap_bdm.volume_size, 2048)
# get_bdms is called only once
self.assertEqual(self.instance.get_bdms.call_count, 1)
@mock.patch('nova.block_device.create_blank_bdm')
@mock.patch('nova.objects.BlockDeviceMapping.destroy')
def test_delete_swap_bdm(self, mock_bdm_destroy, mock_create_bdm):
self.instance.old_flavor = mock.Mock(swap=1024)
self.instance.new_flavor = mock.Mock(swap=0)
existing_swap_bdm = objects.BlockDeviceMapping(
guest_format='swap',
device_type='disk',
volume_size=1024)
self.instance.get_bdms.return_value = objects.BlockDeviceMappingList(
objects=[existing_swap_bdm])
self.compute._update_bdm_for_swap_to_finish_resize(
self.context, self.instance)
mock_bdm_destroy.assert_called_once()
self.instance.get_bdms.assert_called_once()

View File

@ -0,0 +1,10 @@
---
fixes:
- |
With this change, operators can now resize the instance flavor swap to
a smaller swap size, it can be expand and shrunk down to 0 using the same
resize API.
For more details see: `bug 1552777`_
.. _`bug 1552777`: https://bugs.launchpad.net/nova/+bug/1552777