Add new compute method for building an instance

The run_instance method on the compute manager handles rescheduling of a
failed build by casting back to the scheduler.  In order to maintain
backwards compatibility while modifying the build process to go through
the conductor a new method is being added to co-ordinate building an
instance.

This is one part of a series of patches to build up the
build_and_run_instance method.

The conductor will be switched over to calling this method once it
reaches a level of consistency with the current run_instance method.

Part of bp query-scheduler

Change-Id: I879805efa4a0a045082c02b10926e1278136b72c
This commit is contained in:
Andrew Laski
2013-08-13 12:25:35 -04:00
parent b302bea91d
commit 437a1e92c1
2 changed files with 159 additions and 0 deletions

View File

@@ -391,6 +391,7 @@ class ComputeManager(manager.SchedulerDependentManager):
self.compute_api = compute.API()
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.conductor_api = conductor.API()
self.compute_task_api = conductor.ComputeTaskAPI()
self.is_neutron_security_groups = (
openstack_driver.is_neutron_security_groups())
self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI()
@@ -1398,6 +1399,67 @@ class ComputeManager(manager.SchedulerDependentManager):
block_device_mapping)
return {'block_device_mapping': block_device_mapping}
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@reverts_task_state
@wrap_instance_event
@wrap_instance_fault
def build_and_run_instance(self, context, instance, image, request_spec,
filter_properties, admin_password=None,
injected_files=None, requested_networks=None,
security_groups=None, block_device_mapping=None,
node=None):
@utils.synchronized(instance['uuid'])
def do_build_and_run_instance(context, instance, image, request_spec,
filter_properties, admin_password, injected_files,
requested_networks, security_groups, block_device_mapping,
node):
# b64 decode the files to inject:
decoded_files = self._decode_files(injected_files)
try:
self._build_and_run_instance(context, instance, image,
decoded_files, admin_password)
except exception.BuildAbortException:
self._set_instance_error_state(context, instance['uuid'])
except exception.RescheduledException:
self.compute_task_api.build_instances(context, [instance],
image, filter_properties, admin_password,
injected_files, requested_networks, security_groups,
block_device_mapping)
except Exception:
# Should not reach here.
self._set_instance_error_state(context, instance['uuid'])
msg = 'Unexpected build failure, not rescheduling build.'
LOG.exception(msg, instance=instance)
do_build_and_run_instance(context, instance, image, request_spec,
filter_properties, admin_password, injected_files,
requested_networks, security_groups, block_device_mapping,
node)
def _build_and_run_instance(self, context, instance, image, injected_files,
admin_password):
try:
self.driver.spawn(context, instance, image,
injected_files, admin_password)
except exception.InstanceNotFound:
msg = _('Instance disappeared during build.')
LOG.debug(msg, instance=instance)
raise exception.BuildAbortException(instance_uuid=instance['uuid'],
reason=msg)
except exception.UnexpectedTaskStateError as e:
msg = e.format_message()
LOG.debug(msg, instance=instance)
raise exception.BuildAbortException(instance_uuid=instance['uuid'],
reason=msg)
except Exception:
LOG.exception('Instance failed to spawn', instance=instance)
raise exception.RescheduledException(
instance_uuid=instance['uuid'], reason='')
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@reverts_task_state
@wrap_instance_event

View File

@@ -614,3 +614,100 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
{'uuid': 'fake'})
self.assertTrue(volumes[old_volume_id]['status'], 'detaching')
self.assertTrue(volumes[new_volume_id]['status'], 'attaching')
class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
def setUp(self):
super(ComputeManagerBuildInstanceTestCase, self).setUp()
self.compute = importutils.import_object(CONF.compute_manager)
self.context = context.RequestContext('fake', 'fake')
self.instance = fake_instance.fake_db_instance(
vm_state=vm_states.ACTIVE)
self.admin_pass = 'pass'
self.injected_files = []
self.image = {}
def test_build_and_run_instance_called_with_proper_args(self):
self.mox.StubOutWithMock(self.compute, '_build_and_run_instance')
self.compute._build_and_run_instance(self.context, self.instance,
self.image, self.injected_files, self.admin_pass)
self.mox.ReplayAll()
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)
def test_build_abort_exception(self):
self.mox.StubOutWithMock(self.compute, '_build_and_run_instance')
self.mox.StubOutWithMock(self.compute, '_set_instance_error_state')
self.mox.StubOutWithMock(self.compute.compute_task_api,
'build_instances')
self.compute._build_and_run_instance(self.context, self.instance,
self.image, self.injected_files, self.admin_pass).AndRaise(
exception.BuildAbortException(reason='',
instance_uuid=self.instance['uuid']))
self.compute._set_instance_error_state(self.context,
self.instance['uuid'])
self.mox.ReplayAll()
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)
def test_rescheduled_exception(self):
self.mox.StubOutWithMock(self.compute, '_build_and_run_instance')
self.mox.StubOutWithMock(self.compute, '_set_instance_error_state')
self.mox.StubOutWithMock(self.compute.compute_task_api,
'build_instances')
self.compute._build_and_run_instance(self.context, self.instance,
self.image, self.injected_files, self.admin_pass).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.mox.ReplayAll()
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)
def test_instance_not_found(self):
self.mox.StubOutWithMock(self.compute.driver, 'spawn')
self.compute.driver.spawn(self.context, self.instance, self.image,
self.injected_files, self.admin_pass).AndRaise(
exception.InstanceNotFound(instance_id=1))
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)
def test_reschedule_on_exception(self):
self.mox.StubOutWithMock(self.compute.driver, 'spawn')
self.compute.driver.spawn(self.context, self.instance, self.image,
self.injected_files, self.admin_pass).AndRaise(
test.TestingException())
self.mox.ReplayAll()
self.assertRaises(exception.RescheduledException,
self.compute._build_and_run_instance, self.context,
self.instance, self.image, self.injected_files,
self.admin_pass)
def test_unexpected_task_state(self):
self.mox.StubOutWithMock(self.compute.driver, 'spawn')
self.compute.driver.spawn(self.context, self.instance, self.image,
self.injected_files, self.admin_pass).AndRaise(
exception.UnexpectedTaskStateError(expected=None,
actual='deleting'))
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)