Merge "Add block device handling to build_and_run_instance"

This commit is contained in:
Jenkins 2013-10-29 14:20:42 +00:00 committed by Gerrit Code Review
commit 6729fc5678
2 changed files with 188 additions and 38 deletions

View File

@ -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

View File

@ -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)