Skip to content

Commit 15dccae

Browse files
committed
Update Nova bdm with updated swap info
This change updates bdm for swap in finish_resize and revert_resize functionality. Change also adds supporting: - unit and functional tests - docs: releasenote Closes-Bug: #1552777 Change-Id: Ic8aaa0728a43936cd4c6e1ed590e01ba8f0fbf5b
1 parent 3b530ac commit 15dccae

File tree

4 files changed

+242
-4
lines changed

4 files changed

+242
-4
lines changed

nova/compute/manager.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5095,6 +5095,50 @@ def _delete_volume_attachments(self, ctxt, bdms):
50955095
'Error: %s', bdm.attachment_id, str(e),
50965096
instance_uuid=bdm.instance_uuid)
50975097

5098+
def _update_bdm_for_swap_to_finish_resize(
5099+
self, context, instance, confirm=True):
5100+
"""This updates bdm.swap with new swap info"""
5101+
5102+
bdms = instance.get_bdms()
5103+
if not (instance.old_flavor and instance.new_flavor):
5104+
return bdms
5105+
5106+
if instance.old_flavor.swap == instance.new_flavor.swap:
5107+
return bdms
5108+
5109+
old_swap = instance.old_flavor.swap
5110+
new_swap = instance.new_flavor.swap
5111+
if not confirm:
5112+
# revert flavor on _finish_revert_resize
5113+
old_swap = instance.new_flavor.swap
5114+
new_swap = instance.old_flavor.swap
5115+
5116+
# add swap
5117+
if old_swap == 0 and new_swap:
5118+
# (auniyal)old_swap = 0 means we did not have swap bdm
5119+
# for this instance.
5120+
# and as there is a new_swap, its a swap addition
5121+
new_swap_bdm = block_device.create_blank_bdm(new_swap, 'swap')
5122+
bdm_obj = objects.BlockDeviceMapping(
5123+
context, instance_uuid=instance.uuid, **new_swap_bdm)
5124+
bdm_obj.update_or_create()
5125+
return instance.get_bdms()
5126+
5127+
# update swap
5128+
for bdm in bdms:
5129+
if bdm.guest_format == 'swap' and bdm.device_type == 'disk':
5130+
if new_swap > 0:
5131+
LOG.info('Adding swap BDM.', instance=instance)
5132+
bdm.volume_size = new_swap
5133+
bdm.save()
5134+
break
5135+
elif new_swap == 0:
5136+
LOG.info('Deleting swap BDM.', instance=instance)
5137+
bdm.destroy()
5138+
bdms.objects.remove(bdm)
5139+
break
5140+
return bdms
5141+
50985142
@wrap_exception()
50995143
@reverts_task_state
51005144
@wrap_instance_event(prefix='compute')
@@ -5437,8 +5481,9 @@ def _finish_revert_resize(
54375481
):
54385482
"""Inner version of finish_revert_resize."""
54395483
with self._error_out_instance_on_exception(context, instance):
5440-
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
5441-
context, instance.uuid)
5484+
bdms = self._update_bdm_for_swap_to_finish_resize(
5485+
context, instance, confirm=False)
5486+
54425487
self._notify_about_instance_usage(
54435488
context, instance, "resize.revert.start")
54445489
compute_utils.notify_about_instance_action(context, instance,
@@ -6380,8 +6425,7 @@ def _finish_resize_helper(self, context, disk_info, image, instance,
63806425
The caller must revert the instance's allocations if the migration
63816426
process failed.
63826427
"""
6383-
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
6384-
context, instance.uuid)
6428+
bdms = self._update_bdm_for_swap_to_finish_resize(context, instance)
63856429

63866430
with self._error_out_instance_on_exception(context, instance):
63876431
image_meta = objects.ImageMeta.from_dict(image)

nova/tests/functional/test_servers.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3283,6 +3283,73 @@ def test_finish_resize_fails_allocation_cleanup(self):
32833283
self._test_resize_to_same_host_instance_fails(
32843284
'_finish_resize', 'compute_finish_resize')
32853285

3286+
def _verify_swap_resize_in_bdm(self, server_id, swap_size):
3287+
"""Verify swap dev in BDM"""
3288+
ctxt = context.get_admin_context()
3289+
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
3290+
ctxt, server_id)
3291+
if swap_size != 0:
3292+
self.assertIn('swap', [bdm.guest_format for bdm in bdms])
3293+
swaps = [
3294+
bdm.volume_size for bdm in bdms if bdm.guest_format == 'swap']
3295+
self.assertEqual(len(swaps), 1)
3296+
self.assertIn(swap_size, swaps)
3297+
else:
3298+
self.assertNotIn('swap', [bdm.guest_format for bdm in bdms])
3299+
3300+
def _test_swap_resize(self, swap1, swap2, confirm=True):
3301+
fl_1 = self._create_flavor(swap=swap1)
3302+
fl_2 = self._create_flavor(swap=swap2)
3303+
server = self._create_server(flavor_id=fl_1, networks=[])
3304+
# before resize
3305+
self.assertEqual(server['flavor']['swap'], swap1)
3306+
server = self._resize_server(server, fl_2)
3307+
self.assertEqual(server['flavor']['swap'], swap2)
3308+
self._verify_swap_resize_in_bdm(server['id'], swap2)
3309+
3310+
if confirm:
3311+
server = self._confirm_resize(server)
3312+
# after resize
3313+
self.assertEqual(server['flavor']['swap'], swap2)
3314+
# verify block device mapping
3315+
self._verify_swap_resize_in_bdm(server['id'], swap2)
3316+
else:
3317+
server = self._revert_resize(server)
3318+
# after revert
3319+
self.assertEqual(server['flavor']['swap'], swap1)
3320+
# verify block device mapping
3321+
self._verify_swap_resize_in_bdm(server['id'], swap1)
3322+
3323+
def test_swap_expand_0_to_0_confirm(self):
3324+
self._test_swap_resize(0, 0)
3325+
3326+
def test_swap_expand_0_to_1024_confirm(self):
3327+
self._test_swap_resize(0, 1024)
3328+
3329+
def test_swap_expand_0_to_1024_revert(self):
3330+
self._test_swap_resize(0, 1024, confirm=False)
3331+
3332+
def test_swap_expand_1024_to_2048_confirm(self):
3333+
self._test_swap_resize(1024, 2048)
3334+
3335+
def test_swap_expand_1024_to_2048_revert(self):
3336+
self._test_swap_resize(1024, 2048, confirm=False)
3337+
3338+
def test_swap_expand_2048_to_2048_confirm(self):
3339+
self._test_swap_resize(2048, 2048)
3340+
3341+
def test_swap_shrink_1024_to_0_confirm(self):
3342+
self._test_swap_resize(1024, 0)
3343+
3344+
def test_swap_shrink_1024_to_0_revert(self):
3345+
self._test_swap_resize(1024, 0, confirm=False)
3346+
3347+
def test_swap_shrink_2048_to_1024_confirm(self):
3348+
self._test_swap_resize(2048, 1024)
3349+
3350+
def test_swap_shrink_2048_to_1024_revert(self):
3351+
self._test_swap_resize(2048, 1024, confirm=False)
3352+
32863353
def _server_created_with_host(self):
32873354
hostname = self.compute1.host
32883355
server_req = self._build_server(

nova/tests/unit/compute/test_compute_mgr.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13365,3 +13365,120 @@ def test_update_compute_provider_status_unexpected_error(self, m_exc):
1336513365
self.assertIn('An error occurred while updating '
1336613366
'COMPUTE_STATUS_DISABLED trait',
1336713367
m_exc.call_args_list[0][0][0])
13368+
13369+
13370+
class ComputeManagerBDMUpdateTestCase(test.TestCase):
13371+
13372+
def setUp(self):
13373+
super(ComputeManagerBDMUpdateTestCase, self).setUp()
13374+
self.compute = manager.ComputeManager()
13375+
self.context = context.RequestContext(fakes.FAKE_USER_ID,
13376+
fakes.FAKE_PROJECT_ID)
13377+
self.instance = mock.Mock()
13378+
13379+
@mock.patch('nova.block_device.create_blank_bdm')
13380+
@mock.patch('nova.objects.BlockDeviceMapping')
13381+
def test_no_flavor_change(self, mock_bdm_obj, mock_create_bdm):
13382+
self.instance.get_bdms.return_value = []
13383+
self.instance.old_flavor = None
13384+
self.instance.new_flavor = None
13385+
13386+
bdms = self.compute._update_bdm_for_swap_to_finish_resize(
13387+
self.context, self.instance)
13388+
13389+
self.assertEqual(bdms, [])
13390+
self.instance.get_bdms.assert_called_once()
13391+
13392+
@mock.patch('nova.block_device.create_blank_bdm')
13393+
@mock.patch('nova.objects.BlockDeviceMapping.create')
13394+
def test_no_swap_change(self, mock_bdm_obj, mock_create_bdm):
13395+
self.instance.old_flavor = mock.Mock(swap=1024)
13396+
self.instance.new_flavor = mock.Mock(swap=1024)
13397+
13398+
existing_swap_bdm = objects.BlockDeviceMapping(
13399+
guest_format='swap',
13400+
device_type='disk',
13401+
volume_size=1024)
13402+
13403+
bdms = objects.BlockDeviceMappingList(objects=[existing_swap_bdm])
13404+
13405+
self.instance.get_bdms.return_value = bdms
13406+
13407+
new_bdms = self.compute._update_bdm_for_swap_to_finish_resize(
13408+
self.context, self.instance)
13409+
13410+
self.assertEqual(new_bdms, bdms)
13411+
self.instance.get_bdms.assert_called_once()
13412+
13413+
@mock.patch('nova.block_device.create_blank_bdm')
13414+
@mock.patch('nova.objects.BlockDeviceMapping')
13415+
def test_add_new_swap_bdm(self, mock_bdm_obj, mock_create_bdm):
13416+
self.instance.old_flavor = mock.Mock(swap=0)
13417+
self.instance.new_flavor = mock.Mock(swap=1024)
13418+
13419+
self.instance.get_bdms.return_value = []
13420+
13421+
new_swap_bdm = {
13422+
'guest_format': 'swap',
13423+
'device_type': 'disk',
13424+
'volume_size': 1024}
13425+
mock_create_bdm.return_value = new_swap_bdm
13426+
mock_bdm_instance = mock_bdm_obj.return_value
13427+
13428+
self.compute._update_bdm_for_swap_to_finish_resize(
13429+
self.context, self.instance)
13430+
13431+
mock_bdm_obj.assert_called_once_with(
13432+
self.context, instance_uuid=self.instance.uuid, **new_swap_bdm
13433+
)
13434+
mock_bdm_instance.update_or_create.assert_called_once()
13435+
# called twice
13436+
self.assertEqual(self.instance.get_bdms.call_count, 2)
13437+
13438+
@mock.patch('nova.block_device.create_blank_bdm')
13439+
@mock.patch('nova.objects.BlockDeviceMapping.create')
13440+
@mock.patch('nova.objects.BlockDeviceMapping.save')
13441+
def test_update_swap_bdm(
13442+
self, mock_bdm_save, mock_bdm_create,
13443+
mock_create_blank_bdm):
13444+
self.instance.old_flavor = mock.Mock(swap=1024) # Existing swap size
13445+
self.instance.new_flavor = mock.Mock(swap=2048) # New swap size
13446+
13447+
existing_swap_bdm = objects.BlockDeviceMapping(
13448+
guest_format='swap',
13449+
device_type='disk',
13450+
volume_size=1024)
13451+
13452+
bdms = objects.BlockDeviceMappingList(objects=[existing_swap_bdm])
13453+
13454+
self.instance.get_bdms.return_value = bdms
13455+
13456+
self.compute._update_bdm_for_swap_to_finish_resize(
13457+
self.context, self.instance)
13458+
13459+
# assert bdm get saved in DB
13460+
existing_swap_bdm.save.assert_called_once()
13461+
# here we are returning same bdms object, not freh from DB
13462+
self.assertEqual(existing_swap_bdm.volume_size, 2048)
13463+
# get_bdms is called only once
13464+
self.assertEqual(self.instance.get_bdms.call_count, 1)
13465+
13466+
@mock.patch('nova.block_device.create_blank_bdm')
13467+
@mock.patch('nova.objects.BlockDeviceMapping.destroy')
13468+
def test_delete_swap_bdm(self, mock_bdm_destroy, mock_create_bdm):
13469+
self.instance.old_flavor = mock.Mock(swap=1024)
13470+
self.instance.new_flavor = mock.Mock(swap=0)
13471+
13472+
existing_swap_bdm = objects.BlockDeviceMapping(
13473+
guest_format='swap',
13474+
device_type='disk',
13475+
volume_size=1024)
13476+
13477+
self.instance.get_bdms.return_value = objects.BlockDeviceMappingList(
13478+
objects=[existing_swap_bdm])
13479+
13480+
self.compute._update_bdm_for_swap_to_finish_resize(
13481+
self.context, self.instance)
13482+
13483+
mock_bdm_destroy.assert_called_once()
13484+
self.instance.get_bdms.assert_called_once()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
3+
fixes:
4+
- |
5+
With this change, operators can now resize the instance flavor swap to
6+
a smaller swap size, it can be expand and shrunk down to 0 using the same
7+
resize API.
8+
For more details see: `bug 1552777`_
9+
10+
.. _`bug 1552777`: https://bugs.launchpad.net/nova/+bug/1552777

0 commit comments

Comments
 (0)