diff options
| author | Dan Prince <dan.prince@rackspace.com> | 2011-09-07 09:28:45 -0400 |
|---|---|---|
| committer | Dan Prince <dan.prince@rackspace.com> | 2011-09-07 09:28:45 -0400 |
| commit | 1a22d1da448eb08fc4559619b450d716757b6e11 (patch) | |
| tree | fca9749f448d370d8101c29316ed6cb04b9c465b | |
| parent | 2c16115e236760f3933eadd3a5d7d20dda39866d (diff) | |
| parent | d01010583d5d581591c9edcf36c4da54f0c78da9 (diff) | |
| download | nova-1a22d1da448eb08fc4559619b450d716757b6e11.tar.gz nova-1a22d1da448eb08fc4559619b450d716757b6e11.tar.xz nova-1a22d1da448eb08fc4559619b450d716757b6e11.zip | |
Merge w/ trunk.
Fix test_rescue_with_preset_password.
| -rw-r--r-- | nova/api/ec2/cloud.py | 13 | ||||
| -rw-r--r-- | nova/api/openstack/contrib/createserverext.py | 26 | ||||
| -rw-r--r-- | nova/api/openstack/schemas/v1.1/server.rng | 2 | ||||
| -rw-r--r-- | nova/api/openstack/servers.py | 22 | ||||
| -rw-r--r-- | nova/api/openstack/views/servers.py | 2 | ||||
| -rw-r--r-- | nova/compute/api.py | 9 | ||||
| -rw-r--r-- | nova/network/manager.py | 2 | ||||
| -rw-r--r-- | nova/scheduler/host_filter.py | 6 | ||||
| -rw-r--r-- | nova/scheduler/manager.py | 10 | ||||
| -rw-r--r-- | nova/tests/api/openstack/contrib/test_createserverext.py | 99 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_servers.py | 63 | ||||
| -rw-r--r-- | nova/tests/test_compute.py | 13 | ||||
| -rw-r--r-- | nova/utils.py | 9 |
13 files changed, 229 insertions, 47 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index fe44191c8..049ca6f93 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1020,14 +1020,6 @@ class CloudController(object): 'status': volume['attach_status'], 'volumeId': ec2utils.id_to_ec2_vol_id(volume_id)} - @staticmethod - def _convert_to_set(lst, label): - if lst is None or lst == []: - return None - if not isinstance(lst, list): - lst = [lst] - return [{label: x} for x in lst] - def _format_kernel_id(self, instance_ref, result, key): kernel_id = instance_ref['kernel_id'] if kernel_id is None: @@ -1186,7 +1178,7 @@ class CloudController(object): if instance.get('security_groups'): for security_group in instance['security_groups']: security_group_names.append(security_group['name']) - result['groupSet'] = CloudController._convert_to_set( + result['groupSet'] = utils.convert_to_list_dict( security_group_names, 'groupId') def _format_instances(self, context, instance_id=None, use_v6=False, @@ -1250,7 +1242,8 @@ class CloudController(object): i['keyName'] = '%s (%s, %s)' % (i['keyName'], instance['project_id'], instance['host']) - i['productCodesSet'] = self._convert_to_set([], 'product_codes') + i['productCodesSet'] = utils.convert_to_list_dict([], + 'product_codes') self._format_instance_type(instance, i) i['launchTime'] = instance['created_at'] i['amiLaunchIndex'] = instance['launch_index'] diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py index ba72fdb0b..af7f37f13 100644 --- a/nova/api/openstack/contrib/createserverext.py +++ b/nova/api/openstack/contrib/createserverext.py @@ -14,18 +14,34 @@ # License for the specific language governing permissions and limitations # under the License +from nova import utils from nova.api.openstack import create_instance_helper as helper from nova.api.openstack import extensions from nova.api.openstack import servers from nova.api.openstack import wsgi -class Createserverext(extensions.ExtensionDescriptor): - """The servers create ext +class CreateServerController(servers.ControllerV11): + def _build_view(self, req, instance, is_detail=False): + server = super(CreateServerController, self)._build_view(req, + instance, + is_detail) + if is_detail: + self._build_security_groups(server['server'], instance) + return server + + def _build_security_groups(self, response, inst): + sg_names = [] + sec_groups = inst.get('security_groups') + if sec_groups: + sg_names = [sec_group['name'] for sec_group in sec_groups] - Exposes addFixedIp and removeFixedIp actions on servers. + response['security_groups'] = utils.convert_to_list_dict(sg_names, + 'name') - """ + +class Createserverext(extensions.ExtensionDescriptor): + """The servers create ext""" def get_name(self): return "Createserverext" @@ -58,7 +74,7 @@ class Createserverext(extensions.ExtensionDescriptor): deserializer = wsgi.RequestDeserializer(body_deserializers) res = extensions.ResourceExtension('os-create-server-ext', - controller=servers.ControllerV11(), + controller=CreateServerController(), deserializer=deserializer, serializer=serializer) resources.append(res) diff --git a/nova/api/openstack/schemas/v1.1/server.rng b/nova/api/openstack/schemas/v1.1/server.rng index dbd169a83..ef835e408 100644 --- a/nova/api/openstack/schemas/v1.1/server.rng +++ b/nova/api/openstack/schemas/v1.1/server.rng @@ -1,6 +1,8 @@ <element name="server" ns="http://docs.openstack.org/compute/api/v1.1" xmlns="http://relaxng.org/ns/structure/1.0"> <attribute name="name"> <text/> </attribute> + <attribute name="userId"> <text/> </attribute> + <attribute name="tenantId"> <text/> </attribute> <attribute name="id"> <text/> </attribute> <attribute name="uuid"> <text/> </attribute> <attribute name="updated"> <text/> </attribute> diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 3506a4bca..49f267eb9 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -881,6 +881,8 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): def _add_server_attributes(self, node, server): node.setAttribute('id', str(server['id'])) + node.setAttribute('userId', str(server['user_id'])) + node.setAttribute('tenantId', str(server['tenant_id'])) node.setAttribute('uuid', str(server['uuid'])) node.setAttribute('hostId', str(server['hostId'])) node.setAttribute('name', server['name']) @@ -936,6 +938,11 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): server['addresses']) server_node.appendChild(addresses_node) + if 'security_groups' in server: + security_groups_node = self._create_security_groups_node(xml_doc, + server['security_groups']) + server_node.appendChild(security_groups_node) + return server_node def _server_list_to_xml(self, xml_doc, servers, detailed): @@ -988,6 +995,19 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): server_dict['server']) return self.to_xml_string(node, True) + def _security_group_to_xml(self, doc, security_group): + node = doc.createElement('security_group') + node.setAttribute('name', str(security_group.get('name'))) + return node + + def _create_security_groups_node(self, xml_doc, security_groups): + security_groups_node = xml_doc.createElement('security_groups') + if security_groups: + for security_group in security_groups: + node = self._security_group_to_xml(xml_doc, security_group) + security_groups_node.appendChild(node) + return security_groups_node + def create_resource(version='1.0'): controller = { @@ -999,7 +1019,7 @@ def create_resource(version='1.0'): "attributes": { "server": ["id", "imageId", "name", "flavorId", "hostId", "status", "progress", "adminPass", "flavorRef", - "imageRef"], + "imageRef", "userId", "tenantId"], "link": ["rel", "type", "href"], }, "dict_collections": { diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index 3a13d15f1..ac09b5864 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -66,6 +66,8 @@ class ViewBuilder(object): inst_dict = { 'id': inst['id'], 'name': inst['display_name'], + 'user_id': inst.get('user_id', ''), + 'tenant_id': inst.get('project_id', ''), 'status': common.status_from_state(vm_state, task_state)} # Return the metadata as a dictionary diff --git a/nova/compute/api.py b/nova/compute/api.py index ca59b5701..53040f06e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -383,10 +383,6 @@ class API(base.Base): If you are changing this method, be sure to update both call paths. """ - instance = dict(launch_index=num, **base_options) - instance = self.db.instance_create(context, instance) - instance_id = instance['id'] - elevated = context.elevated() if security_group is None: security_group = ['default'] @@ -400,6 +396,10 @@ class API(base.Base): security_group_name) security_groups.append(group['id']) + instance = dict(launch_index=num, **base_options) + instance = self.db.instance_create(context, instance) + instance_id = instance['id'] + for security_group_id in security_groups: self.db.instance_add_security_group(elevated, instance_id, @@ -877,6 +877,7 @@ class API(base.Base): 'image': 'image_ref', 'name': 'display_name', 'instance_name': 'name', + 'tenant_id': 'project_id', 'recurse_zones': None, 'flavor': _remap_flavor_filter, 'fixed_ip': _remap_fixed_ip_filter} diff --git a/nova/network/manager.py b/nova/network/manager.py index e6b30d1a0..050cec250 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -448,7 +448,7 @@ class NetworkManager(manager.SchedulerDependentManager): try: fixed_ips = kwargs.get('fixed_ips') or \ self.db.fixed_ip_get_by_instance(context, instance_id) - except exceptions.FixedIpNotFoundForInstance: + except exception.FixedIpNotFoundForInstance: fixed_ips = [] LOG.debug(_("network deallocation for instance |%s|"), instance_id, context=context) diff --git a/nova/scheduler/host_filter.py b/nova/scheduler/host_filter.py index 826a99b0a..9f7d34ea7 100644 --- a/nova/scheduler/host_filter.py +++ b/nova/scheduler/host_filter.py @@ -32,6 +32,12 @@ from nova import exception from nova import flags import nova.scheduler +# NOTE(Vek): Even though we don't use filters in here anywhere, we +# depend on default_host_filter being available in FLAGS, +# and that happens only when filters/abstract_filter.py is +# imported. +from nova.scheduler import filters + FLAGS = flags.FLAGS diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 0e395ee79..bf18abc6c 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -93,12 +93,14 @@ class SchedulerManager(manager.Manager): driver_method = 'schedule_%s' % method elevated = context.elevated() try: - host = getattr(self.driver, driver_method)(elevated, *args, - **kwargs) + real_meth = getattr(self.driver, driver_method) + args = (elevated,) + args except AttributeError, e: LOG.warning(_("Driver Method %(driver_method)s missing: %(e)s." - "Reverting to schedule()") % locals()) - host = self.driver.schedule(elevated, topic, *args, **kwargs) + "Reverting to schedule()") % locals()) + real_meth = self.driver.schedule + args = (elevated, topic) + args + host = real_meth(*args, **kwargs) if not host: LOG.debug(_("%(topic)s %(method)s handled in Scheduler") diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index 089c8e59d..078b72d67 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -16,6 +16,7 @@ # under the License. import base64 +import datetime import json import unittest from xml.dom import minidom @@ -27,15 +28,7 @@ from nova import db from nova import exception from nova import flags from nova import test -from nova import utils import nova.api.openstack -from nova.api.openstack import servers -from nova.api.openstack.contrib import createserverext -import nova.compute.api - -import nova.scheduler.api -import nova.image.fake -import nova.rpc from nova.tests.api.openstack import fakes @@ -52,22 +45,45 @@ DUPLICATE_NETWORKS = [('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '10.0.1.12'), INVALID_NETWORKS = [('invalid', 'invalid-ip-address')] +INSTANCE = { + "id": 1, + "display_name": "test_server", + "uuid": FAKE_UUID, + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), + "security_groups": [{"id": 1, "name": "test"}] + } + + +def return_server_by_id(context, id, session=None): + INSTANCE['id'] = id + return INSTANCE + + +def return_security_group_non_existing(context, project_id, group_name): + raise exception.SecurityGroupNotFoundForProject(project_id=project_id, + security_group_id=group_name) + + +def return_security_group_get_by_name(context, project_id, group_name): + return {'id': 1, 'name': group_name} + + +def return_security_group_get(context, security_group_id, session): + return {'id': security_group_id} + + +def return_instance_add_security_group(context, instance_id, + security_group_id): + pass + class CreateserverextTest(test.TestCase): def setUp(self): super(CreateserverextTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.auth_data = {} - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_auth(self.stubs) - fakes.stub_out_image_service(self.stubs) - fakes.stub_out_key_pair_funcs(self.stubs) - self.allow_admin = FLAGS.allow_admin_api def tearDown(self): - self.stubs.UnsetAll() - FLAGS.allow_admin_api = self.allow_admin super(CreateserverextTest, self).tearDown() def _setup_mock_compute_api(self): @@ -96,6 +112,8 @@ class CreateserverextTest(test.TestCase): return [{'id': '1234', 'display_name': 'fakeinstance', 'uuid': FAKE_UUID, + 'user_id': 'fake', + 'project_id': 'fake', 'created_at': "", 'updated_at': ""}] @@ -114,6 +132,18 @@ class CreateserverextTest(test.TestCase): '_get_kernel_ramdisk_from_image', make_stub_method((1, 1))) return compute_api + def _create_security_group_request_dict(self, security_groups): + server = {} + server['name'] = 'new-server-test' + server['imageRef'] = 1 + server['flavorRef'] = 1 + if security_groups is not None: + sg_list = [] + for name in security_groups: + sg_list.append({'name': name}) + server['security_groups'] = sg_list + return {'server': server} + def _create_networks_request_dict(self, networks): server = {} server['name'] = 'new-server-test' @@ -348,3 +378,38 @@ class CreateserverextTest(test.TestCase): self._create_instance_with_user_data_json(user_data_contents) self.assertEquals(response.status_int, 400) self.assertEquals(user_data, None) + + def test_create_instance_with_security_group_json(self): + security_groups = ['test', 'test1'] + self.stubs.Set(nova.db.api, 'security_group_get_by_name', + return_security_group_get_by_name) + self.stubs.Set(nova.db.api, 'instance_add_security_group', + return_instance_add_security_group) + 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()) + self.assertEquals(response.status_int, 202) + + def test_get_server_by_id_verify_security_groups_json(self): + self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id) + req = webob.Request.blank('/v1.1/123/os-create-server-ext/1') + req.headers['Content-Type'] = 'application/json' + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 200) + res_dict = json.loads(response.body) + expected_security_group = [{"name": "test"}] + self.assertEquals(res_dict['server']['security_groups'], + expected_security_group) + + def test_get_server_by_id_verify_security_groups_xml(self): + self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id) + req = webob.Request.blank('/v1.1/123/os-create-server-ext/1') + req.headers['Accept'] = 'application/xml' + response = req.get_response(fakes.wsgi_app()) + self.assertEquals(response.status_int, 200) + dom = minidom.parseString(response.body) + server = dom.childNodes[0] + sec_groups = server.getElementsByTagName('security_groups')[0] + sec_group = sec_groups.getElementsByTagName('security_group')[0] + self.assertEqual(INSTANCE['security_groups'][0]['name'], + sec_group.getAttribute("name")) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index b5116b496..be1cabea8 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -349,6 +349,8 @@ class ServersTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, @@ -448,6 +450,8 @@ class ServersTest(test.TestCase): expected = minidom.parseString(""" <server id="1" uuid="%(expected_uuid)s" + userId="fake" + tenantId="fake" xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" name="server1" @@ -517,6 +521,8 @@ class ServersTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, @@ -612,6 +618,8 @@ class ServersTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, @@ -1201,6 +1209,26 @@ class ServersTest(test.TestCase): self.assertEqual(len(servers), 1) self.assertEqual(servers[0]['id'], 100) + def test_tenant_id_filter_converts_to_project_id_for_admin(self): + def fake_get_all(context, filters=None): + self.assertNotEqual(filters, None) + self.assertEqual(filters['project_id'], 'faketenant') + self.assertFalse(filters.get('tenant_id')) + return [stub_instance(100)] + + self.stubs.Set(nova.db.api, 'instance_get_all_by_filters', + fake_get_all) + self.flags(allow_admin_api=True) + + req = webob.Request.blank('/v1.1/fake/servers?tenant_id=faketenant') + # Use admin context + context = nova.context.RequestContext('testuser', 'testproject', + is_admin=True) + res = req.get_response(fakes.wsgi_app(fake_auth_context=context)) + res_dict = json.loads(res.body) + # Failure in fake_get_all returns non 200 status code + self.assertEqual(res.status_int, 200) + def test_get_servers_allows_flavor_v1_1(self): def fake_get_all(compute_self, context, search_opts=None): self.assertNotEqual(search_opts, None) @@ -1457,6 +1485,8 @@ class ServersTest(test.TestCase): 'access_ip_v4': '1.2.3.4', 'access_ip_v6': 'fead::1234', 'image_ref': image_ref, + 'user_id': 'fake', + 'project_id': 'fake', "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, @@ -2513,9 +2543,9 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.api.API, 'rescue', rescue_mock) req = webob.Request.blank('/v1.0/servers/1/rescue') + req.method = 'POST' body = {"rescue": {"adminPass": "AABBCC112233"}} req.body = json.dumps(body) - req.method = 'POST' req.content_type = 'application/json' res = req.get_response(fakes.wsgi_app()) @@ -3130,7 +3160,7 @@ class TestServerCreateRequestXMLDeserializerV11(test.TestCase): "name": "new-server-test", "imageRef": "1", "flavorRef": "1", - "networks": [] + "networks": [], }} self.assertEquals(request['body'], expected) @@ -3357,6 +3387,8 @@ class TestServerInstanceCreation(test.TestCase): self.injected_files = None return [{'id': '1234', 'display_name': 'fakeinstance', + 'user_id': 'fake', + 'project_id': 'fake', 'uuid': FAKE_UUID}] def set_admin_password(self, *args, **kwargs): @@ -3650,8 +3682,8 @@ class ServersViewBuilderV11Test(test.TestCase): "created_at": created_at, "updated_at": updated_at, "admin_pass": "", - "user_id": "", - "project_id": "", + "user_id": "fake", + "project_id": "fake", "image_ref": "5", "kernel_id": "", "ramdisk_id": "", @@ -3676,7 +3708,6 @@ class ServersViewBuilderV11Test(test.TestCase): "terminated_at": utils.utcnow(), "availability_zone": "", "display_name": "test_server", - "display_description": "", "locked": False, "metadata": [], "accessIPv4": "1.2.3.4", @@ -3759,6 +3790,8 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, @@ -3814,6 +3847,8 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, @@ -3870,6 +3905,8 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, @@ -3926,6 +3963,8 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, @@ -3985,6 +4024,8 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, @@ -4053,6 +4094,8 @@ class ServerXMLSerializationTest(test.TestCase): fixture = { "server": { "id": 1, + "user_id": "fake", + "tenant_id": "fake", "uuid": FAKE_UUID, 'created': self.TIMESTAMP, 'updated': self.TIMESTAMP, @@ -4190,6 +4233,8 @@ class ServerXMLSerializationTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", 'created': self.TIMESTAMP, 'updated': self.TIMESTAMP, "progress": 0, @@ -4390,6 +4435,8 @@ class ServerXMLSerializationTest(test.TestCase): { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", 'created': self.TIMESTAMP, 'updated': self.TIMESTAMP, "progress": 0, @@ -4445,6 +4492,8 @@ class ServerXMLSerializationTest(test.TestCase): { "id": 2, "uuid": FAKE_UUID, + "user_id": 'fake', + "tenant_id": 'fake', 'created': self.TIMESTAMP, 'updated': self.TIMESTAMP, "progress": 100, @@ -4564,6 +4613,8 @@ class ServerXMLSerializationTest(test.TestCase): fixture = { "server": { "id": 1, + "user_id": "fake", + "tenant_id": "fake", "uuid": FAKE_UUID, 'created': self.TIMESTAMP, 'updated': self.TIMESTAMP, @@ -4700,6 +4751,8 @@ class ServerXMLSerializationTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", + "tenant_id": "fake", 'created': self.TIMESTAMP, 'updated': self.TIMESTAMP, "progress": 0, diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 766a7da9b..65fdffbd6 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -161,6 +161,19 @@ class ComputeTestCase(test.TestCase): db.security_group_destroy(self.context, group['id']) db.instance_destroy(self.context, ref[0]['id']) + def test_create_instance_with_invalid_security_group_raises(self): + instance_type = instance_types.get_default_instance_type() + + pre_build_len = len(db.instance_get_all(context.get_admin_context())) + self.assertRaises(exception.SecurityGroupNotFoundForProject, + self.compute_api.create, + self.context, + instance_type=instance_type, + image_href=None, + security_group=['this_is_a_fake_sec_group']) + self.assertEqual(pre_build_len, + len(db.instance_get_all(context.get_admin_context()))) + def test_create_instance_associates_config_drive(self): """Make sure create associates a config drive.""" diff --git a/nova/utils.py b/nova/utils.py index 21e6221b2..81157a4cd 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -901,3 +901,12 @@ def monkey_patch(): func = import_class("%s.%s" % (module, key)) setattr(sys.modules[module], key,\ decorator("%s.%s" % (module, key), func)) + + +def convert_to_list_dict(lst, label): + """Convert a value or list into a list of dicts""" + if not lst: + return None + if not isinstance(lst, list): + lst = [lst] + return [{label: x} for x in lst] |
