From 6b16c8731c44e4a6c80b803f3e8afdd88386d577 Mon Sep 17 00:00:00 2001 From: Andrew Laski Date: Tue, 14 May 2013 10:49:15 -0400 Subject: Call scheduler for run_instance from conductor This prepares for having the conductor query the scheduler for a list of hosts to build an instance on. In order to accomplish this the API sends a build_instances message to conductor, or spawns a greenthread in local mode, rather than sending a message to the scheduler. This is being done so that conductor can handle the orchestration of long running tasks like spawning instances. By making this move, the API is free to return to the caller while conductor queries the scheduler for a host to provision to. In the case of cells the build_instances message first goes to the cells scheduler in order to pick a cell to send it to, and then it is sent to the conductor in that cell. Part of bp query-scheduler Change-Id: I4539888e78ebdbb8cef6647273b959a012280110 --- nova/tests/api/ec2/test_cinder_cloud.py | 2 + nova/tests/api/ec2/test_cloud.py | 2 + nova/tests/cells/test_cells_manager.py | 9 ++ nova/tests/cells/test_cells_messaging.py | 10 ++ nova/tests/cells/test_cells_rpcapi.py | 15 ++ nova/tests/cells/test_cells_scheduler.py | 260 ++++++++++++++++++++++++++---- nova/tests/conductor/test_conductor.py | 35 ++++ nova/tests/fake_utils.py | 28 ++++ nova/tests/integrated/test_api_samples.py | 2 + 9 files changed, 333 insertions(+), 30 deletions(-) create mode 100644 nova/tests/fake_utils.py (limited to 'nova/tests') diff --git a/nova/tests/api/ec2/test_cinder_cloud.py b/nova/tests/api/ec2/test_cinder_cloud.py index 5d02a28d7..a307eaa2f 100644 --- a/nova/tests/api/ec2/test_cinder_cloud.py +++ b/nova/tests/api/ec2/test_cinder_cloud.py @@ -34,6 +34,7 @@ from nova import exception from nova.openstack.common import rpc from nova import test from nova.tests import fake_network +from nova.tests import fake_utils from nova.tests.image import fake from nova.tests import matchers from nova import volume @@ -88,6 +89,7 @@ class CinderCloudTestCase(test.TestCase): super(CinderCloudTestCase, self).setUp() ec2utils.reset_cache() vol_tmpdir = self.useFixture(fixtures.TempDir()).path + fake_utils.stub_out_utils_spawn_n(self.stubs) self.flags(compute_driver='nova.virt.fake.FakeDriver', volume_api_class='nova.tests.fake_volume.API') diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index e9ff10be0..22f9c2d81 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -50,6 +50,7 @@ from nova import test from nova.tests.api.openstack.compute.contrib import ( test_quantum_security_groups as test_quantum) from nova.tests import fake_network +from nova.tests import fake_utils from nova.tests.image import fake from nova.tests import matchers from nova import utils @@ -112,6 +113,7 @@ class CloudTestCase(test.TestCase): self.flags(compute_driver='nova.virt.fake.FakeDriver', volume_api_class='nova.tests.fake_volume.API') self.useFixture(fixtures.FakeLogger('boto')) + fake_utils.stub_out_utils_spawn_n(self.stubs) def fake_show(meh, context, id): return {'id': id, diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py index 137d48ff6..543ff66e7 100644 --- a/nova/tests/cells/test_cells_manager.py +++ b/nova/tests/cells/test_cells_manager.py @@ -122,6 +122,15 @@ class CellsManagerClassTestCase(test.TestCase): self.cells_manager.schedule_run_instance(self.ctxt, host_sched_kwargs=host_sched_kwargs) + def test_build_instances(self): + build_inst_kwargs = {'instances': [1, 2]} + self.mox.StubOutWithMock(self.msg_runner, 'build_instances') + our_cell = self.msg_runner.state_manager.get_my_state() + self.msg_runner.build_instances(self.ctxt, our_cell, build_inst_kwargs) + self.mox.ReplayAll() + self.cells_manager.build_instances(self.ctxt, + build_inst_kwargs=build_inst_kwargs) + def test_run_compute_api_method(self): # Args should just be silently passed through cell_name = 'fake-cell-name' diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py index 1de39de1f..9aae11201 100644 --- a/nova/tests/cells/test_cells_messaging.py +++ b/nova/tests/cells/test_cells_messaging.py @@ -608,6 +608,16 @@ class CellsTargetedMethodsTestCase(test.TestCase): self.tgt_cell_name, host_sched_kwargs) + def test_build_instances(self): + build_inst_kwargs = {'filter_properties': {}, + 'key1': 'value1', + 'key2': 'value2'} + self.mox.StubOutWithMock(self.tgt_scheduler, 'build_instances') + self.tgt_scheduler.build_instances(self.ctxt, build_inst_kwargs) + self.mox.ReplayAll() + self.src_msg_runner.build_instances(self.ctxt, self.tgt_cell_name, + build_inst_kwargs) + def test_run_compute_api_method(self): instance_uuid = 'fake_instance_uuid' diff --git a/nova/tests/cells/test_cells_rpcapi.py b/nova/tests/cells/test_cells_rpcapi.py index 76c9f05d3..172b54831 100644 --- a/nova/tests/cells/test_cells_rpcapi.py +++ b/nova/tests/cells/test_cells_rpcapi.py @@ -114,6 +114,21 @@ class CellsAPITestCase(test.TestCase): self._check_result(call_info, 'schedule_run_instance', expected_args) + def test_build_instances(self): + call_info = self._stub_rpc_method('cast', None) + + self.cells_rpcapi.build_instances( + self.fake_context, instances=['1', '2'], + image={'fake': 'image'}, arg1=1, arg2=2, arg3=3) + + expected_args = {'build_inst_kwargs': {'instances': ['1', '2'], + 'image': {'fake': 'image'}, + 'arg1': 1, + 'arg2': 2, + 'arg3': 3}} + self._check_result(call_info, 'build_instances', + expected_args, version=1.8) + def test_instance_update_at_top(self): fake_info_cache = {'id': 1, 'instance': 'fake_instance', diff --git a/nova/tests/cells/test_cells_scheduler.py b/nova/tests/cells/test_cells_scheduler.py index c8f90619e..9cd637cdf 100644 --- a/nova/tests/cells/test_cells_scheduler.py +++ b/nova/tests/cells/test_cells_scheduler.py @@ -26,6 +26,7 @@ from nova import context from nova import db from nova import exception from nova.openstack.common import uuidutils +from nova.scheduler import utils as scheduler_utils from nova import test from nova.tests.cells import fakes @@ -73,24 +74,32 @@ class CellsSchedulerTestCase(test.TestCase): for x in xrange(3): instance_uuids.append(uuidutils.generate_uuid()) self.instance_uuids = instance_uuids - self.request_spec = {'instance_uuids': instance_uuids, - 'other': 'stuff'} + self.instances = [{'uuid': uuid} for uuid in instance_uuids] + self.request_spec = { + 'instance_uuids': instance_uuids, + 'instance_properties': 'fake_properties', + 'instance_type': 'fake_type', + 'image': 'fake_image', + 'security_group': 'fake_sec_groups', + 'block_device_mapping': 'fake_bdm'} + self.build_inst_kwargs = { + 'instances': self.instances, + 'instance_type': 'fake_type', + 'image': 'fake_image', + 'filter_properties': {}, + 'security_group': 'fake_sec_groups', + 'block_device_mapping': 'fake_bdm'} def test_create_instances_here(self): # Just grab the first instance type inst_type = db.instance_type_get(self.ctxt, 1) image = {'properties': {}} + instance_uuids = self.instance_uuids instance_props = {'hostname': 'meow', 'display_name': 'moo', 'image_ref': 'fake_image_ref', 'user_id': self.ctxt.user_id, 'project_id': self.ctxt.project_id} - request_spec = {'instance_type': inst_type, - 'image': image, - 'security_group': ['default'], - 'block_device_mapping': [], - 'instance_properties': instance_props, - 'instance_uuids': self.instance_uuids} call_info = {'uuids': []} @@ -100,10 +109,11 @@ class CellsSchedulerTestCase(test.TestCase): self.stubs.Set(self.msg_runner, 'instance_update_at_top', _fake_instance_update_at_top) - self.scheduler._create_instances_here(self.ctxt, request_spec) - self.assertEqual(self.instance_uuids, call_info['uuids']) + self.scheduler._create_instances_here(self.ctxt, instance_uuids, + instance_props, inst_type, image, ['default'], []) + self.assertEqual(instance_uuids, call_info['uuids']) - for instance_uuid in self.instance_uuids: + for instance_uuid in instance_uuids: instance = db.instance_get_by_uuid(self.ctxt, instance_uuid) self.assertEqual('meow', instance['hostname']) self.assertEqual('moo-%s' % instance['uuid'], @@ -146,6 +156,48 @@ class CellsSchedulerTestCase(test.TestCase): child_cells = self.state_manager.get_child_cells() self.assertIn(call_info['target_cell'], child_cells) + def test_build_instances_selects_child_cell(self): + # Make sure there's no capacity info so we're sure to + # select a child cell + our_cell_info = self.state_manager.get_my_state() + our_cell_info.capacities = {} + + call_info = {'times': 0} + + orig_fn = self.msg_runner.build_instances + + def msg_runner_build_instances(ctxt, target_cell, build_inst_kwargs): + # This gets called twice. Once for our running it + # in this cell.. and then it'll get called when the + # child cell is picked. So, first time.. just run it + # like normal. + if not call_info['times']: + call_info['times'] += 1 + return orig_fn(ctxt, target_cell, build_inst_kwargs) + call_info['ctxt'] = ctxt + call_info['target_cell'] = target_cell + call_info['build_inst_kwargs'] = build_inst_kwargs + + def fake_build_request_spec(image, instances): + request_spec = { + 'instance_uuids': [inst['uuid'] for inst in instances], + 'image': image} + return request_spec + + self.stubs.Set(self.msg_runner, 'build_instances', + msg_runner_build_instances) + self.stubs.Set(scheduler_utils, 'build_request_spec', + fake_build_request_spec) + + self.msg_runner.build_instances(self.ctxt, self.my_cell_state, + self.build_inst_kwargs) + + self.assertEqual(self.ctxt, call_info['ctxt']) + self.assertEqual(self.build_inst_kwargs, + call_info['build_inst_kwargs']) + child_cells = self.state_manager.get_child_cells() + self.assertIn(call_info['target_cell'], child_cells) + def test_run_instance_selects_current_cell(self): # Make sure there's no child cells so that we will be # selected @@ -153,9 +205,16 @@ class CellsSchedulerTestCase(test.TestCase): call_info = {} - def fake_create_instances_here(ctxt, request_spec): + def fake_create_instances_here(ctxt, instance_uuids, + instance_properties, instance_type, image, security_groups, + block_device_mapping): call_info['ctxt'] = ctxt - call_info['request_spec'] = request_spec + call_info['instance_uuids'] = instance_uuids + call_info['instance_properties'] = instance_properties + call_info['instance_type'] = instance_type + call_info['image'] = image + call_info['security_groups'] = security_groups + call_info['block_device_mapping'] = block_device_mapping def fake_rpc_run_instance(ctxt, **host_sched_kwargs): call_info['host_sched_kwargs'] = host_sched_kwargs @@ -172,8 +231,69 @@ class CellsSchedulerTestCase(test.TestCase): self.my_cell_state, host_sched_kwargs) self.assertEqual(self.ctxt, call_info['ctxt']) - self.assertEqual(self.request_spec, call_info['request_spec']) self.assertEqual(host_sched_kwargs, call_info['host_sched_kwargs']) + self.assertEqual(self.instance_uuids, call_info['instance_uuids']) + self.assertEqual(self.request_spec['instance_properties'], + call_info['instance_properties']) + self.assertEqual(self.request_spec['instance_type'], + call_info['instance_type']) + self.assertEqual(self.request_spec['image'], call_info['image']) + self.assertEqual(self.request_spec['security_group'], + call_info['security_groups']) + self.assertEqual(self.request_spec['block_device_mapping'], + call_info['block_device_mapping']) + + def test_build_instances_selects_current_cell(self): + # Make sure there's no child cells so that we will be + # selected + self.state_manager.child_cells = {} + + call_info = {} + + def fake_create_instances_here(ctxt, instance_uuids, + instance_properties, instance_type, image, security_groups, + block_device_mapping): + call_info['ctxt'] = ctxt + call_info['instance_uuids'] = instance_uuids + call_info['instance_properties'] = instance_properties + call_info['instance_type'] = instance_type + call_info['image'] = image + call_info['security_groups'] = security_groups + call_info['block_device_mapping'] = block_device_mapping + + def fake_rpc_build_instances(ctxt, **build_inst_kwargs): + call_info['build_inst_kwargs'] = build_inst_kwargs + + def fake_build_request_spec(image, instances): + request_spec = { + 'instance_uuids': [inst['uuid'] for inst in instances], + 'image': image} + return request_spec + + self.stubs.Set(self.scheduler, '_create_instances_here', + fake_create_instances_here) + self.stubs.Set(self.scheduler.compute_task_api, + 'build_instances', fake_rpc_build_instances) + self.stubs.Set(scheduler_utils, 'build_request_spec', + fake_build_request_spec) + + self.msg_runner.build_instances(self.ctxt, self.my_cell_state, + self.build_inst_kwargs) + + self.assertEqual(self.ctxt, call_info['ctxt']) + self.assertEqual(self.instance_uuids, call_info['instance_uuids']) + self.assertEqual(self.build_inst_kwargs['instances'][0], + call_info['instance_properties']) + self.assertEqual(self.build_inst_kwargs['instance_type'], + call_info['instance_type']) + self.assertEqual(self.build_inst_kwargs['image'], call_info['image']) + self.assertEqual(self.build_inst_kwargs['security_group'], + call_info['security_groups']) + self.assertEqual(self.build_inst_kwargs['block_device_mapping'], + call_info['block_device_mapping']) + self.assertEqual(self.build_inst_kwargs, + call_info['build_inst_kwargs']) + self.assertEqual(self.instance_uuids, call_info['instance_uuids']) def test_run_instance_retries_when_no_cells_avail(self): self.flags(scheduler_retries=7, group='cells') @@ -183,7 +303,7 @@ class CellsSchedulerTestCase(test.TestCase): call_info = {'num_tries': 0, 'errored_uuids': []} - def fake_run_instance(message, host_sched_kwargs): + def fake_grab_target_cells(filter_properties): call_info['num_tries'] += 1 raise exception.NoCellsAvailable() @@ -194,7 +314,8 @@ class CellsSchedulerTestCase(test.TestCase): self.assertEqual(vm_states.ERROR, values['vm_state']) call_info['errored_uuids'].append(instance_uuid) - self.stubs.Set(self.scheduler, '_run_instance', fake_run_instance) + self.stubs.Set(self.scheduler, '_grab_target_cells', + fake_grab_target_cells) self.stubs.Set(time, 'sleep', fake_sleep) self.stubs.Set(db, 'instance_update', fake_instance_update) @@ -204,17 +325,55 @@ class CellsSchedulerTestCase(test.TestCase): self.assertEqual(8, call_info['num_tries']) self.assertEqual(self.instance_uuids, call_info['errored_uuids']) - def test_run_instance_on_random_exception(self): + def test_build_instances_retries_when_no_cells_avail(self): self.flags(scheduler_retries=7, group='cells') - host_sched_kwargs = {'request_spec': self.request_spec, - 'filter_properties': {}} + call_info = {'num_tries': 0, 'errored_uuids': []} + + def fake_grab_target_cells(filter_properties): + call_info['num_tries'] += 1 + raise exception.NoCellsAvailable() + + def fake_sleep(_secs): + return + + def fake_instance_update(ctxt, instance_uuid, values): + self.assertEqual(vm_states.ERROR, values['vm_state']) + call_info['errored_uuids'].append(instance_uuid) + + def fake_build_request_spec(image, instances): + request_spec = { + 'instance_uuids': [inst['uuid'] for inst in instances], + 'image': image} + return request_spec + + self.stubs.Set(self.scheduler, '_grab_target_cells', + fake_grab_target_cells) + self.stubs.Set(time, 'sleep', fake_sleep) + self.stubs.Set(db, 'instance_update', fake_instance_update) + self.stubs.Set(scheduler_utils, 'build_request_spec', + fake_build_request_spec) + + self.msg_runner.build_instances(self.ctxt, self.my_cell_state, + self.build_inst_kwargs) + + self.assertEqual(8, call_info['num_tries']) + self.assertEqual(self.instance_uuids, call_info['errored_uuids']) + + def test_schedule_method_on_random_exception(self): + self.flags(scheduler_retries=7, group='cells') + + instances = [{'uuid': uuid} for uuid in self.instance_uuids] + method_kwargs = { + 'image': 'fake_image', + 'instances': instances, + 'filter_properties': {}} call_info = {'num_tries': 0, 'errored_uuids1': [], 'errored_uuids2': []} - def fake_run_instance(message, host_sched_kwargs): + def fake_grab_target_cells(filter_properties): call_info['num_tries'] += 1 raise test.TestingException() @@ -226,13 +385,22 @@ class CellsSchedulerTestCase(test.TestCase): self.assertEqual(vm_states.ERROR, instance['vm_state']) call_info['errored_uuids2'].append(instance['uuid']) - self.stubs.Set(self.scheduler, '_run_instance', fake_run_instance) + def fake_build_request_spec(image, instances): + request_spec = { + 'instance_uuids': [inst['uuid'] for inst in instances], + 'image': image} + return request_spec + + self.stubs.Set(self.scheduler, '_grab_target_cells', + fake_grab_target_cells) self.stubs.Set(db, 'instance_update', fake_instance_update) self.stubs.Set(self.msg_runner, 'instance_update_at_top', - fake_instance_update_at_top) + fake_instance_update_at_top) + self.stubs.Set(scheduler_utils, 'build_request_spec', + fake_build_request_spec) - self.msg_runner.schedule_run_instance(self.ctxt, - self.my_cell_state, host_sched_kwargs) + self.msg_runner.build_instances(self.ctxt, self.my_cell_state, + method_kwargs) # Shouldn't retry self.assertEqual(1, call_info['num_tries']) self.assertEqual(self.instance_uuids, call_info['errored_uuids1']) @@ -252,9 +420,16 @@ class CellsSchedulerTestCase(test.TestCase): call_info = {} - def fake_create_instances_here(ctxt, request_spec): + def fake_create_instances_here(ctxt, instance_uuids, + instance_properties, instance_type, image, security_groups, + block_device_mapping): call_info['ctxt'] = ctxt - call_info['request_spec'] = request_spec + call_info['instance_uuids'] = instance_uuids + call_info['instance_properties'] = instance_properties + call_info['instance_type'] = instance_type + call_info['image'] = image + call_info['security_groups'] = security_groups + call_info['block_device_mapping'] = block_device_mapping def fake_rpc_run_instance(ctxt, **host_sched_kwargs): call_info['host_sched_kwargs'] = host_sched_kwargs @@ -281,7 +456,16 @@ class CellsSchedulerTestCase(test.TestCase): self.my_cell_state, host_sched_kwargs) # Our cell was selected. self.assertEqual(self.ctxt, call_info['ctxt']) - self.assertEqual(self.request_spec, call_info['request_spec']) + self.assertEqual(self.instance_uuids, call_info['instance_uuids']) + self.assertEqual(self.request_spec['instance_properties'], + call_info['instance_properties']) + self.assertEqual(self.request_spec['instance_type'], + call_info['instance_type']) + self.assertEqual(self.request_spec['image'], call_info['image']) + self.assertEqual(self.request_spec['security_group'], + call_info['security_groups']) + self.assertEqual(self.request_spec['block_device_mapping'], + call_info['block_device_mapping']) self.assertEqual(host_sched_kwargs, call_info['host_sched_kwargs']) # Filter args are correct expected_filt_props = {'context': self.ctxt, @@ -341,9 +525,16 @@ class CellsSchedulerTestCase(test.TestCase): call_info = {} - def fake_create_instances_here(ctxt, request_spec): + def fake_create_instances_here(ctxt, instance_uuids, + instance_properties, instance_type, image, security_groups, + block_device_mapping): call_info['ctxt'] = ctxt - call_info['request_spec'] = request_spec + call_info['instance_uuids'] = instance_uuids + call_info['instance_properties'] = instance_properties + call_info['instance_type'] = instance_type + call_info['image'] = image + call_info['security_groups'] = security_groups + call_info['block_device_mapping'] = block_device_mapping def fake_rpc_run_instance(ctxt, **host_sched_kwargs): call_info['host_sched_kwargs'] = host_sched_kwargs @@ -370,7 +561,16 @@ class CellsSchedulerTestCase(test.TestCase): self.my_cell_state, host_sched_kwargs) # Our cell was selected. self.assertEqual(self.ctxt, call_info['ctxt']) - self.assertEqual(self.request_spec, call_info['request_spec']) + self.assertEqual(self.instance_uuids, call_info['instance_uuids']) + self.assertEqual(self.request_spec['instance_properties'], + call_info['instance_properties']) + self.assertEqual(self.request_spec['instance_type'], + call_info['instance_type']) + self.assertEqual(self.request_spec['image'], call_info['image']) + self.assertEqual(self.request_spec['security_group'], + call_info['security_groups']) + self.assertEqual(self.request_spec['block_device_mapping'], + call_info['block_device_mapping']) self.assertEqual(host_sched_kwargs, call_info['host_sched_kwargs']) # Weight args are correct expected_filt_props = {'context': self.ctxt, diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py index 637596fc5..8b397db02 100644 --- a/nova/tests/conductor/test_conductor.py +++ b/nova/tests/conductor/test_conductor.py @@ -1196,6 +1196,41 @@ class _BaseTaskTestCase(object): self.assertRaises(NotImplementedError, self.conductor.migrate_server, self.context, None, None, True, False, "dummy", None, None) + def test_build_instances(self): + instance_type = flavors.get_default_instance_type() + system_metadata = flavors.save_instance_type_info({}, instance_type) + # NOTE(alaski): instance_type -> system_metadata -> instance_type loses + # some data (extra_specs) so we need both for testing. + instance_type_extract = flavors.extract_instance_type( + {'system_metadata': system_metadata}) + self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi, + 'run_instance') + self.conductor_manager.scheduler_rpcapi.run_instance(self.context, + request_spec={ + 'image': {'fake_data': 'should_pass_silently'}, + 'instance_properties': {'system_metadata': system_metadata, + 'uuid': 'fakeuuid'}, + 'instance_type': instance_type_extract, + 'instance_uuids': ['fakeuuid', 'fakeuuid2'], + 'block_device_mapping': 'block_device_mapping', + 'security_group': 'security_groups'}, + admin_password='admin_password', + injected_files='injected_files', + requested_networks='requested_networks', is_first_time=True, + filter_properties={}) + self.mox.ReplayAll() + self.conductor.build_instances(self.context, + instances=[{'uuid': 'fakeuuid', + 'system_metadata': system_metadata}, + {'uuid': 'fakeuuid2'}], + image={'fake_data': 'should_pass_silently'}, + filter_properties={}, + admin_password='admin_password', + injected_files='injected_files', + requested_networks='requested_networks', + security_groups='security_groups', + block_device_mapping='block_device_mapping') + class ConductorTaskTestCase(_BaseTaskTestCase, test.TestCase): """ComputeTaskManager Tests.""" diff --git a/nova/tests/fake_utils.py b/nova/tests/fake_utils.py new file mode 100644 index 000000000..cb73bc8bb --- /dev/null +++ b/nova/tests/fake_utils.py @@ -0,0 +1,28 @@ +# Copyright (c) 2013 Rackspace Hosting +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""This modules stubs out functions in nova.utils.""" + +from nova import utils + + +def stub_out_utils_spawn_n(stubs): + """Stubs out spawn_n with a blocking version. + + This aids testing async processes by blocking until they're done. + """ + def no_spawn(func, *args, **kwargs): + return func(*args, **kwargs) + + stubs.Set(utils, 'spawn_n', no_spawn) diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index cfd1877f5..c3895678c 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -54,6 +54,7 @@ from nova.tests.api.openstack.compute.contrib import test_services from nova.tests.api.openstack import fakes from nova.tests import fake_instance_actions from nova.tests import fake_network +from nova.tests import fake_utils from nova.tests.image import fake from nova.tests.integrated import integrated_helpers from nova.tests import utils as test_utils @@ -95,6 +96,7 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase): super(ApiSampleTestBase, self).setUp() self.useFixture(test.SampleNetworks()) fake_network.stub_compute_with_ips(self.stubs) + fake_utils.stub_out_utils_spawn_n(self.stubs) self.generate_samples = os.getenv('GENERATE_SAMPLES') is not None def _pretty_data(self, data): -- cgit