Skip to content

Commit c12d07f

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "compute: Reject requests to commit intermediary snapshot of an inactive instance" into stable/wallaby
2 parents 11acae8 + 200c743 commit c12d07f

File tree

2 files changed

+66
-9
lines changed

2 files changed

+66
-9
lines changed

nova/compute/api.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5483,11 +5483,25 @@ def volume_snapshot_delete(self, context, volume_id, snapshot_id,
54835483
bdm = self._get_bdm_by_volume_id(
54845484
context, volume_id, expected_attrs=['instance'])
54855485

5486-
# We allow deleting the snapshot in any vm_state as long as there is
5487-
# no task being performed on the instance and it has a host.
54885486
@check_instance_host()
54895487
@check_instance_state(vm_state=None)
54905488
def do_volume_snapshot_delete(self, context, instance):
5489+
# FIXME(lyarwood): Avoid bug #1919487 by rejecting the request
5490+
# to delete an intermediary volume snapshot offline as this isn't
5491+
# currently implemented within the libvirt driver and will fail.
5492+
# This should be fixed in a future release but as it is essentially
5493+
# a new feature wouldn't be something we could backport. As such
5494+
# reject the request here so n-api can respond correctly to c-vol.
5495+
if (delete_info.get('merge_target_file') is not None and
5496+
instance.vm_state != vm_states.ACTIVE
5497+
):
5498+
raise exception.InstanceInvalidState(
5499+
attr='vm_state',
5500+
instance_uuid=instance.uuid,
5501+
state=instance.vm_state,
5502+
method='volume_snapshot_delete'
5503+
)
5504+
54915505
self.compute_rpcapi.volume_snapshot_delete(context, instance,
54925506
volume_id, snapshot_id, delete_info)
54935507

nova/tests/unit/compute/test_api.py

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3601,23 +3601,29 @@ def test_volume_snapshot_delete(self, mock_get_bdm):
36013601
'boot_index': -1})
36023602
fake_bdm['instance'] = fake_instance.fake_db_instance(
36033603
launched_at=timeutils.utcnow(),
3604-
vm_state=vm_states.STOPPED)
3604+
vm_state=vm_states.ACTIVE)
36053605
fake_bdm['instance_uuid'] = fake_bdm['instance']['uuid']
36063606
fake_bdm = objects.BlockDeviceMapping._from_db_object(
36073607
self.context, objects.BlockDeviceMapping(),
36083608
fake_bdm, expected_attrs=['instance'])
36093609

36103610
mock_get_bdm.return_value = fake_bdm
3611+
delete_info = {
3612+
'merge_target_file': 'foo.qcow2',
3613+
}
36113614

3612-
with mock.patch.object(self.compute_api.compute_rpcapi,
3613-
'volume_snapshot_delete') as mock_snapshot:
3614-
self.compute_api.volume_snapshot_delete(self.context, volume_id,
3615-
snapshot_id, {})
3616-
3615+
with mock.patch.object(
3616+
self.compute_api.compute_rpcapi, 'volume_snapshot_delete'
3617+
) as mock_snapshot:
3618+
self.compute_api.volume_snapshot_delete(
3619+
self.context, volume_id, snapshot_id, delete_info
3620+
)
36173621
mock_get_bdm.assert_called_once_with(self.context, volume_id,
36183622
expected_attrs=['instance'])
36193623
mock_snapshot.assert_called_once_with(
3620-
self.context, fake_bdm['instance'], volume_id, snapshot_id, {})
3624+
self.context, fake_bdm['instance'], volume_id, snapshot_id,
3625+
delete_info
3626+
)
36213627

36223628
@mock.patch.object(
36233629
objects.BlockDeviceMapping, 'get_by_volume',
@@ -3651,6 +3657,43 @@ def test_volume_snapshot_delete_shelved_offloaded(self, bdm_get_by_volume):
36513657
self.context, mock.sentinel.volume_id,
36523658
mock.sentinel.snapshot_id, mock.sentinel.delete_info)
36533659

3660+
@mock.patch.object(compute_api.API, '_get_bdm_by_volume_id')
3661+
def test_volume_snapshot_delete_intermediary_commit(self, mock_get_bdm):
3662+
fake_bdm = fake_block_device.FakeDbBlockDeviceDict({
3663+
'id': 123,
3664+
'device_name': '/dev/sda2',
3665+
'source_type': 'volume',
3666+
'destination_type': 'volume',
3667+
'connection_info': "{'fake': 'connection_info'}",
3668+
'volume_id': uuids.volume_id,
3669+
'boot_index': -1
3670+
})
3671+
fake_bdm['instance'] = fake_instance.fake_db_instance(
3672+
launched_at=timeutils.utcnow(),
3673+
vm_state=vm_states.STOPPED)
3674+
fake_bdm['instance_uuid'] = fake_bdm['instance']['uuid']
3675+
fake_bdm = objects.BlockDeviceMapping._from_db_object(
3676+
self.context, objects.BlockDeviceMapping(),
3677+
fake_bdm, expected_attrs=['instance'])
3678+
mock_get_bdm.return_value = fake_bdm
3679+
3680+
# c-vol can provide delete_info with merge_target_file pointing to
3681+
# an intermediary snapshot to commit into it's base. This is only
3682+
# supported while the instance is running at present.
3683+
delete_info = {
3684+
'merge_target_file': 'snap.img'
3685+
}
3686+
3687+
# Assert that the request is rejected as offline commit isn't supported
3688+
self.assertRaises(
3689+
exception.InstanceInvalidState,
3690+
self.compute_api.volume_snapshot_delete,
3691+
self.context,
3692+
uuids.volume_id,
3693+
uuids.snapshot_id,
3694+
delete_info
3695+
)
3696+
36543697
def _create_instance_with_disabled_disk_config(self, object=False):
36553698
sys_meta = {"image_auto_disk_config": "Disabled"}
36563699
params = {"system_metadata": sys_meta}

0 commit comments

Comments
 (0)