Store block device mappings in cell0

If an instance fails to get scheduled, it gets buried in cell0 but
none of it's block device mappings are stored.  At the API layer,
Nova reserves and creates attachments for new instances when
it gets a create request so these attachments are orphaned if the
block device mappings are not registered in the database somewhere.

This patch makes sure that if an instance is being buried in cell0,
all of it's block device mappings are recorded as well so they can
be later removed when the instance is deleted.

Change-Id: I64074923fb741fbf5459f66b8ab1a23c16f3303f
Related-Bug: #1404867
This commit is contained in:
Mohammed Naser 2018-02-14 18:39:38 -05:00
parent 8cd64670ea
commit ad9e2a568f
2 changed files with 41 additions and 6 deletions

View File

@ -1031,7 +1031,8 @@ class ComputeTaskManager(base.Base):
return tags
def _bury_in_cell0(self, context, request_spec, exc,
build_requests=None, instances=None):
build_requests=None, instances=None,
block_device_mapping=None):
"""Ensure all provided build_requests and instances end up in cell0.
Cell0 is the fake cell we schedule dead instances to when we can't
@ -1067,6 +1068,14 @@ class ComputeTaskManager(base.Base):
for instance in instances_by_uuid.values():
with obj_target_cell(instance, cell0) as cctxt:
instance.create()
# NOTE(mnaser): In order to properly clean-up volumes after
# being buried in cell0, we need to store BDMs.
if block_device_mapping:
self._create_block_device_mapping(
cell0, instance.flavor, instance.uuid,
block_device_mapping)
# Use the context targeted to cell0 here since the instance is
# now in cell0.
self._set_vm_state_and_notify(
@ -1105,7 +1114,8 @@ class ComputeTaskManager(base.Base):
except Exception as exc:
LOG.exception('Failed to schedule instances')
self._bury_in_cell0(context, request_specs[0], exc,
build_requests=build_requests)
build_requests=build_requests,
block_device_mapping=block_device_mapping)
return
host_mapping_cache = {}
@ -1128,9 +1138,10 @@ class ComputeTaskManager(base.Base):
LOG.error('No host-to-cell mapping found for selected '
'host %(host)s. Setup is incomplete.',
{'host': host.service_host})
self._bury_in_cell0(context, request_spec, exc,
build_requests=[build_request],
instances=[instance])
self._bury_in_cell0(
context, request_spec, exc,
build_requests=[build_request], instances=[instance],
block_device_mapping=block_device_mapping)
# This is a placeholder in case the quota recheck fails.
instances.append(None)
continue

View File

@ -1856,12 +1856,15 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
select_dest,
build_and_run):
def _fake_bury(ctxt, request_spec, exc,
build_requests=None, instances=None):
build_requests=None, instances=None,
block_device_mapping=None):
self.assertIn('not mapped to any cell', str(exc))
self.assertEqual(1, len(build_requests))
self.assertEqual(1, len(instances))
self.assertEqual(build_requests[0].instance_uuid,
instances[0].uuid)
self.assertEqual(self.params['block_device_mapping'],
block_device_mapping)
bury.side_effect = _fake_bury
select_dest.return_value = [[fake_selection1]]
@ -2002,6 +2005,27 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.assertEqual(expected, inst_states)
@mock.patch.object(objects.CellMapping, 'get_by_uuid')
@mock.patch.object(conductor_manager.ComputeTaskManager,
'_create_block_device_mapping')
def test_bury_in_cell0_with_block_device_mapping(self, mock_create_bdm,
mock_get_cell):
mock_get_cell.return_value = self.cell_mappings['cell0']
inst_br = fake_build_request.fake_req_obj(self.ctxt)
del inst_br.instance.id
inst_br.create()
inst = inst_br.get_new_instance(self.ctxt)
self.conductor._bury_in_cell0(
self.ctxt, self.params['request_specs'][0], Exception('Foo'),
build_requests=[inst_br], instances=[inst],
block_device_mapping=self.params['block_device_mapping'])
mock_create_bdm.assert_called_once_with(
self.cell_mappings['cell0'], inst.flavor, inst.uuid,
self.params['block_device_mapping'])
def test_reset(self):
with mock.patch('nova.compute.rpcapi.ComputeAPI') as mock_rpc:
old_rpcapi = self.conductor_manager.compute_rpcapi