Merge "Add block device handling to build_and_run_instance"
This commit is contained in:
commit
6729fc5678
@ -1549,14 +1549,20 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
|
||||
try:
|
||||
self._build_and_run_instance(context, instance, image,
|
||||
decoded_files, admin_password, node, limits)
|
||||
except exception.BuildAbortException:
|
||||
decoded_files, admin_password, block_device_mapping,
|
||||
node, limits)
|
||||
except exception.BuildAbortException as e:
|
||||
LOG.exception(e.format_message(), instance=instance)
|
||||
self._set_instance_error_state(context, instance['uuid'])
|
||||
except exception.RescheduledException:
|
||||
except exception.RescheduledException as e:
|
||||
LOG.debug(e.format_message(), instance=instance)
|
||||
self.compute_task_api.build_instances(context, [instance],
|
||||
image, filter_properties, admin_password,
|
||||
injected_files, requested_networks, security_groups,
|
||||
block_device_mapping)
|
||||
except exception.InstanceNotFound:
|
||||
msg = _('Instance disappeared during build.')
|
||||
LOG.debug(msg, instance=instance)
|
||||
except Exception:
|
||||
# Should not reach here.
|
||||
self._set_instance_error_state(context, instance['uuid'])
|
||||
@ -1569,31 +1575,76 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
node, limits)
|
||||
|
||||
def _build_and_run_instance(self, context, instance, image, injected_files,
|
||||
admin_password, node, limits):
|
||||
admin_password, block_device_mapping, node, limits):
|
||||
|
||||
try:
|
||||
rt = self._get_resource_tracker(node)
|
||||
with rt.instance_claim(context, instance, limits):
|
||||
self.driver.spawn(context, instance, image,
|
||||
injected_files, admin_password)
|
||||
with self._build_resources(context, instance, image,
|
||||
block_device_mapping) as resources:
|
||||
block_device_info = resources['block_device_info']
|
||||
self.driver.spawn(context, instance, image,
|
||||
injected_files, admin_password,
|
||||
block_device_info=block_device_info)
|
||||
except exception.InstanceNotFound:
|
||||
msg = _('Instance disappeared during build.')
|
||||
LOG.debug(msg, instance=instance)
|
||||
raise exception.BuildAbortException(instance_uuid=instance['uuid'],
|
||||
reason=msg)
|
||||
raise
|
||||
except exception.UnexpectedTaskStateError as e:
|
||||
msg = e.format_message()
|
||||
LOG.debug(msg, instance=instance)
|
||||
raise exception.BuildAbortException(instance_uuid=instance['uuid'],
|
||||
reason=e.format_message())
|
||||
except exception.ComputeResourcesUnavailable as e:
|
||||
raise exception.RescheduledException(
|
||||
instance_uuid=instance['uuid'], reason=e.format_message())
|
||||
except exception.BuildAbortException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise exception.RescheduledException(
|
||||
instance_uuid=instance['uuid'], reason=str(e))
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _build_resources(self, context, instance, image, block_device_mapping):
|
||||
resources = {}
|
||||
|
||||
# Verify that all the BDMs have a device_name set and assign a
|
||||
# default to the ones missing it with the help of the driver.
|
||||
self._default_block_device_names(context, instance, image,
|
||||
block_device_mapping)
|
||||
|
||||
try:
|
||||
block_device_info = self._prep_block_device(context, instance,
|
||||
block_device_mapping)
|
||||
resources['block_device_info'] = block_device_info
|
||||
except Exception:
|
||||
LOG.exception(_('Failure prepping block device'),
|
||||
instance=instance)
|
||||
msg = _('Failure prepping block device.')
|
||||
raise exception.BuildAbortException(instance_uuid=instance['uuid'],
|
||||
reason=msg)
|
||||
except exception.ComputeResourcesUnavailable as e:
|
||||
LOG.debug(e.format_message(), instance=instance)
|
||||
raise exception.RescheduledException(
|
||||
instance_uuid=instance['uuid'], reason='')
|
||||
|
||||
try:
|
||||
yield resources
|
||||
except Exception:
|
||||
LOG.exception(_('Instance failed to spawn'), instance=instance)
|
||||
raise exception.RescheduledException(
|
||||
instance_uuid=instance['uuid'], reason='')
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
LOG.exception(_('Instance failed to spawn'), instance=instance)
|
||||
try:
|
||||
self._cleanup_build_resources(context, instance,
|
||||
block_device_mapping)
|
||||
except Exception:
|
||||
ctxt.reraise = False
|
||||
msg = _('Could not clean up failed build,'
|
||||
' not rescheduling')
|
||||
raise exception.BuildAbortException(
|
||||
instance_uuid=instance['uuid'], reason=msg)
|
||||
|
||||
def _cleanup_build_resources(self, context, instance,
|
||||
block_device_mapping):
|
||||
try:
|
||||
self._cleanup_volumes(context, instance['uuid'],
|
||||
block_device_mapping)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
msg = _('Failed to cleanup volumes for failed build,'
|
||||
' not rescheduling')
|
||||
LOG.exception(msg, instance=instance)
|
||||
|
||||
@wrap_exception()
|
||||
@reverts_task_state
|
||||
|
@ -727,6 +727,9 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.image = {}
|
||||
self.node = 'fake-node'
|
||||
self.limits = {}
|
||||
self.block_device_mapping = []
|
||||
self.block_device_info = self.compute._prep_block_device(context,
|
||||
self.instance, self.block_device_mapping)
|
||||
|
||||
# override tracker with a version that doesn't need the database:
|
||||
fake_rt = fake_resource_tracker.FakeResourceTracker(self.compute.host,
|
||||
@ -740,8 +743,8 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.mox.StubOutWithMock(self.compute.conductor_api,
|
||||
'action_event_finish')
|
||||
self.compute._build_and_run_instance(self.context, self.instance,
|
||||
self.image, self.injected_files, self.admin_pass, self.node,
|
||||
self.limits)
|
||||
self.image, self.injected_files, self.admin_pass,
|
||||
self.block_device_mapping, self.node, self.limits)
|
||||
self.compute.conductor_api.action_event_start(self.context,
|
||||
mox.IgnoreArg())
|
||||
self.compute.conductor_api.action_event_finish(self.context,
|
||||
@ -751,7 +754,8 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
self.image, request_spec={}, filter_properties=[],
|
||||
injected_files=self.injected_files,
|
||||
admin_password=self.admin_pass, node=self.node,
|
||||
admin_password=self.admin_pass,
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits)
|
||||
|
||||
def test_build_abort_exception(self):
|
||||
@ -764,8 +768,9 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.mox.StubOutWithMock(self.compute.conductor_api,
|
||||
'action_event_finish')
|
||||
self.compute._build_and_run_instance(self.context, self.instance,
|
||||
self.image, self.injected_files, self.admin_pass, self.node,
|
||||
self.limits).AndRaise(exception.BuildAbortException(reason='',
|
||||
self.image, self.injected_files, self.admin_pass,
|
||||
self.block_device_mapping, self.node, self.limits).AndRaise(
|
||||
exception.BuildAbortException(reason='',
|
||||
instance_uuid=self.instance['uuid']))
|
||||
self.compute._set_instance_error_state(self.context,
|
||||
self.instance['uuid'])
|
||||
@ -778,7 +783,8 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
self.image, request_spec={}, filter_properties=[],
|
||||
injected_files=self.injected_files,
|
||||
admin_password=self.admin_pass, node=self.node,
|
||||
admin_password=self.admin_pass,
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits)
|
||||
|
||||
def test_rescheduled_exception(self):
|
||||
@ -791,12 +797,13 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.mox.StubOutWithMock(self.compute.conductor_api,
|
||||
'action_event_finish')
|
||||
self.compute._build_and_run_instance(self.context, self.instance,
|
||||
self.image, self.injected_files, self.admin_pass, self.node,
|
||||
self.limits).AndRaise(exception.RescheduledException(reason='',
|
||||
self.image, self.injected_files, self.admin_pass,
|
||||
self.block_device_mapping, self.node, self.limits).AndRaise(
|
||||
exception.RescheduledException(reason='',
|
||||
instance_uuid=self.instance['uuid']))
|
||||
self.compute.compute_task_api.build_instances(self.context,
|
||||
[self.instance], self.image, [], self.admin_pass,
|
||||
self.injected_files, None, None, None)
|
||||
self.injected_files, None, None, self.block_device_mapping)
|
||||
self.compute.conductor_api.action_event_start(self.context,
|
||||
mox.IgnoreArg())
|
||||
self.compute.conductor_api.action_event_finish(self.context,
|
||||
@ -806,7 +813,8 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
self.image, request_spec={}, filter_properties=[],
|
||||
injected_files=self.injected_files,
|
||||
admin_password=self.admin_pass, node=self.node,
|
||||
admin_password=self.admin_pass,
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits)
|
||||
|
||||
def test_instance_not_found(self):
|
||||
@ -814,23 +822,26 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.mox.StubOutWithMock(conductor_rpcapi.ConductorAPI,
|
||||
'instance_update')
|
||||
self.compute.driver.spawn(self.context, self.instance, self.image,
|
||||
self.injected_files, self.admin_pass).AndRaise(
|
||||
self.injected_files, self.admin_pass,
|
||||
block_device_info=self.block_device_info).AndRaise(
|
||||
exception.InstanceNotFound(instance_id=1))
|
||||
conductor_rpcapi.ConductorAPI.instance_update(
|
||||
self.context, self.instance['uuid'], mox.IgnoreArg(), 'conductor')
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.assertRaises(exception.BuildAbortException,
|
||||
self.assertRaises(exception.InstanceNotFound,
|
||||
self.compute._build_and_run_instance, self.context,
|
||||
self.instance, self.image, self.injected_files,
|
||||
self.admin_pass, self.node, self.limits)
|
||||
self.admin_pass, self.block_device_mapping, self.node,
|
||||
self.limits)
|
||||
|
||||
def test_reschedule_on_exception(self):
|
||||
self.mox.StubOutWithMock(self.compute.driver, 'spawn')
|
||||
self.mox.StubOutWithMock(conductor_rpcapi.ConductorAPI,
|
||||
'instance_update')
|
||||
self.compute.driver.spawn(self.context, self.instance, self.image,
|
||||
self.injected_files, self.admin_pass).AndRaise(
|
||||
self.injected_files, self.admin_pass,
|
||||
block_device_info=self.block_device_info).AndRaise(
|
||||
test.TestingException())
|
||||
conductor_rpcapi.ConductorAPI.instance_update(
|
||||
self.context, self.instance['uuid'], mox.IgnoreArg(), 'conductor')
|
||||
@ -839,14 +850,16 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.assertRaises(exception.RescheduledException,
|
||||
self.compute._build_and_run_instance, self.context,
|
||||
self.instance, self.image, self.injected_files,
|
||||
self.admin_pass, self.node, self.limits)
|
||||
self.admin_pass, self.block_device_mapping, self.node,
|
||||
self.limits)
|
||||
|
||||
def test_unexpected_task_state(self):
|
||||
self.mox.StubOutWithMock(self.compute.driver, 'spawn')
|
||||
self.mox.StubOutWithMock(conductor_rpcapi.ConductorAPI,
|
||||
'instance_update')
|
||||
self.compute.driver.spawn(self.context, self.instance, self.image,
|
||||
self.injected_files, self.admin_pass).AndRaise(
|
||||
self.injected_files, self.admin_pass,
|
||||
block_device_info=self.block_device_info).AndRaise(
|
||||
exception.UnexpectedTaskStateError(expected=None,
|
||||
actual='deleting'))
|
||||
conductor_rpcapi.ConductorAPI.instance_update(
|
||||
@ -856,7 +869,8 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.assertRaises(exception.BuildAbortException,
|
||||
self.compute._build_and_run_instance, self.context,
|
||||
self.instance, self.image, self.injected_files,
|
||||
self.admin_pass, self.node, self.limits)
|
||||
self.admin_pass, self.block_device_mapping, self.node,
|
||||
self.limits)
|
||||
|
||||
def test_reschedule_on_resources_unavailable(self):
|
||||
class FakeResourceTracker(object):
|
||||
@ -874,7 +888,7 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
FakeResourceTracker())
|
||||
self.compute.compute_task_api.build_instances(self.context,
|
||||
[self.instance], self.image, [], self.admin_pass,
|
||||
self.injected_files, None, None, None)
|
||||
self.injected_files, None, None, self.block_device_mapping)
|
||||
self.compute.conductor_api.action_event_start(self.context,
|
||||
mox.IgnoreArg())
|
||||
self.compute.conductor_api.action_event_finish(self.context,
|
||||
@ -884,5 +898,90 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
||||
self.compute.build_and_run_instance(self.context, self.instance,
|
||||
self.image, request_spec={}, filter_properties=[],
|
||||
injected_files=self.injected_files,
|
||||
admin_password=self.admin_pass, node=self.node,
|
||||
admin_password=self.admin_pass,
|
||||
block_device_mapping=self.block_device_mapping, node=self.node,
|
||||
limits=self.limits)
|
||||
|
||||
def test_build_resources_buildabort_reraise(self):
|
||||
self.mox.StubOutWithMock(self.compute, '_build_resources')
|
||||
self.mox.StubOutWithMock(conductor_rpcapi.ConductorAPI,
|
||||
'instance_update')
|
||||
conductor_rpcapi.ConductorAPI.instance_update(
|
||||
self.context, self.instance['uuid'], mox.IgnoreArg(), 'conductor')
|
||||
self.compute._build_resources(self.context, self.instance, self.image,
|
||||
self.block_device_mapping).AndRaise(
|
||||
exception.BuildAbortException(
|
||||
instance_uuid=self.instance['uuid'],
|
||||
reason=''))
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(exception.BuildAbortException,
|
||||
self.compute._build_and_run_instance, self.context,
|
||||
self.instance, self.image, self.injected_files,
|
||||
self.admin_pass, self.block_device_mapping, self.node,
|
||||
self.limits)
|
||||
|
||||
def test_build_resources_reraises_on_failed_bdm_prep(self):
|
||||
self.mox.StubOutWithMock(self.compute, '_prep_block_device')
|
||||
self.compute._prep_block_device(self.context, self.instance,
|
||||
self.block_device_mapping).AndRaise(test.TestingException())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
try:
|
||||
with self.compute._build_resources(self.context, self.instance,
|
||||
self.image, self.block_device_mapping):
|
||||
pass
|
||||
except Exception as e:
|
||||
self.assertTrue(isinstance(e, exception.BuildAbortException))
|
||||
|
||||
def test_build_resources_cleans_up_and_reraises_on_spawn_failure(self):
|
||||
self.mox.StubOutWithMock(self.compute, '_cleanup_build_resources')
|
||||
self.compute._cleanup_build_resources(self.context, self.instance,
|
||||
self.block_device_mapping)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
test_exception = test.TestingException()
|
||||
|
||||
def fake_spawn():
|
||||
raise test_exception
|
||||
|
||||
try:
|
||||
with self.compute._build_resources(self.context, self.instance,
|
||||
self.image, self.block_device_mapping):
|
||||
fake_spawn()
|
||||
except Exception as e:
|
||||
self.assertEqual(test_exception, e)
|
||||
|
||||
def test_build_resources_aborts_on_cleanup_failure(self):
|
||||
self.mox.StubOutWithMock(self.compute, '_cleanup_build_resources')
|
||||
self.compute._cleanup_build_resources(self.context, self.instance,
|
||||
self.block_device_mapping).AndRaise(test.TestingException())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
def fake_spawn():
|
||||
raise test.TestingException()
|
||||
|
||||
try:
|
||||
with self.compute._build_resources(self.context, self.instance,
|
||||
self.image, self.block_device_mapping):
|
||||
fake_spawn()
|
||||
except Exception as e:
|
||||
self.assertTrue(isinstance(e, exception.BuildAbortException))
|
||||
|
||||
def test_cleanup_cleans_volumes(self):
|
||||
self.mox.StubOutWithMock(self.compute, '_cleanup_volumes')
|
||||
self.compute._cleanup_volumes(self.context, self.instance['uuid'],
|
||||
self.block_device_mapping)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.compute._cleanup_build_resources(self.context, self.instance,
|
||||
self.block_device_mapping)
|
||||
|
||||
def test_cleanup_reraises_volume_cleanup_failure(self):
|
||||
self.mox.StubOutWithMock(self.compute, '_cleanup_volumes')
|
||||
self.compute._cleanup_volumes(self.context, self.instance['uuid'],
|
||||
self.block_device_mapping).AndRaise(test.TestingException())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.assertRaises(test.TestingException,
|
||||
self.compute._cleanup_build_resources, self.context,
|
||||
self.instance, self.block_device_mapping)
|
||||
|
Loading…
x
Reference in New Issue
Block a user