From 0fab78825ef06310926181f6f97d377058b56b97 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Sat, 24 Sep 2011 23:49:36 +0000 Subject: compute_api create*() and schedulers refactoring Fixes bug 844160 Makes the servers create API call work with all schedulers, removes 'zone boot', and folds create_instance_helper back into servers controller. Notable changes: 1) compute API's create_at_all_once has been removed. It was only used by zone boot. 2) compute API's create() no longer creates Instance DB entries. The schedulers now do this. This makes sense, as only the schedulers will know where the instances will be placed. They could be placed locally or in a child zone. However, this comes at a cost. compute_api.create() now does a 'call' to the scheduler instead of a 'cast' in most cases (* see below). This is so it can receive the instance ID(s) that were created back from the scheduler. Ultimately, we probably need to figure out a way to generate UUIDs before scheduling and return only the information we know about an instance before it is actually scheduled and created. We could then revert this back to a cast. (Or maybe we always return a reservation ID instead of an instance.) 3) scheduler* calls do not return a host now. They return a value that'll be returned if the caller does an rpc.call(). The casts to hosts are now done by the scheduler drivers themselves. 4) There's been an undocumented feature in the OS API to allow multiple instances to be built. I've kept it. 5) If compute_api.create() is creating multiple instances, only a single call is made to the scheduler, vs the old way of sending many casts. All schedulers now check how many instances have been requested. 6) I've added an undocumented option 'return_reservation_id' when building. If set to True, only a reservation ID is returned to the API caller, not the instance. This essentially gives you the old 'nova zone-boot' functionality. 7) It was requested I create a stub for a zones extension, so you'll see the empty extension in here. We'll move some code to it later. 8) Fixes an unrelated bug that merged into trunk recently where zones DB calls were not being done with admin context always, anymore. 9) Scheduler calls were always done with admin context when they should elevate only when needed. 10) Moved stub_network flag so individual tests can run again. * Case #6 above doesn't wait for the scheduler response with instance IDs. It does a 'cast' instead. Change-Id: Ic040780a2e86d7330e225f14056dadbaa9fb3c7e --- .../api/openstack/contrib/test_createserverext.py | 13 +- nova/tests/api/openstack/contrib/test_volumes.py | 14 +- nova/tests/api/openstack/test_extensions.py | 3 +- nova/tests/api/openstack/test_server_actions.py | 4 +- nova/tests/api/openstack/test_servers.py | 183 ++++++-- nova/tests/integrated/api/client.py | 19 +- nova/tests/integrated/test_servers.py | 36 ++ nova/tests/scheduler/test_abstract_scheduler.py | 75 +++- nova/tests/scheduler/test_least_cost_scheduler.py | 2 +- nova/tests/scheduler/test_scheduler.py | 487 ++++++++++++++------- nova/tests/scheduler/test_vsa_scheduler.py | 30 +- nova/tests/test_compute.py | 153 ++++++- nova/tests/test_quota.py | 17 + 13 files changed, 766 insertions(+), 270 deletions(-) (limited to 'nova/tests') diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index d2fac26c7..9921b54a3 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -25,6 +25,7 @@ import webob from nova import db from nova import exception from nova import flags +from nova import rpc from nova import test import nova.api.openstack from nova.tests.api.openstack import fakes @@ -118,13 +119,15 @@ class CreateserverextTest(test.TestCase): if 'user_data' in kwargs: self.user_data = kwargs['user_data'] - return [{'id': '1234', 'display_name': 'fakeinstance', + resv_id = None + + return ([{'id': '1234', 'display_name': 'fakeinstance', 'uuid': FAKE_UUID, 'user_id': 'fake', 'project_id': 'fake', 'created_at': "", 'updated_at': "", - 'progress': 0}] + 'progress': 0}], resv_id) def set_admin_password(self, *args, **kwargs): pass @@ -133,10 +136,9 @@ class CreateserverextTest(test.TestCase): self.stubs.Set(nova.compute, 'API', self._make_stub_method(compute_api)) self.stubs.Set( - nova.api.openstack.create_instance_helper.CreateInstanceHelper, + nova.api.openstack.servers.Controller, '_get_kernel_ramdisk_from_image', self._make_stub_method((1, 1))) - return compute_api def _setup_mock_network_api(self): @@ -399,7 +401,8 @@ class CreateserverextTest(test.TestCase): self._setup_mock_network_api() body_dict = self._create_security_group_request_dict(security_groups) request = self._get_create_request_json(body_dict) - response = request.get_response(fakes.wsgi_app()) + compute_api, response = \ + self._run_create_instance_with_mock_compute_api(request) self.assertEquals(response.status_int, 202) def test_get_server_by_id_verify_security_groups_json(self): diff --git a/nova/tests/api/openstack/contrib/test_volumes.py b/nova/tests/api/openstack/contrib/test_volumes.py index f61e25d12..9a24b9a54 100644 --- a/nova/tests/api/openstack/contrib/test_volumes.py +++ b/nova/tests/api/openstack/contrib/test_volumes.py @@ -29,8 +29,12 @@ FLAGS = flags.FLAGS def fake_compute_api_create(cls, context, instance_type, image_href, **kwargs): + global _block_device_mapping_seen + _block_device_mapping_seen = kwargs.get('block_device_mapping') + inst_type = instance_types.get_instance_type_by_flavor_id(2) - return [{'id': 1, + resv_id = None + return ([{'id': 1, 'display_name': 'test_server', 'uuid': fake_gen_uuid(), 'instance_type': dict(inst_type), @@ -42,7 +46,7 @@ def fake_compute_api_create(cls, context, instance_type, image_href, **kwargs): 'created_at': datetime.datetime(2010, 10, 10, 12, 0, 0), 'updated_at': datetime.datetime(2010, 11, 11, 11, 0, 0), 'progress': 0 - }] + }], resv_id) def fake_get_instance_nw_info(cls, context, instance): @@ -73,6 +77,8 @@ class BootFromVolumeTest(test.TestCase): delete_on_termination=False, )] )) + global _block_device_mapping_seen + _block_device_mapping_seen = None req = webob.Request.blank('/v1.1/fake/os-volumes_boot') req.method = 'POST' req.body = json.dumps(body) @@ -85,3 +91,7 @@ class BootFromVolumeTest(test.TestCase): self.assertEqual(u'test_server', server['name']) self.assertEqual(3, int(server['image']['id'])) self.assertEqual(FLAGS.password_length, len(server['adminPass'])) + self.assertEqual(len(_block_device_mapping_seen), 1) + self.assertEqual(_block_device_mapping_seen[0]['volume_id'], 1) + self.assertEqual(_block_device_mapping_seen[0]['device_name'], + '/dev/vda') diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index a5c6fe65a..4f66f5405 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -87,6 +87,7 @@ class ExtensionControllerTest(test.TestCase): self.ext_list = [ "Createserverext", "DeferredDelete", + "DiskConfig", "FlavorExtraSpecs", "FlavorExtraData", "Floating_ips", @@ -102,7 +103,7 @@ class ExtensionControllerTest(test.TestCase): "VirtualInterfaces", "Volumes", "VolumeTypes", - "DiskConfig", + "Zones", ] self.ext_list.sort() diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py index 3811fcf0f..04b603237 100644 --- a/nova/tests/api/openstack/test_server_actions.py +++ b/nova/tests/api/openstack/test_server_actions.py @@ -9,7 +9,7 @@ from nova import context from nova import utils from nova import exception from nova import flags -from nova.api.openstack import create_instance_helper +from nova.api.openstack import servers from nova.compute import vm_states from nova.compute import instance_types import nova.db.api @@ -971,7 +971,7 @@ class ServerActionsTestV11(test.TestCase): class TestServerActionXMLDeserializerV11(test.TestCase): def setUp(self): - self.deserializer = create_instance_helper.ServerXMLDeserializerV11() + self.deserializer = servers.ServerXMLDeserializerV11() def tearDown(self): pass diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 29be7a812..fa67db688 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -33,7 +33,6 @@ from nova import flags from nova import test from nova import utils import nova.api.openstack -from nova.api.openstack import create_instance_helper from nova.api.openstack import servers from nova.api.openstack import xmlutil import nova.compute.api @@ -1562,10 +1561,15 @@ class ServersTest(test.TestCase): def _setup_for_create_instance(self): """Shared implementation for tests below that create instance""" + + self.instance_cache_num = 0 + self.instance_cache = {} + def instance_create(context, inst): inst_type = instance_types.get_instance_type_by_flavor_id(3) image_ref = 'http://localhost/images/2' - return {'id': 1, + self.instance_cache_num += 1 + instance = {'id': self.instance_cache_num, 'display_name': 'server_test', 'uuid': FAKE_UUID, 'instance_type': dict(inst_type), @@ -1574,11 +1578,32 @@ class ServersTest(test.TestCase): 'image_ref': image_ref, 'user_id': 'fake', 'project_id': 'fake', + 'reservation_id': inst['reservation_id'], "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), "config_drive": self.config_drive, "progress": 0 } + self.instance_cache[instance['id']] = instance + return instance + + def instance_get(context, instance_id): + """Stub for compute/api create() pulling in instance after + scheduling + """ + return self.instance_cache[instance_id] + + def rpc_call_wrapper(context, topic, msg): + """Stub out the scheduler creating the instance entry""" + if topic == FLAGS.scheduler_topic and \ + msg['method'] == 'run_instance': + request_spec = msg['args']['request_spec'] + num_instances = request_spec.get('num_instances', 1) + instances = [] + for x in xrange(num_instances): + instances.append(instance_create(context, + request_spec['instance_properties'])) + return instances def server_update(context, id, params): return instance_create(context, id) @@ -1601,18 +1626,20 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.db.api, 'project_get_networks', project_get_networks) self.stubs.Set(nova.db.api, 'instance_create', instance_create) + self.stubs.Set(nova.db.api, 'instance_get', instance_get) self.stubs.Set(nova.rpc, 'cast', fake_method) - self.stubs.Set(nova.rpc, 'call', fake_method) + self.stubs.Set(nova.rpc, 'call', rpc_call_wrapper) self.stubs.Set(nova.db.api, 'instance_update', server_update) self.stubs.Set(nova.db.api, 'queue_get_for', queue_get_for) self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip', fake_method) self.stubs.Set( - nova.api.openstack.create_instance_helper.CreateInstanceHelper, - "_get_kernel_ramdisk_from_image", kernel_ramdisk_mapping) + servers.Controller, + "_get_kernel_ramdisk_from_image", + kernel_ramdisk_mapping) self.stubs.Set(nova.compute.api.API, "_find_host", find_host) - def _test_create_instance_helper(self): + def _test_create_instance(self): self._setup_for_create_instance() body = dict(server=dict( @@ -1636,7 +1663,7 @@ class ServersTest(test.TestCase): self.assertEqual(FAKE_UUID, server['uuid']) def test_create_instance(self): - self._test_create_instance_helper() + self._test_create_instance() def test_create_instance_has_uuid(self): """Tests at the db-layer instead of API layer since that's where the @@ -1648,51 +1675,134 @@ class ServersTest(test.TestCase): expected = FAKE_UUID self.assertEqual(instance['uuid'], expected) - def test_create_instance_via_zones(self): - """Server generated ReservationID""" + def test_create_multiple_instances(self): + """Test creating multiple instances but not asking for + reservation_id + """ self._setup_for_create_instance() - self.flags(allow_admin_api=True) - body = dict(server=dict( - name='server_test', imageId=3, flavorId=2, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) - req = webob.Request.blank('/v1.0/zones/boot') + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'min_count': 2, + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [] + } + } + + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) + body = json.loads(res.body) + self.assertIn('server', body) - reservation_id = json.loads(res.body)['reservation_id'] - self.assertEqual(res.status_int, 200) + def test_create_multiple_instances_resv_id_return(self): + """Test creating multiple instances with asking for + reservation_id + """ + self._setup_for_create_instance() + + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'min_count': 2, + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [], + 'return_reservation_id': True + } + } + + req = webob.Request.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) + body = json.loads(res.body) + reservation_id = body.get('reservation_id') self.assertNotEqual(reservation_id, "") self.assertNotEqual(reservation_id, None) self.assertTrue(len(reservation_id) > 1) - def test_create_instance_via_zones_with_resid(self): - """User supplied ReservationID""" + def test_create_instance_with_user_supplied_reservation_id(self): + """Non-admin supplied reservation_id should be ignored.""" self._setup_for_create_instance() - self.flags(allow_admin_api=True) - body = dict(server=dict( - name='server_test', imageId=3, flavorId=2, - metadata={'hello': 'world', 'open': 'stack'}, - personality={}, reservation_id='myresid')) - req = webob.Request.blank('/v1.0/zones/boot') + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [], + 'reservation_id': 'myresid', + 'return_reservation_id': True + } + } + + req = webob.Request.blank('/v1.1/123/servers') req.method = 'POST' req.body = json.dumps(body) req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) + res_body = json.loads(res.body) + self.assertIn('reservation_id', res_body) + self.assertNotEqual(res_body['reservation_id'], 'myresid') + def test_create_instance_with_admin_supplied_reservation_id(self): + """Admin supplied reservation_id should be honored.""" + self._setup_for_create_instance() + + image_href = 'http://localhost/v1.1/123/images/2' + flavor_ref = 'http://localhost/123/flavors/3' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_href, + 'flavorRef': flavor_ref, + 'metadata': {'hello': 'world', + 'open': 'stack'}, + 'personality': [], + 'reservation_id': 'myresid', + 'return_reservation_id': True + } + } + + req = webob.Request.blank('/v1.1/123/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + context = nova.context.RequestContext('testuser', 'testproject', + is_admin=True) + res = req.get_response(fakes.wsgi_app(fake_auth_context=context)) + self.assertEqual(res.status_int, 202) reservation_id = json.loads(res.body)['reservation_id'] - self.assertEqual(res.status_int, 200) self.assertEqual(reservation_id, "myresid") def test_create_instance_no_key_pair(self): fakes.stub_out_key_pair_funcs(self.stubs, have_key_pair=False) - self._test_create_instance_helper() + self._test_create_instance() def test_create_instance_no_name(self): self._setup_for_create_instance() @@ -2782,7 +2892,7 @@ class TestServerStatus(test.TestCase): class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase): def setUp(self): - self.deserializer = create_instance_helper.ServerXMLDeserializer() + self.deserializer = servers.ServerXMLDeserializer() def test_minimal_request(self): serial_request = """ @@ -3068,7 +3178,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): def setUp(self): super(TestServerCreateRequestXMLDeserializerV11, self).setUp() - self.deserializer = create_instance_helper.ServerXMLDeserializerV11() + self.deserializer = servers.ServerXMLDeserializerV11() def test_minimal_request(self): serial_request = """ @@ -3543,10 +3653,12 @@ class TestServerInstanceCreation(test.TestCase): else: self.injected_files = None - return [{'id': '1234', 'display_name': 'fakeinstance', + resv_id = None + + return ([{'id': '1234', 'display_name': 'fakeinstance', 'user_id': 'fake', 'project_id': 'fake', - 'uuid': FAKE_UUID}] + 'uuid': FAKE_UUID}], resv_id) def set_admin_password(self, *args, **kwargs): pass @@ -3559,8 +3671,9 @@ class TestServerInstanceCreation(test.TestCase): compute_api = MockComputeAPI() self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api)) self.stubs.Set( - nova.api.openstack.create_instance_helper.CreateInstanceHelper, - '_get_kernel_ramdisk_from_image', make_stub_method((1, 1))) + servers.Controller, + '_get_kernel_ramdisk_from_image', + make_stub_method((1, 1))) return compute_api def _create_personality_request_dict(self, personality_files): @@ -3821,8 +3934,8 @@ class TestGetKernelRamdiskFromImage(test.TestCase): @staticmethod def _get_k_r(image_meta): """Rebinding function to a shorter name for convenience""" - kernel_id, ramdisk_id = create_instance_helper.CreateInstanceHelper. \ - _do_get_kernel_ramdisk_from_image(image_meta) + kernel_id, ramdisk_id = servers.Controller.\ + _do_get_kernel_ramdisk_from_image(image_meta) return kernel_id, ramdisk_id diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index 67c35fe6b..c942b0108 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -16,6 +16,7 @@ import json import httplib +import urllib import urlparse from nova import log as logging @@ -100,7 +101,7 @@ class TestOpenStackClient(object): relative_url = parsed_url.path if parsed_url.query: - relative_url = relative_url + parsed_url.query + relative_url = relative_url + "?" + parsed_url.query LOG.info(_("Doing %(method)s on %(relative_url)s") % locals()) if body: LOG.info(_("Body: %s") % body) @@ -205,12 +206,24 @@ class TestOpenStackClient(object): def get_server(self, server_id): return self.api_get('/servers/%s' % server_id)['server'] - def get_servers(self, detail=True): + def get_servers(self, detail=True, search_opts=None): rel_url = '/servers/detail' if detail else '/servers' + + if search_opts is not None: + qparams = {} + for opt, val in search_opts.iteritems(): + qparams[opt] = val + if qparams: + query_string = "?%s" % urllib.urlencode(qparams) + rel_url += query_string return self.api_get(rel_url)['servers'] def post_server(self, server): - return self.api_post('/servers', server)['server'] + response = self.api_post('/servers', server) + if 'reservation_id' in response: + return response + else: + return response['server'] def put_server(self, server_id, server): return self.api_put('/servers/%s' % server_id, server) diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index ca3eb7340..4bfad93b1 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -438,6 +438,42 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # Cleanup self._delete_server(server_id) + def test_create_multiple_servers(self): + """Creates multiple servers and checks for reservation_id""" + + # Create 2 servers, setting 'return_reservation_id, which should + # return a reservation_id + server = self._build_minimal_create_server_request() + server['min_count'] = 2 + server['return_reservation_id'] = True + post = {'server': server} + response = self.api.post_server(post) + self.assertIn('reservation_id', response) + reservation_id = response['reservation_id'] + self.assertNotIn(reservation_id, ['', None]) + + # Create 1 more server, which should not return a reservation_id + server = self._build_minimal_create_server_request() + post = {'server': server} + created_server = self.api.post_server(post) + self.assertTrue(created_server['id']) + created_server_id = created_server['id'] + + # lookup servers created by the first request. + servers = self.api.get_servers(detail=True, + search_opts={'reservation_id': reservation_id}) + server_map = dict((server['id'], server) for server in servers) + found_server = server_map.get(created_server_id) + # The server from the 2nd request should not be there. + self.assertEqual(found_server, None) + # Should have found 2 servers. + self.assertEqual(len(server_map), 2) + + # Cleanup + self._delete_server(created_server_id) + for server_id in server_map.iterkeys(): + self._delete_server(server_id) + if __name__ == "__main__": unittest.main() diff --git a/nova/tests/scheduler/test_abstract_scheduler.py b/nova/tests/scheduler/test_abstract_scheduler.py index 5549ea453..da25f1544 100644 --- a/nova/tests/scheduler/test_abstract_scheduler.py +++ b/nova/tests/scheduler/test_abstract_scheduler.py @@ -20,6 +20,7 @@ import json import nova.db +from nova import context from nova import exception from nova import rpc from nova import test @@ -102,7 +103,7 @@ def fake_empty_call_zone_method(context, method, specs, zones): was_called = False -def fake_provision_resource(context, item, instance_id, request_spec, kwargs): +def fake_provision_resource(context, item, request_spec, kwargs): global was_called was_called = True @@ -118,8 +119,7 @@ def fake_provision_resource_locally(context, build_plan, request_spec, kwargs): was_called = True -def fake_provision_resource_from_blob(context, item, instance_id, - request_spec, kwargs): +def fake_provision_resource_from_blob(context, item, request_spec, kwargs): global was_called was_called = True @@ -185,7 +185,7 @@ class AbstractSchedulerTestCase(test.TestCase): zm = FakeZoneManager() sched.set_zone_manager(zm) - fake_context = {} + fake_context = context.RequestContext('user', 'project') build_plan = sched.select(fake_context, {'instance_type': {'memory_mb': 512}, 'num_instances': 4}) @@ -229,9 +229,10 @@ class AbstractSchedulerTestCase(test.TestCase): zm = FakeEmptyZoneManager() sched.set_zone_manager(zm) - fake_context = {} + fake_context = context.RequestContext('user', 'project') + request_spec = {} self.assertRaises(driver.NoValidHost, sched.schedule_run_instance, - fake_context, 1, + fake_context, request_spec, dict(host_filter=None, instance_type={})) def test_schedule_do_not_schedule_with_hint(self): @@ -250,8 +251,8 @@ class AbstractSchedulerTestCase(test.TestCase): 'blob': "Non-None blob data", } - result = sched.schedule_run_instance(None, 1, request_spec) - self.assertEquals(None, result) + instances = sched.schedule_run_instance(None, request_spec) + self.assertTrue(instances) self.assertTrue(was_called) def test_provision_resource_local(self): @@ -263,7 +264,7 @@ class AbstractSchedulerTestCase(test.TestCase): fake_provision_resource_locally) request_spec = {'hostname': "foo"} - sched._provision_resource(None, request_spec, 1, request_spec, {}) + sched._provision_resource(None, request_spec, request_spec, {}) self.assertTrue(was_called) def test_provision_resource_remote(self): @@ -275,7 +276,7 @@ class AbstractSchedulerTestCase(test.TestCase): fake_provision_resource_from_blob) request_spec = {} - sched._provision_resource(None, request_spec, 1, request_spec, {}) + sched._provision_resource(None, request_spec, request_spec, {}) self.assertTrue(was_called) def test_provision_resource_from_blob_empty(self): @@ -285,7 +286,7 @@ class AbstractSchedulerTestCase(test.TestCase): request_spec = {} self.assertRaises(abstract_scheduler.InvalidBlob, sched._provision_resource_from_blob, - None, {}, 1, {}, {}) + None, {}, {}, {}) def test_provision_resource_from_blob_with_local_blob(self): """ @@ -303,20 +304,21 @@ class AbstractSchedulerTestCase(test.TestCase): # return fake instances return {'id': 1, 'uuid': 'f874093c-7b17-49c0-89c3-22a5348497f9'} - def fake_rpc_cast(*args, **kwargs): + def fake_cast_to_compute_host(*args, **kwargs): pass self.stubs.Set(sched, '_decrypt_blob', fake_decrypt_blob_returns_local_info) + self.stubs.Set(driver, 'cast_to_compute_host', + fake_cast_to_compute_host) self.stubs.Set(compute_api.API, 'create_db_entry_for_new_instance', fake_create_db_entry_for_new_instance) - self.stubs.Set(rpc, 'cast', fake_rpc_cast) build_plan_item = {'blob': "Non-None blob data"} request_spec = {'image': {}, 'instance_properties': {}} - sched._provision_resource_from_blob(None, build_plan_item, 1, + sched._provision_resource_from_blob(None, build_plan_item, request_spec, {}) self.assertTrue(was_called) @@ -335,7 +337,7 @@ class AbstractSchedulerTestCase(test.TestCase): request_spec = {'blob': "Non-None blob data"} - sched._provision_resource_from_blob(None, request_spec, 1, + sched._provision_resource_from_blob(None, request_spec, request_spec, {}) self.assertTrue(was_called) @@ -352,7 +354,7 @@ class AbstractSchedulerTestCase(test.TestCase): request_spec = {'child_blob': True, 'child_zone': True} - sched._provision_resource_from_blob(None, request_spec, 1, + sched._provision_resource_from_blob(None, request_spec, request_spec, {}) self.assertTrue(was_called) @@ -386,7 +388,7 @@ class AbstractSchedulerTestCase(test.TestCase): zm.service_states = {} sched.set_zone_manager(zm) - fake_context = {} + fake_context = context.RequestContext('user', 'project') build_plan = sched.select(fake_context, {'instance_type': {'memory_mb': 512}, 'num_instances': 4}) @@ -394,6 +396,45 @@ class AbstractSchedulerTestCase(test.TestCase): # 0 from local zones, 12 from remotes self.assertEqual(12, len(build_plan)) + def test_run_instance_non_admin(self): + """Test creating an instance locally using run_instance, passing + a non-admin context. DB actions should work.""" + sched = FakeAbstractScheduler() + + def fake_cast_to_compute_host(*args, **kwargs): + pass + + def fake_zone_get_all_zero(context): + # make sure this is called with admin context, even though + # we're using user context below + self.assertTrue(context.is_admin) + return [] + + self.stubs.Set(driver, 'cast_to_compute_host', + fake_cast_to_compute_host) + self.stubs.Set(sched, '_call_zone_method', fake_call_zone_method) + self.stubs.Set(nova.db, 'zone_get_all', fake_zone_get_all_zero) + + zm = FakeZoneManager() + sched.set_zone_manager(zm) + + fake_context = context.RequestContext('user', 'project') + + request_spec = { + 'image': {'properties': {}}, + 'security_group': [], + 'instance_properties': { + 'project_id': fake_context.project_id, + 'user_id': fake_context.user_id}, + 'instance_type': {'memory_mb': 256}, + 'filter_driver': 'nova.scheduler.host_filter.AllHostsFilter' + } + + instances = sched.schedule_run_instance(fake_context, request_spec) + self.assertEqual(len(instances), 1) + self.assertFalse(instances[0].get('_is_precooked', False)) + nova.db.instance_destroy(fake_context, instances[0]['id']) + class BaseSchedulerTestCase(test.TestCase): """Test case for Base Scheduler.""" diff --git a/nova/tests/scheduler/test_least_cost_scheduler.py b/nova/tests/scheduler/test_least_cost_scheduler.py index af58de527..b8847a2bf 100644 --- a/nova/tests/scheduler/test_least_cost_scheduler.py +++ b/nova/tests/scheduler/test_least_cost_scheduler.py @@ -134,7 +134,7 @@ class LeastCostSchedulerTestCase(test.TestCase): expected = [] for idx, (hostname, services) in enumerate(hosts): - caps = copy.deepcopy(services["compute"]) + caps = copy.deepcopy(services) # Costs are normalized so over 10 hosts, each host with increasing # free ram will cost 1/N more. Since the lowest cost host has some # free ram, we add in the 1/N for the base_cost diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py index 3a10e9287..77db3520b 100644 --- a/nova/tests/scheduler/test_scheduler.py +++ b/nova/tests/scheduler/test_scheduler.py @@ -35,10 +35,13 @@ from nova import service from nova import test from nova import rpc from nova import utils +from nova.db.sqlalchemy import models from nova.scheduler import api from nova.scheduler import driver from nova.scheduler import manager from nova.scheduler import multi +from nova.scheduler.simple import SimpleScheduler +from nova.scheduler.zone import ZoneScheduler from nova.compute import power_state from nova.compute import vm_states @@ -53,17 +56,86 @@ FAKE_UUID_NOT_FOUND = 'ffffffff-ffff-ffff-ffff-ffffffffffff' FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' -class FakeContext(object): - auth_token = None +def _create_instance_dict(**kwargs): + """Create a dictionary for a test instance""" + inst = {} + # NOTE(jk0): If an integer is passed as the image_ref, the image + # service will use the default image service (in this case, the fake). + inst['image_ref'] = '1' + inst['reservation_id'] = 'r-fakeres' + inst['user_id'] = kwargs.get('user_id', 'admin') + inst['project_id'] = kwargs.get('project_id', 'fake') + inst['instance_type_id'] = '1' + if 'host' in kwargs: + inst['host'] = kwargs.get('host') + inst['vcpus'] = kwargs.get('vcpus', 1) + inst['memory_mb'] = kwargs.get('memory_mb', 20) + inst['local_gb'] = kwargs.get('local_gb', 30) + inst['vm_state'] = kwargs.get('vm_state', vm_states.ACTIVE) + inst['power_state'] = kwargs.get('power_state', power_state.RUNNING) + inst['task_state'] = kwargs.get('task_state', None) + inst['availability_zone'] = kwargs.get('availability_zone', None) + inst['ami_launch_index'] = 0 + inst['launched_on'] = kwargs.get('launched_on', 'dummy') + return inst + + +def _create_volume(): + """Create a test volume""" + vol = {} + vol['size'] = 1 + vol['availability_zone'] = 'test' + ctxt = context.get_admin_context() + return db.volume_create(ctxt, vol)['id'] + + +def _create_instance(**kwargs): + """Create a test instance""" + ctxt = context.get_admin_context() + return db.instance_create(ctxt, _create_instance_dict(**kwargs)) + + +def _create_instance_from_spec(spec): + return _create_instance(**spec['instance_properties']) + + +def _create_request_spec(**kwargs): + return dict(instance_properties=_create_instance_dict(**kwargs)) + + +def _fake_cast_to_compute_host(context, host, method, **kwargs): + global _picked_host + _picked_host = host + + +def _fake_cast_to_volume_host(context, host, method, **kwargs): + global _picked_host + _picked_host = host + + +def _fake_create_instance_db_entry(simple_self, context, request_spec): + instance = _create_instance_from_spec(request_spec) + global instance_ids + instance_ids.append(instance['id']) + return instance + + +class FakeContext(context.RequestContext): + def __init__(self, *args, **kwargs): + super(FakeContext, self).__init__('user', 'project', **kwargs) class TestDriver(driver.Scheduler): """Scheduler Driver for Tests""" - def schedule(context, topic, *args, **kwargs): - return 'fallback_host' + def schedule(self, context, topic, method, *args, **kwargs): + host = 'fallback_host' + driver.cast_to_host(context, topic, host, method, **kwargs) - def schedule_named_method(context, topic, num): - return 'named_host' + def schedule_named_method(self, context, num=None): + topic = 'topic' + host = 'named_host' + method = 'named_method' + driver.cast_to_host(context, topic, host, method, num=num) class SchedulerTestCase(test.TestCase): @@ -89,31 +161,16 @@ class SchedulerTestCase(test.TestCase): return db.service_get(ctxt, s_ref['id']) - def _create_instance(self, **kwargs): - """Create a test instance""" - ctxt = context.get_admin_context() - inst = {} - inst['user_id'] = 'admin' - inst['project_id'] = kwargs.get('project_id', 'fake') - inst['host'] = kwargs.get('host', 'dummy') - inst['vcpus'] = kwargs.get('vcpus', 1) - inst['memory_mb'] = kwargs.get('memory_mb', 10) - inst['local_gb'] = kwargs.get('local_gb', 20) - inst['vm_state'] = kwargs.get('vm_state', vm_states.ACTIVE) - inst['power_state'] = kwargs.get('power_state', power_state.RUNNING) - inst['task_state'] = kwargs.get('task_state', None) - return db.instance_create(ctxt, inst) - def test_fallback(self): scheduler = manager.SchedulerManager() self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True) ctxt = context.get_admin_context() rpc.cast(ctxt, - 'topic.fallback_host', + 'fake_topic.fallback_host', {'method': 'noexist', 'args': {'num': 7}}) self.mox.ReplayAll() - scheduler.noexist(ctxt, 'topic', num=7) + scheduler.noexist(ctxt, 'fake_topic', num=7) def test_named_method(self): scheduler = manager.SchedulerManager() @@ -173,8 +230,8 @@ class SchedulerTestCase(test.TestCase): scheduler = manager.SchedulerManager() ctxt = context.get_admin_context() s_ref = self._create_compute_service() - i_ref1 = self._create_instance(project_id='p-01', host=s_ref['host']) - i_ref2 = self._create_instance(project_id='p-02', vcpus=3, + i_ref1 = _create_instance(project_id='p-01', host=s_ref['host']) + i_ref2 = _create_instance(project_id='p-02', vcpus=3, host=s_ref['host']) result = scheduler.show_host_resources(ctxt, s_ref['host']) @@ -197,7 +254,10 @@ class ZoneSchedulerTestCase(test.TestCase): """Test case for zone scheduler""" def setUp(self): super(ZoneSchedulerTestCase, self).setUp() - self.flags(scheduler_driver='nova.scheduler.zone.ZoneScheduler') + self.flags( + scheduler_driver='nova.scheduler.multi.MultiScheduler', + compute_scheduler_driver='nova.scheduler.zone.ZoneScheduler', + volume_scheduler_driver='nova.scheduler.zone.ZoneScheduler') def _create_service_model(self, **kwargs): service = db.sqlalchemy.models.Service() @@ -214,7 +274,7 @@ class ZoneSchedulerTestCase(test.TestCase): def test_with_two_zones(self): scheduler = manager.SchedulerManager() - ctxt = context.get_admin_context() + ctxt = context.RequestContext('user', 'project') service_list = [self._create_service_model(id=1, host='host1', zone='zone1'), @@ -230,66 +290,53 @@ class ZoneSchedulerTestCase(test.TestCase): self._create_service_model(id=5, host='host5', zone='zone2')] + + request_spec = _create_request_spec(availability_zone='zone1') + + fake_instance = _create_instance_dict( + **request_spec['instance_properties']) + fake_instance['id'] = 100 + fake_instance['uuid'] = FAKE_UUID + self.mox.StubOutWithMock(db, 'service_get_all_by_topic') + self.mox.StubOutWithMock(db, 'instance_update') + # Assumes we're testing with MultiScheduler + compute_sched_driver = scheduler.driver.drivers['compute'] + self.mox.StubOutWithMock(compute_sched_driver, + 'create_instance_db_entry') + self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True) + arg = IgnoreArg() db.service_get_all_by_topic(arg, arg).AndReturn(service_list) - self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True) - rpc.cast(ctxt, + compute_sched_driver.create_instance_db_entry(arg, + request_spec).AndReturn(fake_instance) + db.instance_update(arg, 100, {'host': 'host1', 'scheduled_at': arg}) + rpc.cast(arg, 'compute.host1', {'method': 'run_instance', - 'args': {'instance_id': 'i-ffffffff', - 'availability_zone': 'zone1'}}) + 'args': {'instance_id': 100}}) self.mox.ReplayAll() scheduler.run_instance(ctxt, 'compute', - instance_id='i-ffffffff', - availability_zone='zone1') + request_spec=request_spec) class SimpleDriverTestCase(test.TestCase): """Test case for simple driver""" def setUp(self): super(SimpleDriverTestCase, self).setUp() + simple_scheduler = 'nova.scheduler.simple.SimpleScheduler' self.flags(connection_type='fake', - stub_network=True, - max_cores=4, - max_gigabytes=4, - network_manager='nova.network.manager.FlatManager', - volume_driver='nova.volume.driver.FakeISCSIDriver', - scheduler_driver='nova.scheduler.simple.SimpleScheduler') + stub_network=True, + max_cores=4, + max_gigabytes=4, + network_manager='nova.network.manager.FlatManager', + volume_driver='nova.volume.driver.FakeISCSIDriver', + scheduler_driver='nova.scheduler.multi.MultiScheduler', + compute_scheduler_driver=simple_scheduler, + volume_scheduler_driver=simple_scheduler) self.scheduler = manager.SchedulerManager() self.context = context.get_admin_context() - self.user_id = 'fake' - self.project_id = 'fake' - - def _create_instance(self, **kwargs): - """Create a test instance""" - inst = {} - # NOTE(jk0): If an integer is passed as the image_ref, the image - # service will use the default image service (in this case, the fake). - inst['image_ref'] = '1' - inst['reservation_id'] = 'r-fakeres' - inst['user_id'] = self.user_id - inst['project_id'] = self.project_id - inst['instance_type_id'] = '1' - inst['vcpus'] = kwargs.get('vcpus', 1) - inst['ami_launch_index'] = 0 - inst['availability_zone'] = kwargs.get('availability_zone', None) - inst['host'] = kwargs.get('host', 'dummy') - inst['memory_mb'] = kwargs.get('memory_mb', 20) - inst['local_gb'] = kwargs.get('local_gb', 30) - inst['launched_on'] = kwargs.get('launghed_on', 'dummy') - inst['vm_state'] = kwargs.get('vm_state', vm_states.ACTIVE) - inst['task_state'] = kwargs.get('task_state', None) - inst['power_state'] = kwargs.get('power_state', power_state.RUNNING) - return db.instance_create(self.context, inst)['id'] - - def _create_volume(self): - """Create a test volume""" - vol = {} - vol['size'] = 1 - vol['availability_zone'] = 'test' - return db.volume_create(self.context, vol)['id'] def _create_compute_service(self, **kwargs): """Create a compute service.""" @@ -369,14 +416,30 @@ class SimpleDriverTestCase(test.TestCase): 'compute', FLAGS.compute_manager) compute2.start() - instance_id1 = self._create_instance() - compute1.run_instance(self.context, instance_id1) - instance_id2 = self._create_instance() - host = self.scheduler.driver.schedule_run_instance(self.context, - instance_id2) - self.assertEqual(host, 'host2') - compute1.terminate_instance(self.context, instance_id1) - db.instance_destroy(self.context, instance_id2) + + global instance_ids + instance_ids = [] + instance_ids.append(_create_instance()['id']) + compute1.run_instance(self.context, instance_ids[0]) + + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _fake_create_instance_db_entry) + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec() + instances = self.scheduler.driver.schedule_run_instance( + self.context, request_spec) + + self.assertEqual(_picked_host, 'host2') + self.assertEqual(len(instance_ids), 2) + self.assertEqual(len(instances), 1) + self.assertEqual(instances[0].get('_is_precooked', False), False) + + compute1.terminate_instance(self.context, instance_ids[0]) + compute2.terminate_instance(self.context, instance_ids[1]) compute1.kill() compute2.kill() @@ -392,14 +455,27 @@ class SimpleDriverTestCase(test.TestCase): 'compute', FLAGS.compute_manager) compute2.start() - instance_id1 = self._create_instance() - compute1.run_instance(self.context, instance_id1) - instance_id2 = self._create_instance(availability_zone='nova:host1') - host = self.scheduler.driver.schedule_run_instance(self.context, - instance_id2) - self.assertEqual('host1', host) - compute1.terminate_instance(self.context, instance_id1) - db.instance_destroy(self.context, instance_id2) + + global instance_ids + instance_ids = [] + instance_ids.append(_create_instance()['id']) + compute1.run_instance(self.context, instance_ids[0]) + + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _fake_create_instance_db_entry) + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec(availability_zone='nova:host1') + instances = self.scheduler.driver.schedule_run_instance( + self.context, request_spec) + self.assertEqual(_picked_host, 'host1') + self.assertEqual(len(instance_ids), 2) + + compute1.terminate_instance(self.context, instance_ids[0]) + compute1.terminate_instance(self.context, instance_ids[1]) compute1.kill() compute2.kill() @@ -414,12 +490,21 @@ class SimpleDriverTestCase(test.TestCase): delta = datetime.timedelta(seconds=FLAGS.service_down_time * 2) past = now - delta db.service_update(self.context, s1['id'], {'updated_at': past}) - instance_id2 = self._create_instance(availability_zone='nova:host1') + + global instance_ids + instance_ids = [] + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _fake_create_instance_db_entry) + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec(availability_zone='nova:host1') self.assertRaises(driver.WillNotSchedule, self.scheduler.driver.schedule_run_instance, self.context, - instance_id2) - db.instance_destroy(self.context, instance_id2) + request_spec) compute1.kill() def test_will_schedule_on_disabled_host_if_specified_no_queue(self): @@ -430,11 +515,22 @@ class SimpleDriverTestCase(test.TestCase): compute1.start() s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute') db.service_update(self.context, s1['id'], {'disabled': True}) - instance_id2 = self._create_instance(availability_zone='nova:host1') - host = self.scheduler.driver.schedule_run_instance(self.context, - instance_id2) - self.assertEqual('host1', host) - db.instance_destroy(self.context, instance_id2) + + global instance_ids + instance_ids = [] + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _fake_create_instance_db_entry) + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec(availability_zone='nova:host1') + instances = self.scheduler.driver.schedule_run_instance( + self.context, request_spec) + self.assertEqual(_picked_host, 'host1') + self.assertEqual(len(instance_ids), 1) + compute1.terminate_instance(self.context, instance_ids[0]) compute1.kill() def test_too_many_cores_no_queue(self): @@ -452,17 +548,17 @@ class SimpleDriverTestCase(test.TestCase): instance_ids1 = [] instance_ids2 = [] for index in xrange(FLAGS.max_cores): - instance_id = self._create_instance() + instance_id = _create_instance()['id'] compute1.run_instance(self.context, instance_id) instance_ids1.append(instance_id) - instance_id = self._create_instance() + instance_id = _create_instance()['id'] compute2.run_instance(self.context, instance_id) instance_ids2.append(instance_id) - instance_id = self._create_instance() + request_spec = _create_request_spec() self.assertRaises(driver.NoValidHost, self.scheduler.driver.schedule_run_instance, self.context, - instance_id) + request_spec) for instance_id in instance_ids1: compute1.terminate_instance(self.context, instance_id) for instance_id in instance_ids2: @@ -481,13 +577,19 @@ class SimpleDriverTestCase(test.TestCase): 'nova-volume', 'volume', FLAGS.volume_manager) + + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_volume_host', _fake_cast_to_volume_host) + volume2.start() - volume_id1 = self._create_volume() + volume_id1 = _create_volume() volume1.create_volume(self.context, volume_id1) - volume_id2 = self._create_volume() - host = self.scheduler.driver.schedule_create_volume(self.context, - volume_id2) - self.assertEqual(host, 'host2') + volume_id2 = _create_volume() + self.scheduler.driver.schedule_create_volume(self.context, + volume_id2) + self.assertEqual(_picked_host, 'host2') volume1.delete_volume(self.context, volume_id1) db.volume_destroy(self.context, volume_id2) @@ -514,17 +616,30 @@ class SimpleDriverTestCase(test.TestCase): compute2.kill() def test_least_busy_host_gets_instance(self): - """Ensures the host with less cores gets the next one""" + """Ensures the host with less cores gets the next one w/ Simple""" compute1 = self.start_service('compute', host='host1') compute2 = self.start_service('compute', host='host2') - instance_id1 = self._create_instance() - compute1.run_instance(self.context, instance_id1) - instance_id2 = self._create_instance() - host = self.scheduler.driver.schedule_run_instance(self.context, - instance_id2) - self.assertEqual(host, 'host2') - compute1.terminate_instance(self.context, instance_id1) - db.instance_destroy(self.context, instance_id2) + + global instance_ids + instance_ids = [] + instance_ids.append(_create_instance()['id']) + compute1.run_instance(self.context, instance_ids[0]) + + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _fake_create_instance_db_entry) + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec() + instances = self.scheduler.driver.schedule_run_instance( + self.context, request_spec) + self.assertEqual(_picked_host, 'host2') + self.assertEqual(len(instance_ids), 2) + + compute1.terminate_instance(self.context, instance_ids[0]) + compute2.terminate_instance(self.context, instance_ids[1]) compute1.kill() compute2.kill() @@ -532,41 +647,64 @@ class SimpleDriverTestCase(test.TestCase): """Ensures if you set availability_zone it launches on that zone""" compute1 = self.start_service('compute', host='host1') compute2 = self.start_service('compute', host='host2') - instance_id1 = self._create_instance() - compute1.run_instance(self.context, instance_id1) - instance_id2 = self._create_instance(availability_zone='nova:host1') - host = self.scheduler.driver.schedule_run_instance(self.context, - instance_id2) - self.assertEqual('host1', host) - compute1.terminate_instance(self.context, instance_id1) - db.instance_destroy(self.context, instance_id2) + + global instance_ids + instance_ids = [] + instance_ids.append(_create_instance()['id']) + compute1.run_instance(self.context, instance_ids[0]) + + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _fake_create_instance_db_entry) + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec(availability_zone='nova:host1') + instances = self.scheduler.driver.schedule_run_instance( + self.context, request_spec) + self.assertEqual(_picked_host, 'host1') + self.assertEqual(len(instance_ids), 2) + + compute1.terminate_instance(self.context, instance_ids[0]) + compute1.terminate_instance(self.context, instance_ids[1]) compute1.kill() compute2.kill() - def test_wont_sechedule_if_specified_host_is_down(self): + def test_wont_schedule_if_specified_host_is_down(self): compute1 = self.start_service('compute', host='host1') s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute') now = utils.utcnow() delta = datetime.timedelta(seconds=FLAGS.service_down_time * 2) past = now - delta db.service_update(self.context, s1['id'], {'updated_at': past}) - instance_id2 = self._create_instance(availability_zone='nova:host1') + request_spec = _create_request_spec(availability_zone='nova:host1') self.assertRaises(driver.WillNotSchedule, self.scheduler.driver.schedule_run_instance, self.context, - instance_id2) - db.instance_destroy(self.context, instance_id2) + request_spec) compute1.kill() def test_will_schedule_on_disabled_host_if_specified(self): compute1 = self.start_service('compute', host='host1') s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute') db.service_update(self.context, s1['id'], {'disabled': True}) - instance_id2 = self._create_instance(availability_zone='nova:host1') - host = self.scheduler.driver.schedule_run_instance(self.context, - instance_id2) - self.assertEqual('host1', host) - db.instance_destroy(self.context, instance_id2) + + global instance_ids + instance_ids = [] + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _fake_create_instance_db_entry) + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec(availability_zone='nova:host1') + instances = self.scheduler.driver.schedule_run_instance( + self.context, request_spec) + self.assertEqual(_picked_host, 'host1') + self.assertEqual(len(instance_ids), 1) + compute1.terminate_instance(self.context, instance_ids[0]) compute1.kill() def test_too_many_cores(self): @@ -576,18 +714,30 @@ class SimpleDriverTestCase(test.TestCase): instance_ids1 = [] instance_ids2 = [] for index in xrange(FLAGS.max_cores): - instance_id = self._create_instance() + instance_id = _create_instance()['id'] compute1.run_instance(self.context, instance_id) instance_ids1.append(instance_id) - instance_id = self._create_instance() + instance_id = _create_instance()['id'] compute2.run_instance(self.context, instance_id) instance_ids2.append(instance_id) - instance_id = self._create_instance() + + def _create_instance_db_entry(simple_self, context, request_spec): + self.fail(_("Shouldn't try to create DB entry when at " + "max cores")) + self.stubs.Set(SimpleScheduler, + 'create_instance_db_entry', _create_instance_db_entry) + + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_compute_host', _fake_cast_to_compute_host) + + request_spec = _create_request_spec() + self.assertRaises(driver.NoValidHost, self.scheduler.driver.schedule_run_instance, self.context, - instance_id) - db.instance_destroy(self.context, instance_id) + request_spec) for instance_id in instance_ids1: compute1.terminate_instance(self.context, instance_id) for instance_id in instance_ids2: @@ -599,12 +749,18 @@ class SimpleDriverTestCase(test.TestCase): """Ensures the host with less gigabytes gets the next one""" volume1 = self.start_service('volume', host='host1') volume2 = self.start_service('volume', host='host2') - volume_id1 = self._create_volume() + + global _picked_host + _picked_host = None + self.stubs.Set(driver, + 'cast_to_volume_host', _fake_cast_to_volume_host) + + volume_id1 = _create_volume() volume1.create_volume(self.context, volume_id1) - volume_id2 = self._create_volume() - host = self.scheduler.driver.schedule_create_volume(self.context, - volume_id2) - self.assertEqual(host, 'host2') + volume_id2 = _create_volume() + self.scheduler.driver.schedule_create_volume(self.context, + volume_id2) + self.assertEqual(_picked_host, 'host2') volume1.delete_volume(self.context, volume_id1) db.volume_destroy(self.context, volume_id2) volume1.kill() @@ -617,13 +773,13 @@ class SimpleDriverTestCase(test.TestCase): volume_ids1 = [] volume_ids2 = [] for index in xrange(FLAGS.max_gigabytes): - volume_id = self._create_volume() + volume_id = _create_volume() volume1.create_volume(self.context, volume_id) volume_ids1.append(volume_id) - volume_id = self._create_volume() + volume_id = _create_volume() volume2.create_volume(self.context, volume_id) volume_ids2.append(volume_id) - volume_id = self._create_volume() + volume_id = _create_volume() self.assertRaises(driver.NoValidHost, self.scheduler.driver.schedule_create_volume, self.context, @@ -636,13 +792,13 @@ class SimpleDriverTestCase(test.TestCase): volume2.kill() def test_scheduler_live_migration_with_volume(self): - """scheduler_live_migration() works correctly as expected. + """schedule_live_migration() works correctly as expected. Also, checks instance state is changed from 'running' -> 'migrating'. """ - instance_id = self._create_instance() + instance_id = _create_instance(host='dummy')['id'] i_ref = db.instance_get(self.context, instance_id) dic = {'instance_id': instance_id, 'size': 1} v_ref = db.volume_create(self.context, dic) @@ -680,7 +836,8 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_src_check_instance_not_running(self): """The instance given by instance_id is not running.""" - instance_id = self._create_instance(power_state=power_state.NOSTATE) + instance_id = _create_instance( + power_state=power_state.NOSTATE)['id'] i_ref = db.instance_get(self.context, instance_id) try: @@ -695,7 +852,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_src_check_volume_node_not_alive(self): """Raise exception when volume node is not alive.""" - instance_id = self._create_instance() + instance_id = _create_instance()['id'] i_ref = db.instance_get(self.context, instance_id) dic = {'instance_id': instance_id, 'size': 1} v_ref = db.volume_create(self.context, {'instance_id': instance_id, @@ -715,7 +872,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_src_check_compute_node_not_alive(self): """Confirms src-compute node is alive.""" - instance_id = self._create_instance() + instance_id = _create_instance()['id'] i_ref = db.instance_get(self.context, instance_id) t = utils.utcnow() - datetime.timedelta(10) s_ref = self._create_compute_service(created_at=t, updated_at=t, @@ -730,7 +887,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_src_check_works_correctly(self): """Confirms this method finishes with no error.""" - instance_id = self._create_instance() + instance_id = _create_instance()['id'] i_ref = db.instance_get(self.context, instance_id) s_ref = self._create_compute_service(host=i_ref['host']) @@ -743,7 +900,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_dest_check_not_alive(self): """Confirms exception raises in case dest host does not exist.""" - instance_id = self._create_instance() + instance_id = _create_instance()['id'] i_ref = db.instance_get(self.context, instance_id) t = utils.utcnow() - datetime.timedelta(10) s_ref = self._create_compute_service(created_at=t, updated_at=t, @@ -758,7 +915,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_dest_check_service_same_host(self): """Confirms exceptioin raises in case dest and src is same host.""" - instance_id = self._create_instance() + instance_id = _create_instance()['id'] i_ref = db.instance_get(self.context, instance_id) s_ref = self._create_compute_service(host=i_ref['host']) @@ -771,9 +928,9 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_dest_check_service_lack_memory(self): """Confirms exception raises when dest doesn't have enough memory.""" - instance_id = self._create_instance() - instance_id2 = self._create_instance(host='somewhere', - memory_mb=12) + instance_id = _create_instance()['id'] + instance_id2 = _create_instance(host='somewhere', + memory_mb=12)['id'] i_ref = db.instance_get(self.context, instance_id) s_ref = self._create_compute_service(host='somewhere') @@ -787,9 +944,9 @@ class SimpleDriverTestCase(test.TestCase): def test_block_migration_dest_check_service_lack_disk(self): """Confirms exception raises when dest doesn't have enough disk.""" - instance_id = self._create_instance() - instance_id2 = self._create_instance(host='somewhere', - local_gb=70) + instance_id = _create_instance()['id'] + instance_id2 = _create_instance(host='somewhere', + local_gb=70)['id'] i_ref = db.instance_get(self.context, instance_id) s_ref = self._create_compute_service(host='somewhere') @@ -803,7 +960,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_dest_check_service_works_correctly(self): """Confirms method finishes with no error.""" - instance_id = self._create_instance() + instance_id = _create_instance()['id'] i_ref = db.instance_get(self.context, instance_id) s_ref = self._create_compute_service(host='somewhere', memory_mb_used=5) @@ -821,7 +978,7 @@ class SimpleDriverTestCase(test.TestCase): dest = 'dummydest' # mocks for live_migration_common_check() - instance_id = self._create_instance() + instance_id = _create_instance()['id'] i_ref = db.instance_get(self.context, instance_id) t1 = utils.utcnow() - datetime.timedelta(10) s_ref = self._create_compute_service(created_at=t1, updated_at=t1, @@ -855,7 +1012,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_common_check_service_different_hypervisor(self): """Original host and dest host has different hypervisor type.""" dest = 'dummydest' - instance_id = self._create_instance() + instance_id = _create_instance(host='dummy')['id'] i_ref = db.instance_get(self.context, instance_id) # compute service for destination @@ -880,7 +1037,7 @@ class SimpleDriverTestCase(test.TestCase): def test_live_migration_common_check_service_different_version(self): """Original host and dest host has different hypervisor version.""" dest = 'dummydest' - instance_id = self._create_instance() + instance_id = _create_instance(host='dummy')['id'] i_ref = db.instance_get(self.context, instance_id) # compute service for destination @@ -904,10 +1061,10 @@ class SimpleDriverTestCase(test.TestCase): db.service_destroy(self.context, s_ref2['id']) def test_live_migration_common_check_checking_cpuinfo_fail(self): - """Raise excetion when original host doen't have compatible cpu.""" + """Raise exception when original host doesn't have compatible cpu.""" dest = 'dummydest' - instance_id = self._create_instance() + instance_id = _create_instance(host='dummy')['id'] i_ref = db.instance_get(self.context, instance_id) # compute service for destination @@ -927,7 +1084,7 @@ class SimpleDriverTestCase(test.TestCase): self.mox.ReplayAll() try: - self.scheduler.driver._live_migration_common_check(self.context, + driver._live_migration_common_check(self.context, i_ref, dest, False) @@ -1021,7 +1178,6 @@ class FakeResource(object): class ZoneRedirectTest(test.TestCase): def setUp(self): super(ZoneRedirectTest, self).setUp() - self.stubs = stubout.StubOutForTesting() self.stubs.Set(db, 'zone_get_all', zone_get_all) self.stubs.Set(db, 'instance_get_by_uuid', @@ -1029,7 +1185,6 @@ class ZoneRedirectTest(test.TestCase): self.flags(enable_zone_routing=True) def tearDown(self): - self.stubs.UnsetAll() super(ZoneRedirectTest, self).tearDown() def test_trap_found_locally(self): @@ -1257,12 +1412,10 @@ class FakeNovaClientZones(object): class CallZoneMethodTest(test.TestCase): def setUp(self): super(CallZoneMethodTest, self).setUp() - self.stubs = stubout.StubOutForTesting() self.stubs.Set(db, 'zone_get_all', zone_get_all) self.stubs.Set(novaclient, 'Client', FakeNovaClientZones) def tearDown(self): - self.stubs.UnsetAll() super(CallZoneMethodTest, self).tearDown() def test_call_zone_method(self): diff --git a/nova/tests/scheduler/test_vsa_scheduler.py b/nova/tests/scheduler/test_vsa_scheduler.py index 37964f00d..802946e1b 100644 --- a/nova/tests/scheduler/test_vsa_scheduler.py +++ b/nova/tests/scheduler/test_vsa_scheduler.py @@ -22,6 +22,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging +from nova import rpc from nova import test from nova import utils from nova.volume import volume_types @@ -37,6 +38,10 @@ scheduled_volume = {} global_volume = {} +def fake_rpc_cast(*args, **kwargs): + pass + + class FakeVsaLeastUsedScheduler( vsa_sched.VsaSchedulerLeastUsedHost): # No need to stub anything at the moment @@ -170,12 +175,10 @@ class VsaSchedulerTestCase(test.TestCase): LOG.debug(_("Test: provision vol %(name)s on host %(host)s"), locals()) LOG.debug(_("\t vol=%(vol)s"), locals()) - pass def _fake_vsa_update(self, context, vsa_id, values): LOG.debug(_("Test: VSA update request: vsa_id=%(vsa_id)s "\ "values=%(values)s"), locals()) - pass def _fake_volume_create(self, context, options): LOG.debug(_("Test: Volume create: %s"), options) @@ -196,7 +199,6 @@ class VsaSchedulerTestCase(test.TestCase): "values=%(values)s"), locals()) global scheduled_volume scheduled_volume = {'id': volume_id, 'host': values['host']} - pass def _fake_service_get_by_args(self, context, host, binary): return "service" @@ -209,7 +211,6 @@ class VsaSchedulerTestCase(test.TestCase): def setUp(self, sched_class=None): super(VsaSchedulerTestCase, self).setUp() - self.stubs = stubout.StubOutForTesting() self.context = context.get_admin_context() if sched_class is None: @@ -220,6 +221,7 @@ class VsaSchedulerTestCase(test.TestCase): self.host_num = 10 self.drive_type_num = 5 + self.stubs.Set(rpc, 'cast', fake_rpc_cast) self.stubs.Set(self.sched, '_get_service_states', self._fake_get_service_states) self.stubs.Set(self.sched, @@ -234,8 +236,6 @@ class VsaSchedulerTestCase(test.TestCase): def tearDown(self): for name in self.created_types_lst: volume_types.purge(self.context, name) - - self.stubs.UnsetAll() super(VsaSchedulerTestCase, self).tearDown() def test_vsa_sched_create_volumes_simple(self): @@ -333,6 +333,8 @@ class VsaSchedulerTestCase(test.TestCase): self.stubs.Set(self.sched, '_get_service_states', self._fake_get_service_states) self.stubs.Set(nova.db, 'volume_create', self._fake_volume_create) + self.stubs.Set(nova.db, 'volume_update', self._fake_volume_update) + self.stubs.Set(rpc, 'cast', fake_rpc_cast) self.sched.schedule_create_volumes(self.context, request_spec, @@ -467,10 +469,9 @@ class VsaSchedulerTestCase(test.TestCase): self.stubs.Set(self.sched, 'service_is_up', self._fake_service_is_up_True) - host = self.sched.schedule_create_volume(self.context, - 123, availability_zone=None) + self.sched.schedule_create_volume(self.context, + 123, availability_zone=None) - self.assertEqual(host, 'host_3') self.assertEqual(scheduled_volume['id'], 123) self.assertEqual(scheduled_volume['host'], 'host_3') @@ -514,10 +515,9 @@ class VsaSchedulerTestCase(test.TestCase): global_volume['volume_type_id'] = volume_type['id'] global_volume['size'] = 0 - host = self.sched.schedule_create_volume(self.context, - 123, availability_zone=None) + self.sched.schedule_create_volume(self.context, + 123, availability_zone=None) - self.assertEqual(host, 'host_2') self.assertEqual(scheduled_volume['id'], 123) self.assertEqual(scheduled_volume['host'], 'host_2') @@ -529,7 +529,6 @@ class VsaSchedulerTestCaseMostAvail(VsaSchedulerTestCase): FakeVsaMostAvailCapacityScheduler()) def tearDown(self): - self.stubs.UnsetAll() super(VsaSchedulerTestCaseMostAvail, self).tearDown() def test_vsa_sched_create_single_volume(self): @@ -558,10 +557,9 @@ class VsaSchedulerTestCaseMostAvail(VsaSchedulerTestCase): global_volume['volume_type_id'] = volume_type['id'] global_volume['size'] = 0 - host = self.sched.schedule_create_volume(self.context, - 123, availability_zone=None) + self.sched.schedule_create_volume(self.context, + 123, availability_zone=None) - self.assertEqual(host, 'host_9') self.assertEqual(scheduled_volume['id'], 123) self.assertEqual(scheduled_volume['host'], 'host_9') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 86ffc18a6..4da551c93 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -28,6 +28,7 @@ from nova import db from nova import exception from nova import flags from nova import log as logging +from nova.scheduler import driver as scheduler_driver from nova import rpc from nova import test from nova import utils @@ -56,6 +57,38 @@ class FakeTime(object): self.counter += t +orig_rpc_call = rpc.call +orig_rpc_cast = rpc.cast + + +def rpc_call_wrapper(context, topic, msg, do_cast=True): + """Stub out the scheduler creating the instance entry""" + if topic == FLAGS.scheduler_topic and \ + msg['method'] == 'run_instance': + request_spec = msg['args']['request_spec'] + scheduler = scheduler_driver.Scheduler + num_instances = request_spec.get('num_instances', 1) + instances = [] + for x in xrange(num_instances): + instance = scheduler().create_instance_db_entry( + context, request_spec) + encoded = scheduler_driver.encode_instance(instance) + instances.append(encoded) + return instances + else: + if do_cast: + orig_rpc_cast(context, topic, msg) + else: + return orig_rpc_call(context, topic, msg) + + +def rpc_cast_wrapper(context, topic, msg): + """Stub out the scheduler creating the instance entry in + the reservation_id case. + """ + rpc_call_wrapper(context, topic, msg, do_cast=True) + + def nop_report_driver_status(self): pass @@ -80,6 +113,8 @@ class ComputeTestCase(test.TestCase): 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} self.stubs.Set(fake_image._FakeImageService, 'show', fake_show) + self.stubs.Set(rpc, 'call', rpc_call_wrapper) + self.stubs.Set(rpc, 'cast', rpc_cast_wrapper) def _create_instance(self, params=None): """Create a test instance""" @@ -142,7 +177,7 @@ class ComputeTestCase(test.TestCase): """Verify that an instance cannot be created without a display_name.""" cases = [dict(), dict(display_name=None)] for instance in cases: - ref = self.compute_api.create(self.context, + (ref, resv_id) = self.compute_api.create(self.context, instance_types.get_default_instance_type(), None, **instance) try: self.assertNotEqual(ref[0]['display_name'], None) @@ -152,7 +187,7 @@ class ComputeTestCase(test.TestCase): def test_create_instance_associates_security_groups(self): """Make sure create associates security groups""" group = self._create_group() - ref = self.compute_api.create( + (ref, resv_id) = self.compute_api.create( self.context, instance_type=instance_types.get_default_instance_type(), image_href=None, @@ -212,7 +247,7 @@ class ComputeTestCase(test.TestCase): ('<}\x1fh\x10e\x08l\x02l\x05o\x12!{>', 'hello'), ('hello_server', 'hello-server')] for display_name, hostname in cases: - ref = self.compute_api.create(self.context, + (ref, resv_id) = self.compute_api.create(self.context, instance_types.get_default_instance_type(), None, display_name=display_name) try: @@ -224,7 +259,7 @@ class ComputeTestCase(test.TestCase): """Make sure destroying disassociates security groups""" group = self._create_group() - ref = self.compute_api.create( + (ref, resv_id) = self.compute_api.create( self.context, instance_type=instance_types.get_default_instance_type(), image_href=None, @@ -240,7 +275,7 @@ class ComputeTestCase(test.TestCase): """Make sure destroying security groups disassociates instances""" group = self._create_group() - ref = self.compute_api.create( + (ref, resv_id) = self.compute_api.create( self.context, instance_type=instance_types.get_default_instance_type(), image_href=None, @@ -1398,6 +1433,84 @@ class ComputeTestCase(test.TestCase): 'swap'), swap_size) + def test_reservation_id_one_instance(self): + """Verify building an instance has a reservation_id that + matches return value from create""" + (refs, resv_id) = self.compute_api.create(self.context, + instance_types.get_default_instance_type(), None) + try: + self.assertEqual(len(refs), 1) + self.assertEqual(refs[0]['reservation_id'], resv_id) + finally: + db.instance_destroy(self.context, refs[0]['id']) + + def test_reservation_ids_two_instances(self): + """Verify building 2 instances at once results in a + reservation_id being returned equal to reservation id set + in both instances + """ + (refs, resv_id) = self.compute_api.create(self.context, + instance_types.get_default_instance_type(), None, + min_count=2, max_count=2) + try: + self.assertEqual(len(refs), 2) + self.assertNotEqual(resv_id, None) + finally: + for instance in refs: + self.assertEqual(instance['reservation_id'], resv_id) + db.instance_destroy(self.context, instance['id']) + + def test_reservation_ids_two_instances_no_wait(self): + """Verify building 2 instances at once without waiting for + instance IDs results in a reservation_id being returned equal + to reservation id set in both instances + """ + (refs, resv_id) = self.compute_api.create(self.context, + instance_types.get_default_instance_type(), None, + min_count=2, max_count=2, wait_for_instances=False) + try: + self.assertEqual(refs, None) + self.assertNotEqual(resv_id, None) + finally: + instances = self.compute_api.get_all(self.context, + search_opts={'reservation_id': resv_id}) + self.assertEqual(len(instances), 2) + for instance in instances: + self.assertEqual(instance['reservation_id'], resv_id) + db.instance_destroy(self.context, instance['id']) + + def test_create_with_specified_reservation_id(self): + """Verify building instances with a specified + reservation_id results in the correct reservation_id + being set + """ + + # We need admin context to be able to specify our own + # reservation_ids. + context = self.context.elevated() + # 1 instance + (refs, resv_id) = self.compute_api.create(context, + instance_types.get_default_instance_type(), None, + min_count=1, max_count=1, reservation_id='meow') + try: + self.assertEqual(len(refs), 1) + self.assertEqual(resv_id, 'meow') + finally: + self.assertEqual(refs[0]['reservation_id'], resv_id) + db.instance_destroy(self.context, refs[0]['id']) + + # 2 instances + (refs, resv_id) = self.compute_api.create(context, + instance_types.get_default_instance_type(), None, + min_count=2, max_count=2, reservation_id='woof') + try: + self.assertEqual(len(refs), 2) + self.assertEqual(resv_id, 'woof') + finally: + for instance in refs: + self.assertEqual(instance['reservation_id'], resv_id) + db.instance_destroy(self.context, instance['id']) + class ComputeTestMinRamMinDisk(test.TestCase): def setUp(self): @@ -1405,6 +1518,8 @@ class ComputeTestMinRamMinDisk(test.TestCase): self.compute = utils.import_object(FLAGS.compute_manager) self.compute_api = compute.API() self.context = context.RequestContext('fake', 'fake') + self.stubs.Set(rpc, 'call', rpc_call_wrapper) + self.stubs.Set(rpc, 'cast', rpc_cast_wrapper) self.fake_image = { 'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} @@ -1425,10 +1540,9 @@ class ComputeTestMinRamMinDisk(test.TestCase): # Now increase the inst_type memory and make sure all is fine. inst_type['memory_mb'] = 2 - ref = self.compute_api.create(self.context, inst_type, None) - self.assertTrue(ref) - - db.instance_destroy(self.context, ref[0]['id']) + (refs, resv_id) = self.compute_api.create(self.context, + inst_type, None) + db.instance_destroy(self.context, refs[0]['id']) def test_create_with_too_little_disk(self): """Test an instance type with too little disk space""" @@ -1447,10 +1561,9 @@ class ComputeTestMinRamMinDisk(test.TestCase): # Now increase the inst_type disk space and make sure all is fine. inst_type['local_gb'] = 2 - ref = self.compute_api.create(self.context, inst_type, None) - self.assertTrue(ref) - - db.instance_destroy(self.context, ref[0]['id']) + (refs, resv_id) = self.compute_api.create(self.context, + inst_type, None) + db.instance_destroy(self.context, refs[0]['id']) def test_create_just_enough_ram_and_disk(self): """Test an instance type with just enough ram and disk space""" @@ -1466,10 +1579,9 @@ class ComputeTestMinRamMinDisk(test.TestCase): return img self.stubs.Set(fake_image._FakeImageService, 'show', fake_show) - ref = self.compute_api.create(self.context, inst_type, None) - self.assertTrue(ref) - - db.instance_destroy(self.context, ref[0]['id']) + (refs, resv_id) = self.compute_api.create(self.context, + inst_type, None) + db.instance_destroy(self.context, refs[0]['id']) def test_create_with_no_ram_and_disk_reqs(self): """Test an instance type with no min_ram or min_disk""" @@ -1482,7 +1594,6 @@ class ComputeTestMinRamMinDisk(test.TestCase): return copy(self.fake_image) self.stubs.Set(fake_image._FakeImageService, 'show', fake_show) - ref = self.compute_api.create(self.context, inst_type, None) - self.assertTrue(ref) - - db.instance_destroy(self.context, ref[0]['id']) + (refs, resv_id) = self.compute_api.create(self.context, + inst_type, None) + db.instance_destroy(self.context, refs[0]['id']) diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index f4b481ebe..35d48dd62 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -21,9 +21,11 @@ from nova import context from nova import db from nova import flags from nova import quota +from nova import rpc from nova import test from nova import volume from nova.compute import instance_types +from nova.scheduler import driver as scheduler_driver FLAGS = flags.FLAGS @@ -51,6 +53,21 @@ class QuotaTestCase(test.TestCase): self.context = context.RequestContext(self.user_id, self.project_id, True) + orig_rpc_call = rpc.call + + def rpc_call_wrapper(context, topic, msg): + """Stub out the scheduler creating the instance entry""" + if topic == FLAGS.scheduler_topic and \ + msg['method'] == 'run_instance': + scheduler = scheduler_driver.Scheduler + instance = scheduler().create_instance_db_entry( + context, + msg['args']['request_spec']) + return [scheduler_driver.encode_instance(instance)] + else: + return orig_rpc_call(context, topic, msg) + + self.stubs.Set(rpc, 'call', rpc_call_wrapper) def _create_instance(self, cores=2): """Create a test instance""" -- cgit