Fix nova assisted volume snapshots
When performing nova assisted volume snapshots, the nova api does
not receive an instance id, so there is not the usual instance
lookup from the cell which automatically targets the context for the
cell that the instance was pulled from, which is also where we'd get
the BDM.
Since we do not know which cell to target when fetching the BDM, we
have to iterate through *all* of them.
Closes-Bug: #1713735
Change-Id: Id2e3d3f177739a31d63790e4a1ae6ac41f438ddd
(cherry picked from commit 074614f077
)
This commit is contained in:
parent
8b162ba21f
commit
b514f93c40
|
@ -4071,9 +4071,31 @@ class API(base.Base):
|
|||
return objects.Migration.get_by_id_and_instance(
|
||||
context, migration_id, instance_uuid)
|
||||
|
||||
def _get_bdm_by_volume_id(self, context, volume_id, expected_attrs=None):
|
||||
"""Retrieve a BDM without knowing its cell.
|
||||
|
||||
.. note:: The context will be targeted to the cell in which the
|
||||
BDM is found, if any.
|
||||
|
||||
:param context: The API request context.
|
||||
:param volume_id: The ID of the volume.
|
||||
:param expected_attrs: list of any additional attributes that should
|
||||
be joined when the BDM is loaded from the database.
|
||||
:raises: nova.exception.VolumeBDMNotFound if not found in any cell
|
||||
"""
|
||||
load_cells()
|
||||
for cell in CELLS:
|
||||
nova_context.set_target_cell(context, cell)
|
||||
try:
|
||||
return objects.BlockDeviceMapping.get_by_volume(
|
||||
context, volume_id, expected_attrs=expected_attrs)
|
||||
except exception.NotFound:
|
||||
continue
|
||||
raise exception.VolumeBDMNotFound(volume_id=volume_id)
|
||||
|
||||
def volume_snapshot_create(self, context, volume_id, create_info):
|
||||
bdm = objects.BlockDeviceMapping.get_by_volume(
|
||||
context, volume_id, expected_attrs=['instance'])
|
||||
bdm = self._get_bdm_by_volume_id(
|
||||
context, volume_id, expected_attrs=['instance'])
|
||||
|
||||
# We allow creating the snapshot in any vm_state as long as there is
|
||||
# no task being performed on the instance and it has a host.
|
||||
|
@ -4094,8 +4116,8 @@ class API(base.Base):
|
|||
|
||||
def volume_snapshot_delete(self, context, volume_id, snapshot_id,
|
||||
delete_info):
|
||||
bdm = objects.BlockDeviceMapping.get_by_volume(
|
||||
context, volume_id, expected_attrs=['instance'])
|
||||
bdm = self._get_bdm_by_volume_id(
|
||||
context, volume_id, expected_attrs=['instance'])
|
||||
|
||||
# We allow deleting the snapshot in any vm_state as long as there is
|
||||
# no task being performed on the instance and it has a host.
|
||||
|
|
|
@ -2789,6 +2789,46 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||
self._test_snapshot_volume_backed(False, True,
|
||||
vm_state=vm_states.SUSPENDED)
|
||||
|
||||
@mock.patch.object(context, 'set_target_cell')
|
||||
@mock.patch.object(objects.BlockDeviceMapping, 'get_by_volume')
|
||||
def test_get_bdm_by_volume_id(self, mock_get_by_volume,
|
||||
mock_target_cell):
|
||||
fake_cells = [mock.sentinel.cell0, mock.sentinel.cell1]
|
||||
|
||||
mock_get_by_volume.side_effect = [
|
||||
exception.VolumeBDMNotFound(volume_id=mock.sentinel.volume_id),
|
||||
mock.sentinel.bdm]
|
||||
|
||||
with mock.patch.object(compute_api, 'CELLS', fake_cells):
|
||||
bdm = self.compute_api._get_bdm_by_volume_id(
|
||||
self.context, mock.sentinel.volume_id,
|
||||
mock.sentinel.expected_attrs)
|
||||
|
||||
self.assertEqual(mock.sentinel.bdm, bdm)
|
||||
|
||||
mock_target_cell.assert_has_calls([
|
||||
mock.call(self.context, cell) for cell in fake_cells])
|
||||
mock_get_by_volume.assert_has_calls(
|
||||
[mock.call(self.context,
|
||||
mock.sentinel.volume_id,
|
||||
expected_attrs=mock.sentinel.expected_attrs)] * 2)
|
||||
|
||||
@mock.patch.object(context, 'set_target_cell')
|
||||
@mock.patch.object(objects.BlockDeviceMapping, 'get_by_volume')
|
||||
def test_get_missing_bdm_by_volume_id(self, mock_get_by_volume,
|
||||
mock_target_cell):
|
||||
fake_cells = [mock.sentinel.cell0, mock.sentinel.cell1]
|
||||
|
||||
mock_get_by_volume.side_effect = exception.VolumeBDMNotFound(
|
||||
volume_id=mock.sentinel.volume_id)
|
||||
|
||||
with mock.patch.object(compute_api, 'CELLS', fake_cells):
|
||||
self.assertRaises(
|
||||
exception.VolumeBDMNotFound,
|
||||
self.compute_api._get_bdm_by_volume_id,
|
||||
self.context, mock.sentinel.volume_id,
|
||||
mock.sentinel.expected_attrs)
|
||||
|
||||
def test_volume_snapshot_create(self):
|
||||
volume_id = '1'
|
||||
create_info = {'id': 'eyedee'}
|
||||
|
@ -2808,12 +2848,12 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||
self.context, objects.BlockDeviceMapping(),
|
||||
fake_bdm, expected_attrs=['instance'])
|
||||
|
||||
self.mox.StubOutWithMock(objects.BlockDeviceMapping,
|
||||
'get_by_volume')
|
||||
self.mox.StubOutWithMock(self.compute_api,
|
||||
'_get_bdm_by_volume_id')
|
||||
self.mox.StubOutWithMock(self.compute_api.compute_rpcapi,
|
||||
'volume_snapshot_create')
|
||||
|
||||
objects.BlockDeviceMapping.get_by_volume(
|
||||
self.compute_api._get_bdm_by_volume_id(
|
||||
self.context, volume_id,
|
||||
expected_attrs=['instance']).AndReturn(fake_bdm)
|
||||
self.compute_api.compute_rpcapi.volume_snapshot_create(self.context,
|
||||
|
@ -2883,12 +2923,12 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||
self.context, objects.BlockDeviceMapping(),
|
||||
fake_bdm, expected_attrs=['instance'])
|
||||
|
||||
self.mox.StubOutWithMock(objects.BlockDeviceMapping,
|
||||
'get_by_volume')
|
||||
self.mox.StubOutWithMock(self.compute_api,
|
||||
'_get_bdm_by_volume_id')
|
||||
self.mox.StubOutWithMock(self.compute_api.compute_rpcapi,
|
||||
'volume_snapshot_delete')
|
||||
|
||||
objects.BlockDeviceMapping.get_by_volume(
|
||||
self.compute_api._get_bdm_by_volume_id(
|
||||
self.context, volume_id,
|
||||
expected_attrs=['instance']).AndReturn(fake_bdm)
|
||||
self.compute_api.compute_rpcapi.volume_snapshot_delete(self.context,
|
||||
|
|
Loading…
Reference in New Issue