From 54be28647ac3ad401006bca3069b1dfc1a65d093 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 12 Jul 2011 14:35:09 -0400 Subject: server create deserialization functional and tested --- nova/api/openstack/create_instance_helper.py | 81 ++++-- nova/api/openstack/servers.py | 63 +++- nova/api/openstack/wsgi.py | 21 ++ nova/tests/api/openstack/test_servers.py | 414 ++++++++++++++++++++++++--- 4 files changed, 508 insertions(+), 71 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 2654e3c40..eea973a56 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -180,7 +180,7 @@ class CreateInstanceHelper(object): Overrides normal behavior in the case of xml content """ if request.content_type == "application/xml": - deserializer = ServerCreateRequestXMLDeserializer() + deserializer = ServerXMLDeserializer() return deserializer.deserialize(request.body) else: return self._deserialize(request.body, request.get_content_type()) @@ -295,9 +295,15 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): """Marshal the server attribute of a parsed request""" server = {} server_node = self._find_first_child_named(node, 'server') - for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]: + for attr in ["name", "imageId", "flavorId"]: if server_node.getAttribute(attr): server[attr] = server_node.getAttribute(attr) + image = self._extract_image(server_node) + if image is not None: + server["image"] = image + flavor = self._extract_flavor(server_node) + if flavor is not None: + server["flavor"] = flavor metadata = self._extract_metadata(server_node) if metadata is not None: server["metadata"] = metadata @@ -306,6 +312,56 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): server["personality"] = personality return server + def _extract_image(self, server_node): + """Retrieve an image entity from the server node""" + image_node = self._find_first_child_named(server_node, "image") + if image_node is None: + return None + + image = {} + image_id = image_node.getAttribute('id') + if image_id: + image['id'] = image_id + + image_links = self._extract_links_from_node(image_node) + if len(image_links) > 0: + image['links'] = image_links + + return image + + def _extract_flavor(self, server_node): + """Retrieve a flavor entity from the server node""" + flavor_node = self._find_first_child_named(server_node, "flavor") + if flavor_node is None: + return None + + flavor = {} + flavor_id = flavor_node.getAttribute('id') + if flavor_id: + flavor['id'] = flavor_id + + flavor_links = self._extract_links_from_node(flavor_node) + if len(flavor_links) > 0: + flavor['links'] = flavor_links + + return flavor + + def _extract_links_from_node(self, parent_node): + """Retrieve link entities from a links container provided node""" + links = [] + + for link_node in self._find_children_named(parent_node, 'atom:link'): + link = {} + link_rel = link_node.getAttribute('rel') + if link_rel is not None: + link['rel'] = link_rel + link_href = link_node.getAttribute('href') + if link_href is not None: + link['href'] = link_href + links.append(link) + + return links + def _extract_metadata(self, server_node): """Marshal the metadata attribute of a parsed request""" metadata_node = self._find_first_child_named(server_node, "metadata") @@ -331,24 +387,3 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): item["contents"] = self._extract_text(file_node) personality.append(item) return personality - - def _find_first_child_named(self, parent, name): - """Search a nodes children for the first child with a given name""" - for node in parent.childNodes: - if node.nodeName == name: - return node - return None - - def _find_children_named(self, parent, name): - """Return all of a nodes children who have the given name""" - for node in parent.childNodes: - if node.nodeName == name: - yield node - - def _extract_text(self, node): - """Get the text field contained by the given node""" - if len(node.childNodes) == 1: - child = node.childNodes[0] - if child.nodeType == child.TEXT_NODE: - return child.nodeValue - return "" diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 12af44a8d..f239044ff 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -492,11 +492,68 @@ class ControllerV11(Controller): return faults.Fault(exc.HTTPNotFound()) def _image_ref_from_req_data(self, data): - return data['server']['imageRef'] + try: + image = data['server']['image'] + except (AttributeError, KeyError): + msg = _("Missing image entity") + raise exc.HTTPBadRequest(explanation=msg) + + try: + links = image.get('links', []) + except AttributeError: + msg = _("Malformed image entity") + raise exc.HTTPBadRequest(explanation=msg) + + image_ref = None + for link in links: + try: + if link.get('rel') == 'bookmark': + image_ref = link.get('href') + break + except AttributeError: + msg = _("Malformed image link") + raise exc.HTTPBadRequest(explanation=msg) + + if image_ref is None: + try: + image_ref = image['id'] + except KeyError: + msg = _("Missing id attribute on image entity") + raise exc.HTTPBadRequest(explanation=msg) + + return image_ref def _flavor_id_from_req_data(self, data): - href = data['server']['flavorRef'] - return common.get_id_from_href(href) + try: + flavor = data['server']['flavor'] + except (AttributeError, KeyError): + msg = _("Missing flavor entity") + raise exc.HTTPBadRequest(explanation=msg) + + try: + links = flavor.get('links', []) + except AttributeError: + msg = _("Malformed flavor entity") + raise exc.HTTPBadRequest(explanation=msg) + + flavor_ref = None + for link in links: + try: + if link.get('rel') == 'bookmark': + flavor_ref = link.get('href') + break + except AttributeError: + msg = _("Malformed flavor link") + raise exc.HTTPBadRequest(explanation=msg) + + if flavor_ref is None: + try: + return flavor['id'] + except (KeyError, AttributeError): + msg = _("Missing id attribute in flavor entity") + raise exc.HTTPBadRequest(explanation=msg) + else: + return common.get_id_from_href(flavor_ref) def _get_view_builder(self, req): base_url = req.application_url diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 8eff9e441..0ece2cff0 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -134,6 +134,27 @@ class XMLDeserializer(TextDeserializer): listnames) return result + def _find_first_child_named(self, parent, name): + """Search a nodes children for the first child with a given name""" + for node in parent.childNodes: + if node.nodeName == name: + return node + return None + + def _find_children_named(self, parent, name): + """Return all of a nodes children who have the given name""" + for node in parent.childNodes: + if node.nodeName == name: + yield node + + def _extract_text(self, node): + """Get the text field contained by the given node""" + if len(node.childNodes) == 1: + child = node.childNodes[0] + if child.nodeType == child.TEXT_NODE: + return child.nodeValue + return "" + def default(self, datastring): return {'body': self._from_xml(datastring)} diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 775f66ad0..cb7e03934 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -612,7 +612,7 @@ class ServersTest(test.TestCase): "_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( @@ -626,6 +626,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(16, len(server['adminPass'])) self.assertEqual('server_test', server['name']) @@ -633,10 +634,6 @@ class ServersTest(test.TestCase): self.assertEqual(2, server['flavorId']) self.assertEqual(3, server['imageId']) self.assertEqual(FAKE_UUID, server['uuid']) - self.assertEqual(res.status_int, 200) - - def test_create_instance(self): - self._test_create_instance_helper() def test_create_instance_has_uuid(self): """Tests at the db-layer instead of API layer since that's where the @@ -692,7 +689,27 @@ class ServersTest(test.TestCase): 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._setup_for_create_instance() + + body = dict(server=dict( + name='server_test', imageId=3, flavorId=2, + metadata={'hello': 'world', 'open': 'stack'}, + personality={})) + req = webob.Request.blank('/v1.0/servers') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + + server = json.loads(res.body)['server'] + self.assertEqual(16, len(server['adminPass'])) + self.assertEqual('server_test', server['name']) + self.assertEqual(1, server['id']) + self.assertEqual(2, server['flavorId']) + self.assertEqual(3, server['imageId']) + self.assertEqual(FAKE_UUID, server['uuid']) + self.assertEqual(res.status_int, 200) def test_create_instance_no_name(self): self._setup_for_create_instance() @@ -765,18 +782,34 @@ class ServersTest(test.TestCase): def test_create_instance_v1_1(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' + image_href = 'http://localhost/v1.1/images/3' + flavor_href = 'http://localhost/v1.1/flavors/2' + body = { 'server': { 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, + 'image': { + 'id': 3, + 'links': [ + {'rel': 'bookmark', 'href': image_href}, + ], + }, + 'flavor': { + 'id': 2, + 'links': [ + {'rel': 'bookmark', 'href': flavor_href}, + ], + }, 'metadata': { 'hello': 'world', 'open': 'stack', }, - 'personality': {}, + 'personality': [ + { + "path" : "/etc/banner.txt", + "contents" : "MQ==", + }, + ], }, } @@ -787,40 +820,102 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(16, len(server['adminPass'])) self.assertEqual('server_test', server['name']) self.assertEqual(1, server['id']) - self.assertEqual(flavor_ref, server['flavorRef']) + self.assertEqual(flavor_href, server['flavorRef']) self.assertEqual(image_href, server['imageRef']) - self.assertEqual(res.status_int, 200) + self.assertFalse('personality' in server) - def test_create_instance_v1_1_bad_href(self): + def test_create_instance_v1_1_image_id(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/asdf' + image_id = 2 flavor_ref = 'http://localhost/v1.1/flavors/3' - body = dict(server=dict( - name='server_test', imageRef=image_href, flavorRef=flavor_ref, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) + body = { + 'server': { + 'name': 'server_test', + 'image': {'id': image_id}, + 'flavor': {'id': 3}, + }, + } + + req = webob.Request.blank('/v1.1/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, 200) + server = json.loads(res.body)['server'] + self.assertEqual(flavor_ref, server['flavorRef']) + + def test_create_instance_v1_1_image_link(self): + self._setup_for_create_instance() + + image_ref = 'http://localhost/v1.1/image/3' + body = { + 'server': { + 'name': 'server_test', + 'image': { + 'links':[ + {'rel': 'self', 'href': 'http://google.com'}, + {'rel': 'bookmark', 'href': image_ref}, + ], + }, + 'flavor': {'id': 3}, + }, + } + req = webob.Request.blank('/v1.1/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, 200) + server = json.loads(res.body)['server'] + self.assertEqual(image_ref, server['imageRef']) + + def test_create_instance_v1_1_no_valid_image(self): + self._setup_for_create_instance() + + body = { + 'server': { + 'name': 'server_test', + 'image': {}, + 'flavor': {'id': 3}, + }, + } + + req = webob.Request.blank('/v1.1/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, 400) - def test_create_instance_v1_1_local_href(self): + def test_create_instance_v1_1_flavor_link(self): self._setup_for_create_instance() - image_id = 2 flavor_ref = 'http://localhost/v1.1/flavors/3' + body = { 'server': { 'name': 'server_test', - 'imageRef': image_id, - 'flavorRef': flavor_ref, + 'image': {'id': 3}, + 'flavor': { + 'id': 2, + 'links': [ + {'rel': 'bookmark', 'href': flavor_ref}, + ], + }, }, } @@ -831,11 +926,55 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] - self.assertEqual(1, server['id']) self.assertEqual(flavor_ref, server['flavorRef']) - self.assertEqual(image_id, server['imageRef']) + + def test_create_instance_v1_1_flavor_id(self): + self._setup_for_create_instance() + + flavor_ref = 'http://localhost/v1.1/flavors/2' + + body = { + 'server': { + 'name': 'server_test', + 'image': {'id': 3}, + 'flavor': {'id': 2}, + }, + } + + req = webob.Request.blank('/v1.1/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, 200) + server = json.loads(res.body)['server'] + self.assertEqual(flavor_ref, server['flavorRef']) + + def test_create_instance_v1_1_no_valid_flavor(self): + self._setup_for_create_instance() + + body = { + 'server': { + 'name': 'server_test', + 'image': {'id': 3}, + 'flavor': {}, + }, + } + + req = webob.Request.blank('/v1.1/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, 400) + + def test_create_instance_with_admin_pass_v1_0(self): self._setup_for_create_instance() @@ -858,7 +997,7 @@ class ServersTest(test.TestCase): self.assertNotEqual(res['server']['adminPass'], body['server']['adminPass']) - def test_create_instance_with_admin_pass_v1_1(self): + def test_create_instance_v1_1_admin_pass(self): self._setup_for_create_instance() image_href = 'http://localhost/v1.1/images/2' @@ -866,8 +1005,8 @@ class ServersTest(test.TestCase): body = { 'server': { 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, + 'image': {'id': 3}, + 'flavor': {'id': 3}, 'adminPass': 'testpass', }, } @@ -876,20 +1015,22 @@ class ServersTest(test.TestCase): 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, 200) + server = json.loads(res.body)['server'] self.assertEqual(server['adminPass'], body['server']['adminPass']) - def test_create_instance_with_empty_admin_pass_v1_1(self): + def test_create_instance_v1_1_admin_pass_empty(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' body = { 'server': { 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, + 'image': {'id': 3}, + 'flavor': {'id': 3}, 'adminPass': '', }, } @@ -1644,7 +1785,7 @@ class ServersTest(test.TestCase): self.assertEqual(res_dict['server']['status'], 'SHUTOFF') -class TestServerCreateRequestXMLDeserializer(unittest.TestCase): +class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase): def setUp(self): self.deserializer = create_instance_helper.ServerXMLDeserializer() @@ -1652,7 +1793,7 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase): def test_minimal_request(self): serial_request = """ """ + name="new-server-test" imageId="1" flavorId="1" />""" request = self.deserializer.deserialize(serial_request, 'create') expected = {"server": { "name": "new-server-test", @@ -1924,19 +2065,202 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""", request = self.deserializer.deserialize(serial_request, 'create') self.assertEqual(request['body'], expected) - def test_request_xmlser_with_flavor_image_href(self): + +class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): + + def setUp(self): + self.deserializer = create_instance_helper.ServerXMLDeserializer() + + def test_minimal_request(self): serial_request = """ - - """ + + + +""" request = self.deserializer.deserialize(serial_request, 'create') - self.assertEquals(request['body']["server"]["flavorRef"], - "http://localhost:8774/v1.1/flavors/1") - self.assertEquals(request['body']["server"]["imageRef"], - "http://localhost:8774/v1.1/images/1") + expected = { + "server": { + "name": "new-server-test", + "image": {"id": "1"}, + "flavor": {"id": "2"}, + }, + } + self.assertEquals(request['body'], expected) + def test_image_link(self): + serial_request = """ + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "image": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": "http://localhost:8774/v1.1/images/2", + }, + ], + }, + "flavor": {"id": "3"}, + }, + } + self.assertEquals(request['body'], expected) + + def test_flavor_link(self): + serial_request = """ + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "image": {"id": "1"}, + "flavor": { + "id": "2", + "links": [ + { + "rel": "bookmark", + "href": "http://localhost:8774/v1.1/flavors/3", + }, + ], + }, + }, + } + self.assertEquals(request['body'], expected) + + def test_empty_metadata_personality(self): + serial_request = """ + + + + + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "image": {"id": "1"}, + "flavor": {"id": "2"}, + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_multiple_metadata_items(self): + serial_request = """ + + + + + two + snack + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "image": {"id": "1"}, + "flavor": {"id": "2"}, + "metadata": {"one": "two", "open": "snack"}, + }, + } + self.assertEquals(request['body'], expected) + + def test_multiple_personality_files(self): + serial_request = """ + + + + + MQ== + Mg== + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "image": {"id": "1"}, + "flavor": {"id": "2"}, + "personality": [ + {"path": "/etc/banner.txt", "contents": "MQ=="}, + {"path": "/etc/hosts", "contents": "Mg=="}, + ], + }, + } + self.assertEquals(request['body'], expected) + + def test_spec_request(self): + serial_request = """ + + + + + + + + Apache1 + + + Mg== + +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "image": { + "id": "52415800-8b69-11e0-9b19-734f6f006e54", + "links": [ + { + "rel": "self", + "href": "http://servers.api.openstack.org/" + \ + "v1.1/1234/images/52415800-8b69-11" + \ + "e0-9b19-734f6f006e54", + }, + { + "rel": "bookmark", + "href": "http://servers.api.openstack.org/" + \ + "1234/images/52415800-8b69-11e0-9b" + \ + "19-734f6f006e54", + }, + ], + }, + "flavor": {"id": "52415800-8b69-11e0-9b19-734f1195ff37"}, + "metadata": {"My Server Name": "Apache1"}, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "Mg==", + }, + ], + }, + } + self.assertEquals(request['body'], expected) class TestServerInstanceCreation(test.TestCase): -- cgit From 07baabb67d9491da61fa5bfe9adc52f7ff744e22 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 12 Jul 2011 16:02:39 -0400 Subject: cleanup --- nova/api/openstack/create_instance_helper.py | 24 ++++++---------- nova/api/openstack/servers.py | 41 ++++++++++++---------------- nova/tests/api/openstack/test_servers.py | 37 +++++++------------------ 3 files changed, 37 insertions(+), 65 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index eea973a56..e46bc9d98 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -320,12 +320,10 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): image = {} image_id = image_node.getAttribute('id') - if image_id: + if image_id is not None: image['id'] = image_id - image_links = self._extract_links_from_node(image_node) - if len(image_links) > 0: - image['links'] = image_links + image['links'] = self._extract_links_from_node(image_node) return image @@ -340,9 +338,7 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): if flavor_id: flavor['id'] = flavor_id - flavor_links = self._extract_links_from_node(flavor_node) - if len(flavor_links) > 0: - flavor['links'] = flavor_links + flavor['links'] = self._extract_links_from_node(flavor_node) return flavor @@ -351,14 +347,12 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer): links = [] for link_node in self._find_children_named(parent_node, 'atom:link'): - link = {} - link_rel = link_node.getAttribute('rel') - if link_rel is not None: - link['rel'] = link_rel - link_href = link_node.getAttribute('href') - if link_href is not None: - link['href'] = link_href - links.append(link) + link = { + 'rel': link_node.getAttribute('rel'), + 'href': link_node.getAttribute('href'), + } + if link['rel'] is not None and link['href'] is not None: + links.append(link) return links diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index f239044ff..1e8749f56 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -491,10 +491,21 @@ class ControllerV11(Controller): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) + def _href_from_bookmark_links(self, links) + for link in links: + try: + if link.get('rel') == 'bookmark': + href = link.get('href') + if href is not None: + return href + except AttributeError: + msg = _("Malformed link entity") + raise exc.HTTPBadRequest(explanation=msg) + def _image_ref_from_req_data(self, data): try: image = data['server']['image'] - except (AttributeError, KeyError): + except (TypeError, KeyError): msg = _("Missing image entity") raise exc.HTTPBadRequest(explanation=msg) @@ -504,29 +515,21 @@ class ControllerV11(Controller): msg = _("Malformed image entity") raise exc.HTTPBadRequest(explanation=msg) - image_ref = None - for link in links: - try: - if link.get('rel') == 'bookmark': - image_ref = link.get('href') - break - except AttributeError: - msg = _("Malformed image link") - raise exc.HTTPBadRequest(explanation=msg) + image_ref = self._href_from_bookmark_links(links) if image_ref is None: try: - image_ref = image['id'] + return image['id'] except KeyError: msg = _("Missing id attribute on image entity") raise exc.HTTPBadRequest(explanation=msg) - - return image_ref + else: + return image_ref def _flavor_id_from_req_data(self, data): try: flavor = data['server']['flavor'] - except (AttributeError, KeyError): + except (TypeError, KeyError): msg = _("Missing flavor entity") raise exc.HTTPBadRequest(explanation=msg) @@ -536,15 +539,7 @@ class ControllerV11(Controller): msg = _("Malformed flavor entity") raise exc.HTTPBadRequest(explanation=msg) - flavor_ref = None - for link in links: - try: - if link.get('rel') == 'bookmark': - flavor_ref = link.get('href') - break - except AttributeError: - msg = _("Malformed flavor link") - raise exc.HTTPBadRequest(explanation=msg) + flavor_ref = self._href_from_bookmark_links(links) if flavor_ref is None: try: diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index cb7e03934..83b43b8ac 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -612,7 +612,7 @@ class ServersTest(test.TestCase): "_get_kernel_ramdisk_from_image", kernel_ramdisk_mapping) self.stubs.Set(nova.compute.api.API, "_find_host", find_host) - def test_create_instance(self): + def _test_create_instance_helper(self): self._setup_for_create_instance() body = dict(server=dict( @@ -635,6 +635,9 @@ class ServersTest(test.TestCase): self.assertEqual(3, server['imageId']) self.assertEqual(FAKE_UUID, server['uuid']) + def test_create_instance(self): + self._test_create_instance_helper() + def test_create_instance_has_uuid(self): """Tests at the db-layer instead of API layer since that's where the UUID is generated @@ -689,27 +692,7 @@ class ServersTest(test.TestCase): def test_create_instance_no_key_pair(self): fakes.stub_out_key_pair_funcs(self.stubs, have_key_pair=False) - self._setup_for_create_instance() - - body = dict(server=dict( - name='server_test', imageId=3, flavorId=2, - metadata={'hello': 'world', 'open': 'stack'}, - personality={})) - req = webob.Request.blank('/v1.0/servers') - req.method = 'POST' - req.body = json.dumps(body) - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app()) - - server = json.loads(res.body)['server'] - self.assertEqual(16, len(server['adminPass'])) - self.assertEqual('server_test', server['name']) - self.assertEqual(1, server['id']) - self.assertEqual(2, server['flavorId']) - self.assertEqual(3, server['imageId']) - self.assertEqual(FAKE_UUID, server['uuid']) - self.assertEqual(res.status_int, 200) + self._test_create_instance_helper() def test_create_instance_no_name(self): self._setup_for_create_instance() @@ -782,20 +765,20 @@ class ServersTest(test.TestCase): def test_create_instance_v1_1(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/3' - flavor_href = 'http://localhost/v1.1/flavors/2' + image_href = 'http://localhost/v1.1/images/2' + flavor_href = 'http://localhost/v1.1/flavors/3' body = { 'server': { 'name': 'server_test', 'image': { - 'id': 3, + 'id': 2, 'links': [ {'rel': 'bookmark', 'href': image_href}, ], }, 'flavor': { - 'id': 2, + 'id': 3, 'links': [ {'rel': 'bookmark', 'href': flavor_href}, ], @@ -1793,7 +1776,7 @@ class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase): def test_minimal_request(self): serial_request = """ """ + name="new-server-test" imageId="1" flavorId="1"/>""" request = self.deserializer.deserialize(serial_request, 'create') expected = {"server": { "name": "new-server-test", -- cgit From 486afc9b9e38a68c18b80daab4f23c5b936ee185 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 13 Jul 2011 11:17:05 -0400 Subject: pep8 --- nova/api/openstack/servers.py | 2 +- nova/tests/api/openstack/test_servers.py | 44 ++++++++++++++++------------- nova/tests/integrated/integrated_helpers.py | 4 +-- nova/tests/integrated/test_servers.py | 21 +++++++------- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 1e8749f56..50d1442d4 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -491,7 +491,7 @@ class ControllerV11(Controller): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - def _href_from_bookmark_links(self, links) + def _href_from_bookmark_links(self, links): for link in links: try: if link.get('rel') == 'bookmark': diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 83b43b8ac..ea3837519 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -789,8 +789,8 @@ class ServersTest(test.TestCase): }, 'personality': [ { - "path" : "/etc/banner.txt", - "contents" : "MQ==", + "path": "/etc/banner.txt", + "contents": "MQ==", }, ], }, @@ -844,7 +844,7 @@ class ServersTest(test.TestCase): 'server': { 'name': 'server_test', 'image': { - 'links':[ + 'links': [ {'rel': 'self', 'href': 'http://google.com'}, {'rel': 'bookmark', 'href': image_ref}, ], @@ -957,8 +957,6 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 400) - - def test_create_instance_with_admin_pass_v1_0(self): self._setup_for_create_instance() @@ -2064,8 +2062,8 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1"}, - "flavor": {"id": "2"}, + "image": {"id": "1", "links": []}, + "flavor": {"id": "2", "links": []}, }, } self.assertEquals(request['body'], expected) @@ -2091,7 +2089,7 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): }, ], }, - "flavor": {"id": "3"}, + "flavor": {"id": "3", "links": []}, }, } self.assertEquals(request['body'], expected) @@ -2108,7 +2106,7 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1"}, + "image": {"id": "1", "links": []}, "flavor": { "id": "2", "links": [ @@ -2134,8 +2132,8 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1"}, - "flavor": {"id": "2"}, + "image": {"id": "1", "links": []}, + "flavor": {"id": "2", "links": []}, "metadata": {}, "personality": [], }, @@ -2156,8 +2154,8 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1"}, - "flavor": {"id": "2"}, + "image": {"id": "1", "links": []}, + "flavor": {"id": "2", "links": []}, "metadata": {"one": "two", "open": "snack"}, }, } @@ -2177,8 +2175,8 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1"}, - "flavor": {"id": "2"}, + "image": {"id": "1", "links": []}, + "flavor": {"id": "2", "links": []}, "personality": [ {"path": "/etc/banner.txt", "contents": "MQ=="}, {"path": "/etc/hosts", "contents": "Mg=="}, @@ -2188,6 +2186,10 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): self.assertEquals(request['body'], expected) def test_spec_request(self): + image_self_link = "http://servers.api.openstack.org/v1.1/1234/" + \ + "images/52415800-8b69-11e0-9b19-734f6f006e54" + image_bookmark_link = "http://servers.api.openstack.org/1234/" + \ + "images/52415800-8b69-11e0-9b19-734f6f006e54" serial_request = """ + href="%s"/> + href="%s"/> @@ -2211,7 +2213,7 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): Mg== -""" +""" % (image_self_link, image_bookmark_link) request = self.deserializer.deserialize(serial_request, 'create') expected = { "server": { @@ -2233,7 +2235,10 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): }, ], }, - "flavor": {"id": "52415800-8b69-11e0-9b19-734f1195ff37"}, + "flavor": { + "id": "52415800-8b69-11e0-9b19-734f1195ff37", + "links": [], + }, "metadata": {"My Server Name": "Apache1"}, "personality": [ { @@ -2245,6 +2250,7 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): } self.assertEquals(request['body'], expected) + class TestServerInstanceCreation(test.TestCase): def setUp(self): diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index 47bd8c1e4..eb611ae0d 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -205,12 +205,12 @@ class _IntegratedTestBase(test.TestCase): image_href = 'http://fake.server/%s' % image_href # We now have a valid imageId - server['imageRef'] = image_href + server['image'] = {'links': [{'rel':'bookmark', 'href': image_href}]} # Set a valid flavorId flavor = self.api.get_flavors()[0] LOG.debug("Using flavor: %s" % flavor) - server['flavorRef'] = 'http://fake.server/%s' % flavor['id'] + server['flavor'] = {'id': flavor['id']} # Set a valid server name server_name = self.user.get_unused_server_name() diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index fcb517cf5..5c1812c76 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -49,31 +49,30 @@ class ServersTest(integrated_helpers._IntegratedTestBase): post = {'server': server} - # Without an imageRef, this throws 500. + # Without an image, this throws 400. # TODO(justinsb): Check whatever the spec says should be thrown here self.assertRaises(client.OpenStackApiException, self.api.post_server, post) - # With an invalid imageRef, this throws 500. - server['imageRef'] = self.user.get_invalid_image() + # With an invalid image entity, this throws 400. + bookmark = {'rel': 'bookmark', 'href': self.user.get_invalid_image()} + server['image'] = {'links': [bookmark]} # TODO(justinsb): Check whatever the spec says should be thrown here self.assertRaises(client.OpenStackApiException, self.api.post_server, post) - # Add a valid imageId/imageRef - server['imageId'] = good_server.get('imageId') - server['imageRef'] = good_server.get('imageRef') + # Add a valid image entity + server['image'] = good_server.get('image', {}) - # Without flavorId, this throws 500 + # Without a flavor entity, this throws 400 # TODO(justinsb): Check whatever the spec says should be thrown here self.assertRaises(client.OpenStackApiException, self.api.post_server, post) - # Set a valid flavorId/flavorRef - server['flavorRef'] = good_server.get('flavorRef') - server['flavorId'] = good_server.get('flavorId') + # Set a valid flavor etity + server['flavor'] = good_server.get('flavor', {}) - # Without a name, this throws 500 + # Without a name, this throws 400 # TODO(justinsb): Check whatever the spec says should be thrown here self.assertRaises(client.OpenStackApiException, self.api.post_server, post) -- cgit From 74d8a358193c9119f2edd17300eebd699ed6e755 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Fri, 15 Jul 2011 16:30:39 -0400 Subject: Added ServerXMLSerializationTest --- nova/tests/api/openstack/test_servers.py | 142 +++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index c0ccb3ef2..27308ee4e 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2927,12 +2927,12 @@ class ServersViewBuilderV11Test(test.TestCase): }, "flavor": { "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], }, "addresses": {}, "metadata": { @@ -2954,3 +2954,133 @@ class ServersViewBuilderV11Test(test.TestCase): output = self.view_builder.build(self.instance, True) self.assertDictMatch(output, expected_server) + + +class ServerXMLSerializationTest(test.TestCase): + + TIMESTAMP = "2010-10-11T10:30:22Z" + SERVER_HREF = 'http://localhost/v1.1/servers/123' + SERVER_BOOKMARK = 'http://localhost/servers/123' + IMAGE_BOOKMARK = 'http://localhost/images/5' + FLAVOR_BOOKMARK = 'http://localhost/flavors/1' + + def test_show(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": 1, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'show') + actual = minidom.parseString(output.replace(" ", "")) + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + expected_uuid = FAKE_UUID + expected = minidom.parseString(""" + + + + + + + + + + Stack + + + 1 + + + + + + + + + + + + + + + + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) -- cgit From 64a9c37cbf070345831ba6e4db646c5d972e179b Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sat, 16 Jul 2011 19:17:08 -0400 Subject: Added ServerXMLSerializer with working 'show' method Factored out MetadataXMLSerializer from images and servers into common --- nova/api/openstack/common.py | 50 ++++++++++++++++++ nova/api/openstack/image_metadata.py | 49 ------------------ nova/api/openstack/images.py | 4 +- nova/api/openstack/servers.py | 89 ++++++++++++++++++++++++++++++++ nova/tests/api/openstack/test_servers.py | 12 +++-- 5 files changed, 150 insertions(+), 54 deletions(-) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 8e12ce0c0..8a78292aa 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -17,12 +17,14 @@ import re from urlparse import urlparse +from xml.dom import minidom import webob from nova import exception from nova import flags from nova import log as logging +from nova.api.openstack import wsgi LOG = logging.getLogger('nova.api.openstack.common') @@ -162,3 +164,51 @@ def remove_version_from_href(href): msg = _('href does not contain version') raise ValueError(msg) return new_href + + +class MetadataXMLSerializer(wsgi.XMLDictSerializer): + def __init__(self, xmlns=wsgi.XMLNS_V11): + super(MetadataXMLSerializer, self).__init__(xmlns=xmlns) + + def _meta_item_to_xml(self, doc, key, value): + node = doc.createElement('meta') + doc.appendChild(node) + node.setAttribute('key', '%s' % key) + text = doc.createTextNode('%s' % value) + node.appendChild(text) + return node + + def meta_list_to_xml(self, xml_doc, meta_items): + container_node = xml_doc.createElement('metadata') + for (key, value) in meta_items: + item_node = self._meta_item_to_xml(xml_doc, key, value) + container_node.appendChild(item_node) + return container_node + + def _meta_list_to_xml_string(self, metadata_dict): + xml_doc = minidom.Document() + items = metadata_dict['metadata'].items() + container_node = self.meta_list_to_xml(xml_doc, items) + xml_doc.appendChild(container_node) + self._add_xmlns(container_node) + return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') + + def index(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def create(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def _meta_item_to_xml_string(self, meta_item_dict): + xml_doc = minidom.Document() + item_key, item_value = meta_item_dict.items()[0] + item_node = self._meta_item_to_xml(xml_doc, item_key, item_value) + xml_doc.appendChild(item_node) + self._add_xmlns(item_node) + return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') + + def show(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) + + def update(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index 4f33844fa..dd1a3f130 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -16,7 +16,6 @@ # under the License. from webob import exc -from xml.dom import minidom from nova import flags from nova import image @@ -111,54 +110,6 @@ class Controller(object): self.image_service.update(context, image_id, img, None) -class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer): - def __init__(self, xmlns=wsgi.XMLNS_V11): - super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns) - - def _meta_item_to_xml(self, doc, key, value): - node = doc.createElement('meta') - doc.appendChild(node) - node.setAttribute('key', '%s' % key) - text = doc.createTextNode('%s' % value) - node.appendChild(text) - return node - - def meta_list_to_xml(self, xml_doc, meta_items): - container_node = xml_doc.createElement('metadata') - for (key, value) in meta_items: - item_node = self._meta_item_to_xml(xml_doc, key, value) - container_node.appendChild(item_node) - return container_node - - def _meta_list_to_xml_string(self, metadata_dict): - xml_doc = minidom.Document() - items = metadata_dict['metadata'].items() - container_node = self.meta_list_to_xml(xml_doc, items) - xml_doc.appendChild(container_node) - self._add_xmlns(container_node) - return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') - - def index(self, metadata_dict): - return self._meta_list_to_xml_string(metadata_dict) - - def create(self, metadata_dict): - return self._meta_list_to_xml_string(metadata_dict) - - def _meta_item_to_xml_string(self, meta_item_dict): - xml_doc = minidom.Document() - item_key, item_value = meta_item_dict.items()[0] - item_node = self._meta_item_to_xml(xml_doc, item_key, item_value) - xml_doc.appendChild(item_node) - self._add_xmlns(item_node) - return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') - - def show(self, meta_item_dict): - return self._meta_item_to_xml_string(meta_item_dict['meta']) - - def update(self, meta_item_dict): - return self._meta_item_to_xml_string(meta_item_dict['meta']) - - def create_resource(): body_serializers = { 'application/xml': ImageMetadataXMLSerializer(), diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index d0317583e..6c7c0feb9 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -278,9 +278,9 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer): xmlns = wsgi.XMLNS_V11 def __init__(self): - self.metadata_serializer = image_metadata.ImageMetadataXMLSerializer() + self.metadata_serializer = common.MetadataXMLSerializer() - def _image_to_xml(self, xml_doc, image): + def image_to_xml(self, xml_doc, image): image_node = xml_doc.createElement('image') image_node.setAttribute('id', str(image['id'])) image_node.setAttribute('name', image['name']) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 93f8e832c..b07b903f8 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -17,6 +17,7 @@ import base64 import traceback from webob import exc +from xml.dom import minidom from nova import compute from nova import db @@ -612,6 +613,94 @@ class HeadersSerializer(wsgi.ResponseHeadersSerializer): response.status_int = 204 +class ServerXMLSerializer(wsgi.XMLDictSerializer): + + xmlns = wsgi.XMLNS_V11 + + def __init__(self): + self.metadata_serializer = common.MetadataXMLSerializer() + + def _create_basic_entity_node(self, xml_doc, id, links, name): + basic_node = xml_doc.createElement(name) + basic_node.setAttribute('id', str(id)) + link_nodes = self._create_link_nodes(xml_doc, links) + for link_node in link_nodes: + basic_node.appendChild(link_node) + return basic_node + + def _create_metadata_node(self, xml_doc, metadata): + return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata) + + def _create_addresses_node(self, xml_doc, addresses): + addresses_node = xml_doc.createElement('addresses') + for name, network_dict in addresses.items(): + network_node = self._create_network_node(xml_doc, + name, + network_dict) + addresses_node.appendChild(network_node) + return addresses_node + + def _create_network_node(self, xml_doc, network_name, network_dict): + network_node = xml_doc.createElement('network') + network_node.setAttribute('id', network_name) + for ip in network_dict: + ip_node = xml_doc.createElement('ip') + ip_node.setAttribute('version', str(ip['version'])) + ip_node.setAttribute('addr', ip['addr']) + network_node.appendChild(ip_node) + return network_node + + + def _add_server_attributes(self, node, server): + node.setAttribute('id', str(server['id'])) + node.setAttribute('uuid', str(server['uuid'])) + node.setAttribute('hostId', str(server['hostId'])) + node.setAttribute('name', server['name']) + node.setAttribute('created', server['created']) + node.setAttribute('updated', server['updated']) + node.setAttribute('status', server['status']) + if 'progress' in server: + node.setAttribute('progress', str(server['progress'])) + + def _server_to_xml_detailed(self, xml_doc, server): + server_node = xml_doc.createElement('server') + self._add_server_attributes(server_node, server) + + link_nodes = self._create_link_nodes(xml_doc, + server['links']) + for link_node in link_nodes: + server_node.appendChild(link_node) + + image_node = self._create_basic_entity_node(xml_doc, + server['image']['id'], + server['image']['links'], + 'image') + server_node.appendChild(image_node) + + flavor_node = self._create_basic_entity_node(xml_doc, + server['flavor']['id'], + server['flavor']['links'], + 'flavor') + server_node.appendChild(flavor_node) + + metadata = server.get('metadata', {}).items() + if len(metadata) > 0: + metadata_node = self._create_metadata_node(xml_doc, metadata) + server_node.appendChild(metadata_node) + + addresses_node = self._create_addresses_node(xml_doc, + server['addresses']) + server_node.appendChild(addresses_node) + + return server_node + + def show(self, server_dict): + xml_doc = minidom.Document() + node = self._server_to_xml_detailed(xml_doc, + server_dict['server']) + return self.to_xml_string(node, True) + + def create_resource(version='1.0'): controller = { '1.0': ControllerV10, diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 27308ee4e..ff878bba6 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2964,6 +2964,12 @@ class ServerXMLSerializationTest(test.TestCase): IMAGE_BOOKMARK = 'http://localhost/images/5' FLAVOR_BOOKMARK = 'http://localhost/flavors/1' + def setUp(self): + self.maxDiff = None + + def tearDown(self): + pass + def test_show(self): serializer = servers.ServerXMLSerializer() @@ -3048,12 +3054,14 @@ class ServerXMLSerializationTest(test.TestCase): uuid="%(expected_uuid)s" xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" - name="Image1" + name="test_server" updated="%(expected_now)s" created="%(expected_now)s" hostId="e4d909c290d0fb1ca068ffaddf22cbd0" status="BUILD" progress="0"> + + @@ -3078,8 +3086,6 @@ class ServerXMLSerializationTest(test.TestCase): - - """.replace(" ", "") % (locals())) -- cgit From 7af043463a350cfc71c45ff719354511173b5c39 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sat, 16 Jul 2011 19:39:27 -0400 Subject: Moved Metadata Serialization Test --- nova/api/openstack/image_metadata.py | 3 +- nova/api/openstack/images.py | 2 +- nova/tests/api/openstack/test_common.py | 132 ++++++++++++++++++++++++ nova/tests/api/openstack/test_image_metadata.py | 132 ------------------------ 4 files changed, 135 insertions(+), 134 deletions(-) diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index dd1a3f130..d1065a4e3 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -21,6 +21,7 @@ from nova import flags from nova import image from nova import quota from nova import utils +from nova.api.openstack import common from nova.api.openstack import faults from nova.api.openstack import wsgi @@ -112,7 +113,7 @@ class Controller(object): def create_resource(): body_serializers = { - 'application/xml': ImageMetadataXMLSerializer(), + 'application/xml': common.MetadataXMLSerializer(), } serializer = wsgi.ResponseSerializer(body_serializers) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 6c7c0feb9..df7bf0cf9 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -280,7 +280,7 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer): def __init__(self): self.metadata_serializer = common.MetadataXMLSerializer() - def image_to_xml(self, xml_doc, image): + def _image_to_xml(self, xml_doc, image): image_node = xml_doc.createElement('image') image_node.setAttribute('id', str(image['id'])) image_node.setAttribute('name', image['name']) diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index 4c4d03995..eec3f8f64 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -20,6 +20,7 @@ Test suites for 'common' code used throughout the OpenStack HTTP API. """ import webob.exc +import xml.dom.minidom as minidom from webob import Request @@ -247,3 +248,134 @@ class MiscFunctionsTest(test.TestCase): self.assertRaises(ValueError, common.get_id_from_href, fixture) + + +class MetadataXMLSerializationTest(test.TestCase): + + def test_index_xml(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + 'one': 'two', + 'three': 'four', + }, + } + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + + four + + + two + + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_index_xml_null(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + None: None, + }, + } + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + + None + + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_index_xml_unicode(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + u'three': u'Jos\xe9', + }, + } + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(u""" + + + Jos\xe9 + + + """.encode("UTF-8").replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_show_xml(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'meta': { + 'one': 'two', + }, + } + output = serializer.serialize(fixture, 'show') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + two + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_update_item_xml(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'meta': { + 'one': 'two', + }, + } + output = serializer.serialize(fixture, 'update') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + two + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_create_xml(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + 'key9': 'value9', + 'key2': 'value2', + 'key1': 'value1', + }, + } + output = serializer.serialize(fixture, 'create') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + + value2 + + + value9 + + + value1 + + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index d9fb61e2a..17cd35693 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -19,7 +19,6 @@ import json import stubout import unittest import webob -import xml.dom.minidom as minidom from nova import flags @@ -219,134 +218,3 @@ class ImageMetaDataTest(test.TestCase): req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) - - -class ImageMetadataXMLSerializationTest(test.TestCase): - - def test_index_xml(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - 'one': 'two', - 'three': 'four', - }, - } - output = serializer.serialize(fixture, 'index') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - - - four - - - two - - - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_index_xml_null(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - None: None, - }, - } - output = serializer.serialize(fixture, 'index') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - - - None - - - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_index_xml_unicode(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - u'three': u'Jos\xe9', - }, - } - output = serializer.serialize(fixture, 'index') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(u""" - - - Jos\xe9 - - - """.encode("UTF-8").replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_show_xml(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'meta': { - 'one': 'two', - }, - } - output = serializer.serialize(fixture, 'show') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - - two - - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_update_item_xml(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'meta': { - 'one': 'two', - }, - } - output = serializer.serialize(fixture, 'update') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - - two - - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_create_xml(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - 'key9': 'value9', - 'key2': 'value2', - 'key1': 'value1', - }, - } - output = serializer.serialize(fixture, 'create') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - - - value2 - - - value9 - - - value1 - - - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) -- cgit From c538d38d890e74382e928d225e8abdc57da9760e Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sat, 16 Jul 2011 19:45:28 -0400 Subject: pep8 --- nova/api/openstack/image_metadata.py | 2 +- nova/api/openstack/servers.py | 1 - nova/tests/api/openstack/test_servers.py | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index d1065a4e3..b9b782f92 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -21,7 +21,7 @@ from nova import flags from nova import image from nova import quota from nova import utils -from nova.api.openstack import common +from nova.api.openstack import common from nova.api.openstack import faults from nova.api.openstack import wsgi diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index b07b903f8..1a85fda58 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -650,7 +650,6 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): network_node.appendChild(ip_node) return network_node - def _add_server_attributes(self, node, server): node.setAttribute('id', str(server['id'])) node.setAttribute('uuid', str(server['uuid'])) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index ff878bba6..4d43be03d 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2771,8 +2771,7 @@ class ServersViewBuilderV11Test(test.TestCase): address_builder, flavor_builder, image_builder, - base_url - ) + base_url) return view_builder def test_build_server(self): -- cgit From 712493f65415a7a5fc727f6b316c66ef90f1cad5 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sun, 17 Jul 2011 14:50:44 -0400 Subject: added index to servers xml serializer --- nova/api/openstack/servers.py | 29 ++++++++++++++++ nova/tests/api/openstack/test_servers.py | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 1a85fda58..8d59c8626 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -661,6 +661,16 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): if 'progress' in server: node.setAttribute('progress', str(server['progress'])) + def _server_to_xml(self, xml_doc, server): + server_node = xml_doc.createElement('server') + server_node.setAttribute('id', str(server['id'])) + server_node.setAttribute('name', server['name']) + link_nodes = self._create_link_nodes(xml_doc, + server['links']) + for link_node in link_nodes: + server_node.appendChild(link_node) + return server_node + def _server_to_xml_detailed(self, xml_doc, server): server_node = xml_doc.createElement('server') self._add_server_attributes(server_node, server) @@ -693,6 +703,25 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): return server_node + def _server_list_to_xml(self, xml_doc, servers, detailed): + container_node = xml_doc.createElement('servers') + if detailed: + server_to_xml = self._server_to_xml_detailed + else: + server_to_xml = self._server_to_xml + + for server in servers: + item_node = server_to_xml(xml_doc, server) + container_node.appendChild(item_node) + return container_node + + def index(self, servers_dict): + xml_doc = minidom.Document() + node = self._server_list_to_xml(xml_doc, + servers_dict['servers'], + detailed=False) + return self.to_xml_string(node, True) + def show(self, server_dict): xml_doc = minidom.Document() node = self._server_to_xml_detailed(xml_doc, diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 4d43be03d..6ef789d33 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3089,3 +3089,60 @@ class ServerXMLSerializationTest(test.TestCase): """.replace(" ", "") % (locals())) self.assertEqual(expected.toxml(), actual.toxml()) + + def test_index(self): + serializer = servers.ServerXMLSerializer() + + expected_server_href = 'http://localhost/v1.1/servers/1' + expected_server_bookmark = 'http://localhost/servers/1' + expected_server_href_2 = 'http://localhost/v1.1/servers/2' + expected_server_bookmark_2 = 'http://localhost/servers/2' + fixture = { "servers": [ + { + "id": 1, + "name": "test_server", + 'links': [ + { + 'href': expected_server_href, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark, + 'rel': 'bookmark', + }, + ], + }, + { + "id": 2, + "name": "test_server_2", + 'links': [ + { + 'href': expected_server_href_2, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark_2, + 'rel': 'bookmark', + }, + ], + }, + ]} + + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + + + + + + + + + + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) -- cgit From baaaa80d36570d5734ac823bc49be8ff2477e5c2 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sun, 17 Jul 2011 21:24:02 -0400 Subject: added 'detail' to server XML serializer --- nova/api/openstack/servers.py | 7 ++ nova/tests/api/openstack/test_servers.py | 188 +++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 8d59c8626..82630397f 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -722,6 +722,13 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): detailed=False) return self.to_xml_string(node, True) + def detail(self, servers_dict): + xml_doc = minidom.Document() + node = self._server_list_to_xml(xml_doc, + servers_dict['servers'], + detailed=True) + return self.to_xml_string(node, True) + def show(self, server_dict): xml_doc = minidom.Document() node = self._server_to_xml_detailed(xml_doc, diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 6ef789d33..037e35545 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3146,3 +3146,191 @@ class ServerXMLSerializationTest(test.TestCase): """.replace(" ", "") % (locals())) self.assertEqual(expected.toxml(), actual.toxml()) + + def test_detail(self): + serializer = servers.ServerXMLSerializer() + + expected_server_href = 'http://localhost/v1.1/servers/1' + expected_server_bookmark = 'http://localhost/servers/1' + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + expected_uuid = FAKE_UUID + + expected_server_href_2 = 'http://localhost/v1.1/servers/2' + expected_server_bookmark_2 = 'http://localhost/servers/2' + fixture = { "servers": [ + { + "id": 1, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": expected_image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": expected_flavor_bookmark, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + }, + "metadata": { + "Number": "1", + }, + "links": [ + { + "href": expected_server_href, + "rel": "self", + }, + { + "href": expected_server_bookmark, + "rel": "bookmark", + }, + ], + }, + { + "id": 2, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 100, + "name": "test_server_2", + "status": "ACTIVE", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": expected_image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": expected_flavor_bookmark, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + }, + "metadata": { + "Number": "2", + }, + "links": [ + { + "href": expected_server_href_2, + "rel": "self", + }, + { + "href": expected_server_bookmark_2, + "rel": "bookmark", + }, + ], + }, + ]} + + output = serializer.serialize(fixture, 'detail') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) -- cgit From 8ab775585fee4af7b30a28a5bffae46c23ec76d1 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sun, 17 Jul 2011 21:36:57 -0400 Subject: added 'create' to server XML serializer --- nova/api/openstack/servers.py | 7 ++ nova/tests/api/openstack/test_servers.py | 123 +++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 82630397f..4a29e1454 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -735,6 +735,13 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): server_dict['server']) return self.to_xml_string(node, True) + def create(self, server_dict): + xml_doc = minidom.Document() + node = self._server_to_xml_detailed(xml_doc, + server_dict['server']) + node.setAttribute('adminPass', server_dict['server']['adminPass']) + return self.to_xml_string(node, True) + def create_resource(version='1.0'): controller = { diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 037e35545..3b50b4331 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3090,6 +3090,129 @@ class ServerXMLSerializationTest(test.TestCase): self.assertEqual(expected.toxml(), actual.toxml()) + def test_create(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": 1, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", + "adminPass": "test_password", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'create') + actual = minidom.parseString(output.replace(" ", "")) + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + expected_uuid = FAKE_UUID + expected = minidom.parseString(""" + + + + + + + + + + + + Stack + + + 1 + + + + + + + + + + + + + + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) + def test_index(self): serializer = servers.ServerXMLSerializer() -- cgit From e23e70afd096ca1d7ad22c776f6f439986bbc8b5 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sun, 17 Jul 2011 22:28:16 -0400 Subject: updated servers to use ServerXMLSerializer --- nova/api/openstack/servers.py | 3 +- nova/tests/api/openstack/test_servers.py | 71 ++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 4a29e1454..a6229125d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -773,8 +773,7 @@ def create_resource(version='1.0'): headers_serializer = HeadersSerializer() body_serializers = { - 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, - xmlns=xmlns), + 'application/xml': ServerXMLSerializer(), } body_deserializers = { diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 3b50b4331..413996fdc 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -392,6 +392,77 @@ class ServersTest(test.TestCase): self.assertDictMatch(res_dict, expected_server) + def test_get_server_by_id_v1_1_xml(self): + image_bookmark = "http://localhost/images/10" + flavor_ref = "http://localhost/v1.1/flavors/1" + flavor_id = "1" + flavor_bookmark = "http://localhost/flavors/1" + server_href = "http://localhost/v1.1/servers/1" + server_bookmark = "http://localhost/servers/1" + + public_ip = '192.168.0.3' + private_ip = '172.19.0.1' + interfaces = [ + { + 'network': {'label': 'public'}, + 'fixed_ips': [ + {'address': public_ip}, + ], + }, + { + 'network': {'label': 'private'}, + 'fixed_ips': [ + {'address': private_ip}, + ], + }, + ] + new_return_server = return_server_with_interfaces(interfaces) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + + req = webob.Request.blank('/v1.1/servers/1') + req.headers['Accept'] = 'application/xml' + res = req.get_response(fakes.wsgi_app()) + actual = minidom.parseString(res.body.replace(' ', '')) + expected_uuid = FAKE_UUID + expected_updated = "2010-11-11T11:00:00Z" + expected_created = "2010-10-10T12:00:00Z" + expected = minidom.parseString(""" + + + + + + + + + + + + 1 + + + + + + + + + + + + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) + def test_get_server_with_active_status_by_id_v1_1(self): image_bookmark = "http://localhost/images/10" flavor_ref = "http://localhost/v1.1/flavors/1" -- cgit From 596b38b8c899727c4750aa9b5a05a1ab2c2ecad3 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sun, 17 Jul 2011 22:29:28 -0400 Subject: pep8 --- nova/tests/api/openstack/test_servers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 413996fdc..782db21ec 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3291,7 +3291,7 @@ class ServerXMLSerializationTest(test.TestCase): expected_server_bookmark = 'http://localhost/servers/1' expected_server_href_2 = 'http://localhost/v1.1/servers/2' expected_server_bookmark_2 = 'http://localhost/servers/2' - fixture = { "servers": [ + fixture = {"servers": [ { "id": 1, "name": "test_server", @@ -3353,7 +3353,7 @@ class ServerXMLSerializationTest(test.TestCase): expected_server_href_2 = 'http://localhost/v1.1/servers/2' expected_server_bookmark_2 = 'http://localhost/servers/2' - fixture = { "servers": [ + fixture = {"servers": [ { "id": 1, "uuid": FAKE_UUID, -- cgit From b9d316452a1e2a204e56d1434feade1ab0bd281c Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Sun, 17 Jul 2011 22:50:10 -0400 Subject: Updated servers to choose XML serializer based on api version --- nova/api/openstack/servers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index a6229125d..6d55b856d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -772,8 +772,13 @@ def create_resource(version='1.0'): headers_serializer = HeadersSerializer() + xml_serializer = { + '1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10), + '1.1': ServerXMLSerializer(), + }[version] + body_serializers = { - 'application/xml': ServerXMLSerializer(), + 'application/xml': xml_serializer, } body_deserializers = { -- cgit From 77347efae5171e5a6ffa5af885c0ffd7220688cf Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Wed, 20 Jul 2011 15:38:29 -0500 Subject: Fix plus passing tests --- nova/tests/test_xenapi.py | 39 +++++++++++++++++++++++++++++++++++++++ nova/virt/xenapi/vmops.py | 9 ++++++--- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 4cb7447d3..9b512b73b 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -785,6 +785,45 @@ class XenAPIMigrateInstance(test.TestCase): def test_finish_resize(self): instance = db.instance_create(self.context, self.values) + self.called = False + + def fake_vdi_resize(*args, **kwargs): + self.called = True + + self.stubs.Set(stubs.FakeSessionForMigrationTests, + "VDI_resize_online", fake_vdi_resize) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + stubs.stubout_loopingcall_start(self.stubs) + conn = xenapi_conn.get_connection(False) + network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False}, + {'broadcast': '192.168.0.255', + 'dns': ['192.168.0.1'], + 'gateway': '192.168.0.1', + 'gateway6': 'dead:beef::1', + 'ip6s': [{'enabled': '1', + 'ip': 'dead:beef::dcad:beff:feef:0', + 'netmask': '64'}], + 'ips': [{'enabled': '1', + 'ip': '192.168.0.100', + 'netmask': '255.255.255.0'}], + 'label': 'fake', + 'mac': 'DE:AD:BE:EF:00:00', + 'rxtx_cap': 3})] + conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'), + network_info) + self.assertEqual(self.called, True) + + def test_finish_migrate_no_resize_vdi(self): + tiny_type_id = \ + instance_types.get_instance_type_by_name('m1.tiny')['id'] + self.values.update({'instance_type_id': tiny_type_id, 'local_gb': 0}) + instance = db.instance_create(self.context, self.values) + + def fake_vdi_resize(*args, **kwargs): + raise Exception("This shouldn't be called") + + self.stubs.Set(stubs.FakeSessionForMigrationTests, + "VDI_resize_online", fake_vdi_resize) stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) stubs.stubout_loopingcall_start(self.stubs) conn = xenapi_conn.get_connection(False) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c332c27b0..8bce6bb89 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -566,17 +566,20 @@ class VMOps(object): return new_cow_uuid def resize_instance(self, instance, vdi_uuid): - """Resize a running instance by changing it's RAM and disk size.""" + """Resize a running instance by changing its RAM and disk size.""" #TODO(mdietz): this will need to be adjusted for swap later #The new disk size must be in bytes - new_disk_size = str(instance.local_gb * 1024 * 1024 * 1024) + new_disk_size = instance.local_gb * 1024 * 1024 * 1024 instance_name = instance.name instance_local_gb = instance.local_gb LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance %(instance_name)s." " Expanding to %(instance_local_gb)d GB") % locals()) vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - self._session.call_xenapi('VDI.resize_online', vdi_ref, new_disk_size) + # for an instance with no local storage + if new_disk_size > 0: + self._session.call_xenapi('VDI.resize_online', vdi_ref, + str(new_disk_size)) LOG.debug(_("Resize instance %s complete") % (instance.name)) def reboot(self, instance): -- cgit From 76aab6d65fa35ae88f9b16acd4ee2968dfe049ce Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Wed, 20 Jul 2011 16:56:45 -0500 Subject: CHanges based on feedback --- nova/compute/manager.py | 20 ++++++++++++-------- nova/tests/test_xenapi.py | 36 +++++++++++++++++++++++++++++++++--- nova/virt/xenapi/vmops.py | 9 +++++---- nova/virt/xenapi_conn.py | 6 ++++-- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index eb3996d29..01a7d195f 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -820,20 +820,24 @@ class ComputeManager(manager.SchedulerDependentManager): migration_ref['instance_id']) # TODO(mdietz): apply the rest of the instance_type attributes going # after they're supported - instance_type = self.db.instance_type_get_by_flavor_id(context, - migration_ref['new_flavor_id']) - self.db.instance_update(context, instance_id, - dict(instance_type_id=instance_type['id'], - memory_mb=instance_type['memory_mb'], - vcpus=instance_type['vcpus'], - local_gb=instance_type['local_gb'])) + resize_instance = False + if migration_ref['old_flavor_id'] != migration_ref['new_flavor_id']: + instance_type = self.db.instance_type_get_by_flavor_id(context, + migration_ref['new_flavor_id']) + self.db.instance_update(context, instance_id, + dict(instance_type_id=instance_type['id'], + memory_mb=instance_type['memory_mb'], + vcpus=instance_type['vcpus'], + local_gb=instance_type['local_gb'])) + resize_instance = True # reload the updated instance ref # FIXME(mdietz): is there reload functionality? instance = self.db.instance_get(context, instance_id) network_info = self.network_api.get_instance_nw_info(context, instance) - self.driver.finish_resize(instance, disk_info, network_info) + self.driver.finish_resize(instance, disk_info, network_info, + resize_instance) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 9b512b73b..be263d17c 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -810,10 +810,10 @@ class XenAPIMigrateInstance(test.TestCase): 'mac': 'DE:AD:BE:EF:00:00', 'rxtx_cap': 3})] conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'), - network_info) + network_info, resize_instance=True) self.assertEqual(self.called, True) - def test_finish_migrate_no_resize_vdi(self): + def test_finish_migrate_no_local_storage(self): tiny_type_id = \ instance_types.get_instance_type_by_name('m1.tiny')['id'] self.values.update({'instance_type_id': tiny_type_id, 'local_gb': 0}) @@ -842,7 +842,37 @@ class XenAPIMigrateInstance(test.TestCase): 'mac': 'DE:AD:BE:EF:00:00', 'rxtx_cap': 3})] conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'), - network_info) + network_info, resize_instance=True) + + def test_finish_migrate_no_resize_vdi(self): + instance = db.instance_create(self.context, self.values) + + def fake_vdi_resize(*args, **kwargs): + raise Exception("This shouldn't be called") + + self.stubs.Set(stubs.FakeSessionForMigrationTests, + "VDI_resize_online", fake_vdi_resize) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + stubs.stubout_loopingcall_start(self.stubs) + conn = xenapi_conn.get_connection(False) + network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False}, + {'broadcast': '192.168.0.255', + 'dns': ['192.168.0.1'], + 'gateway': '192.168.0.1', + 'gateway6': 'dead:beef::1', + 'ip6s': [{'enabled': '1', + 'ip': 'dead:beef::dcad:beff:feef:0', + 'netmask': '64'}], + 'ips': [{'enabled': '1', + 'ip': '192.168.0.100', + 'netmask': '255.255.255.0'}], + 'label': 'fake', + 'mac': 'DE:AD:BE:EF:00:00', + 'rxtx_cap': 3})] + + # Resize instance would be determined by the compute call + conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'), + network_info, resize_instance=False) class XenAPIDetermineDiskImageTestCase(test.TestCase): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 8bce6bb89..aec14f880 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -110,13 +110,14 @@ class VMOps(object): vm_ref = VMHelper.lookup(self._session, instance.name) self._start(instance, vm_ref) - def finish_resize(self, instance, disk_info, network_info): + def finish_resize(self, instance, disk_info, network_info, + resize_instance): vdi_uuid = self.link_disks(instance, disk_info['base_copy'], disk_info['cow']) vm_ref = self._create_vm(instance, [dict(vdi_type='os', vdi_uuid=vdi_uuid)], network_info) - self.resize_instance(instance, vdi_uuid) + self.resize_instance(instance, vdi_uuid, resize_instance) self._spawn(instance, vm_ref) def _start(self, instance, vm_ref=None): @@ -565,7 +566,7 @@ class VMOps(object): return new_cow_uuid - def resize_instance(self, instance, vdi_uuid): + def resize_instance(self, instance, vdi_uuid, resize_instance): """Resize a running instance by changing its RAM and disk size.""" #TODO(mdietz): this will need to be adjusted for swap later #The new disk size must be in bytes @@ -577,7 +578,7 @@ class VMOps(object): " Expanding to %(instance_local_gb)d GB") % locals()) vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) # for an instance with no local storage - if new_disk_size > 0: + if resize_instance and new_disk_size > 0: self._session.call_xenapi('VDI.resize_online', vdi_ref, str(new_disk_size)) LOG.debug(_("Resize instance %s complete") % (instance.name)) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index ec8c44c1c..18654d7e5 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -202,9 +202,11 @@ class XenAPIConnection(driver.ComputeDriver): """Reverts a resize, powering back on the instance""" self._vmops.revert_resize(instance) - def finish_resize(self, instance, disk_info, network_info): + def finish_resize(self, instance, disk_info, network_info, + resize_instance=False): """Completes a resize, turning on the migrated instance""" - self._vmops.finish_resize(instance, disk_info, network_info) + self._vmops.finish_resize(instance, disk_info, network_info, + resize_instance) def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ -- cgit From 806be42000cf54e5b2ff9fb03446e8e6924bd38b Mon Sep 17 00:00:00 2001 From: "matt.dietz@rackspace.com" <> Date: Thu, 21 Jul 2011 12:46:58 -0500 Subject: Renamed the virt driver resize methods to migration for marginally more understandable code --- nova/compute/manager.py | 4 ++-- nova/tests/test_compute.py | 6 +++--- nova/tests/test_xenapi.py | 8 ++++---- nova/virt/driver.py | 4 ++-- nova/virt/xenapi/vmops.py | 24 +++++++++++++----------- nova/virt/xenapi_conn.py | 6 +++--- 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 01a7d195f..78d7f6479 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -723,7 +723,7 @@ class ComputeManager(manager.SchedulerDependentManager): local_gb=instance_type['local_gb'], instance_type_id=instance_type['id'])) - self.driver.revert_resize(instance_ref) + self.driver.revert_migration(instance_ref) self.db.migration_update(context, migration_id, {'status': 'reverted'}) usage_info = utils.usage_from_instance(instance_ref) @@ -836,7 +836,7 @@ class ComputeManager(manager.SchedulerDependentManager): instance = self.db.instance_get(context, instance_id) network_info = self.network_api.get_instance_nw_info(context, instance) - self.driver.finish_resize(instance, disk_info, network_info, + self.driver.finish_migration(instance, disk_info, network_info, resize_instance) self.db.migration_update(context, migration_id, diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index dc3f0596d..352011e52 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -420,7 +420,7 @@ class ComputeTestCase(test.TestCase): def fake(*args, **kwargs): pass - self.stubs.Set(self.compute.driver, 'finish_resize', fake) + self.stubs.Set(self.compute.driver, 'finish_migration', fake) self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake) context = self.context.elevated() instance_id = self._create_instance() @@ -527,8 +527,8 @@ class ComputeTestCase(test.TestCase): def fake(*args, **kwargs): pass - self.stubs.Set(self.compute.driver, 'finish_resize', fake) - self.stubs.Set(self.compute.driver, 'revert_resize', fake) + self.stubs.Set(self.compute.driver, 'finish_migration', fake) + self.stubs.Set(self.compute.driver, 'revert_migration', fake) self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake) self.compute.run_instance(self.context, instance_id) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index be263d17c..9f203c477 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -783,7 +783,7 @@ class XenAPIMigrateInstance(test.TestCase): conn = xenapi_conn.get_connection(False) conn.migrate_disk_and_power_off(instance, '127.0.0.1') - def test_finish_resize(self): + def test_finish_migrate(self): instance = db.instance_create(self.context, self.values) self.called = False @@ -809,7 +809,7 @@ class XenAPIMigrateInstance(test.TestCase): 'label': 'fake', 'mac': 'DE:AD:BE:EF:00:00', 'rxtx_cap': 3})] - conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'), + conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'), network_info, resize_instance=True) self.assertEqual(self.called, True) @@ -841,7 +841,7 @@ class XenAPIMigrateInstance(test.TestCase): 'label': 'fake', 'mac': 'DE:AD:BE:EF:00:00', 'rxtx_cap': 3})] - conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'), + conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'), network_info, resize_instance=True) def test_finish_migrate_no_resize_vdi(self): @@ -871,7 +871,7 @@ class XenAPIMigrateInstance(test.TestCase): 'rxtx_cap': 3})] # Resize instance would be determined by the compute call - conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'), + conn.finish_migration(instance, dict(base_copy='hurr', cow='durr'), network_info, resize_instance=False) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 178279d31..59582d253 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -122,11 +122,11 @@ class ComputeDriver(object): """Create snapshot from a running VM instance.""" raise NotImplementedError() - def finish_resize(self, instance, disk_info): + def finish_migration(self, instance, disk_info): """Completes a resize, turning on the migrated instance""" raise NotImplementedError() - def revert_resize(self, instance): + def revert_migration(self, instance): """Reverts a resize, powering back on the instance""" raise NotImplementedError() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index aec14f880..008feea0a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -106,18 +106,19 @@ class VMOps(object): instance_infos.append(instance_info) return instance_infos - def revert_resize(self, instance): + def revert_migration(self, instance): vm_ref = VMHelper.lookup(self._session, instance.name) self._start(instance, vm_ref) - def finish_resize(self, instance, disk_info, network_info, + def finish_migration(self, instance, disk_info, network_info, resize_instance): vdi_uuid = self.link_disks(instance, disk_info['base_copy'], disk_info['cow']) vm_ref = self._create_vm(instance, [dict(vdi_type='os', vdi_uuid=vdi_uuid)], network_info) - self.resize_instance(instance, vdi_uuid, resize_instance) + if resize_instance: + self.resize_instance(instance, vdi_uuid, resize_instance) self._spawn(instance, vm_ref) def _start(self, instance, vm_ref=None): @@ -572,16 +573,17 @@ class VMOps(object): #The new disk size must be in bytes new_disk_size = instance.local_gb * 1024 * 1024 * 1024 - instance_name = instance.name - instance_local_gb = instance.local_gb - LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance %(instance_name)s." - " Expanding to %(instance_local_gb)d GB") % locals()) - vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - # for an instance with no local storage - if resize_instance and new_disk_size > 0: + if new_disk_size > 0: + instance_name = instance.name + instance_local_gb = instance.local_gb + LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance" + "%(instance_name)s. Expanding to %(instance_local_gb)d" + " GB") % locals()) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) + # for an instance with no local storage self._session.call_xenapi('VDI.resize_online', vdi_ref, str(new_disk_size)) - LOG.debug(_("Resize instance %s complete") % (instance.name)) + LOG.debug(_("Resize instance %s complete") % (instance.name)) def reboot(self, instance): """Reboot VM instance.""" diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 18654d7e5..0e86d9e76 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -198,14 +198,14 @@ class XenAPIConnection(driver.ComputeDriver): """Create VM instance""" self._vmops.spawn(instance, network_info) - def revert_resize(self, instance): + def revert_migration(self, instance): """Reverts a resize, powering back on the instance""" self._vmops.revert_resize(instance) - def finish_resize(self, instance, disk_info, network_info, + def finish_migration(self, instance, disk_info, network_info, resize_instance=False): """Completes a resize, turning on the migrated instance""" - self._vmops.finish_resize(instance, disk_info, network_info, + self._vmops.finish_migration(instance, disk_info, network_info, resize_instance) def snapshot(self, instance, image_id): -- cgit From 8383838afffeedcde8cd0dc486e32d2f5bb26f8e Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 21 Jul 2011 22:46:36 +0000 Subject: change context to maintain exact time, store roles, use ids instead of objects and use a uuid for request_id --- nova/context.py | 88 ++++++++++++++++++--------------------------------------- nova/utils.py | 23 ++++++++++----- 2 files changed, 43 insertions(+), 68 deletions(-) diff --git a/nova/context.py b/nova/context.py index 99085ed75..e7c60142c 100644 --- a/nova/context.py +++ b/nova/context.py @@ -18,9 +18,8 @@ """RequestContext: context for requests that persist through all of nova.""" -import random +import uuid -from nova import exception from nova import utils @@ -31,86 +30,53 @@ class RequestContext(object): """ - def __init__(self, user, project, is_admin=None, read_deleted=False, - remote_address=None, timestamp=None, request_id=None): - if hasattr(user, 'id'): - self._user = user - self.user_id = user.id - else: - self._user = None - self.user_id = user - if hasattr(project, 'id'): - self._project = project - self.project_id = project.id - else: - self._project = None - self.project_id = project - if is_admin is None: - if self.user_id and self.user: - self.is_admin = self.user.is_admin() + def __init__(self, user_id, project_id, is_admin=None, read_deleted=False, + roles=None, remote_address=None, timestamp=None, request_id=None): + self.user_id = user_id + self.project_id = project_id + self.roles = roles or [] + self.is_admin = is_admin + if self.is_admin is None: + if 'admin' in self.roles: + self.is_admin = True else: self.is_admin = False - else: - self.is_admin = is_admin self.read_deleted = read_deleted self.remote_address = remote_address if not timestamp: timestamp = utils.utcnow() - if isinstance(timestamp, str) or isinstance(timestamp, unicode): - timestamp = utils.parse_isotime(timestamp) + if isinstance(timestamp, basestring): + timestamp = utils.parse_strtime(timestamp) self.timestamp = timestamp if not request_id: - chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-' - request_id = ''.join([random.choice(chars) for x in xrange(20)]) + request_id = unicode(uuid.uuid4()) self.request_id = request_id - @property - def user(self): - # NOTE(vish): Delay import of manager, so that we can import this - # file from manager. - from nova.auth import manager - if not self._user: - try: - self._user = manager.AuthManager().get_user(self.user_id) - except exception.NotFound: - pass - return self._user - - @property - def project(self): - # NOTE(vish): Delay import of manager, so that we can import this - # file from manager. - from nova.auth import manager - if not self._project: - try: - auth_manager = manager.AuthManager() - self._project = auth_manager.get_project(self.project_id) - except exception.NotFound: - pass - return self._project - def to_dict(self): - return {'user': self.user_id, - 'project': self.project_id, + return {'user_id': self.user_id, + 'project_id': self.project_id, 'is_admin': self.is_admin, 'read_deleted': self.read_deleted, + 'roles': self.roles, 'remote_address': self.remote_address, - 'timestamp': utils.isotime(self.timestamp), + 'timestamp': utils.strtime(self.timestamp), 'request_id': self.request_id} @classmethod def from_dict(cls, values): return cls(**values) - def elevated(self, read_deleted=False): + def elevated(self, read_deleted=None): """Return a version of this context with admin flag set.""" - return RequestContext(self.user_id, - self.project_id, - True, - read_deleted, - self.remote_address, - self.timestamp, - self.request_id) + rd = self.read_deleted if read_deleted is None else read_deleted + return RequestContext(user_id=self.user_id, + project_id=self.project_id, + is_admin=True, + read_deleted=rd, + roles=self.roles, + remote_address=self.remote_address, + timestamp=self.timestamp, + request_id=self.request_id) def get_admin_context(read_deleted=False): diff --git a/nova/utils.py b/nova/utils.py index 8784a227d..737903f81 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -19,7 +19,6 @@ """Utilities and helper functions.""" -import base64 import datetime import functools import inspect @@ -30,7 +29,6 @@ import os import random import re import socket -import string import struct import sys import time @@ -50,7 +48,8 @@ from nova import version LOG = logging.getLogger("nova.utils") -TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" +ISO_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" +PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" FLAGS = flags.FLAGS @@ -361,16 +360,26 @@ def clear_time_override(): utcnow.override_time = None -def isotime(at=None): - """Returns iso formatted utcnow.""" +def strtime(at=None, fmt=PERFECT_TIME_FORMAT): + """Returns formatted utcnow.""" if not at: at = utcnow() - return at.strftime(TIME_FORMAT) + return at.strftime(fmt) + + +def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): + """Turn a formatted time back into a datetime.""" + return datetime.datetime.strptime(timestr, fmt) + + +def isotime(at=None): + """Returns iso formatted utcnow.""" + return strtime(at, ISO_TIME_FORMAT) def parse_isotime(timestr): """Turn an iso formatted time back into a datetime.""" - return datetime.datetime.strptime(timestr, TIME_FORMAT) + return parse_strtime(timestr, ISO_TIME_FORMAT) def parse_mailmap(mailmap='.mailmap'): -- cgit From 5f75097eb46fa03814fe53c5d9fda84f0000fdd4 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 21 Jul 2011 22:46:57 +0000 Subject: start removing references to AuthManager --- nova/api/direct.py | 3 +- nova/api/ec2/__init__.py | 10 +++--- nova/api/openstack/auth.py | 26 ++++----------- nova/cloudpipe/pipelib.py | 18 +++++----- nova/compute/api.py | 2 +- nova/db/sqlalchemy/api.py | 4 +-- nova/image/s3.py | 6 ++-- nova/log.py | 4 +-- nova/tests/api/openstack/test_auth.py | 1 - nova/tests/hyperv_unittest.py | 2 +- nova/tests/scheduler/test_scheduler.py | 17 +++------- nova/tests/test_access.py | 2 +- nova/tests/test_adminapi.py | 4 +-- nova/tests/test_cloud.py | 12 +++---- nova/tests/test_libvirt.py | 9 +++-- nova/tests/test_quota.py | 60 +++++++++++++++------------------- 16 files changed, 77 insertions(+), 103 deletions(-) diff --git a/nova/api/direct.py b/nova/api/direct.py index ec79151b1..993815fc7 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -107,7 +107,8 @@ class DelegatedAuthMiddleware(wsgi.Middleware): def process_request(self, request): os_user = request.headers['X-OpenStack-User'] os_project = request.headers['X-OpenStack-Project'] - context_ref = context.RequestContext(user=os_user, project=os_project) + context_ref = context.RequestContext(user_id=os_user, + project_id=os_project) request.environ['openstack.context'] = context_ref diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index cf1734281..8bb2ea944 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -174,8 +174,8 @@ class Authenticate(wsgi.Middleware): remote_address = req.remote_addr if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) - ctxt = context.RequestContext(user=user, - project=project, + ctxt = context.RequestContext(user_id=user.id, + project_id=project.id, remote_address=remote_address) req.environ['ec2.context'] = ctxt uname = user.name @@ -295,13 +295,15 @@ class Authorizer(wsgi.Middleware): def _matches_any_role(self, context, roles): """Return True if any role in roles is allowed in context.""" - if context.user.is_superuser(): + authman = manager.AuthManager() + user = authman.get_user(context.user_id) + if user.is_superuser(): return True if 'all' in roles: return True if 'none' in roles: return False - return any(context.project.has_role(context.user_id, role) + return any(authman.has_role(context.user_id, role, context.project_id) for role in roles) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 7c3e683d6..5b387c081 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -48,31 +48,19 @@ class AuthMiddleware(wsgi.Middleware): def __call__(self, req): if not self.has_authentication(req): return self.authenticate(req) - user = self.get_user_by_authentication(req) - if not user: + user_id = self.get_user_by_authentication(req) + if not user_id: token = req.headers["X-Auth-Token"] - msg = _("%(user)s could not be found with token '%(token)s'") + msg = _("%(user_id)s could not be found with token '%(token)s'") LOG.warn(msg % locals()) return faults.Fault(webob.exc.HTTPUnauthorized()) try: - account = req.headers["X-Auth-Project-Id"] + project_id = req.headers["X-Auth-Project-Id"] except KeyError: - # FIXME(usrleon): It needed only for compatibility - # while osapi clients don't use this header - accounts = self.auth.get_projects(user=user) - if accounts: - account = accounts[0] - else: - return faults.Fault(webob.exc.HTTPUnauthorized()) - - if not self.auth.is_admin(user) and \ - not self.auth.is_project_member(user, account): - msg = _("%(user)s must be an admin or a member of %(account)s") - LOG.warn(msg % locals()) - return faults.Fault(webob.exc.HTTPUnauthorized()) + project_id = user_id - req.environ['nova.context'] = context.RequestContext(user, account) + req.environ['nova.context'] = context.RequestContext(user_id, project_id) return self.application def has_authentication(self, req): @@ -133,7 +121,7 @@ class AuthMiddleware(wsgi.Middleware): if delta.days >= 2: self.db.auth_token_destroy(ctxt, token['token_hash']) else: - return self.auth.get_user(token['user_id']) + return token['user_id'] return None def _authorize_user(self, username, key, req): diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 7844d31e1..521525205 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -96,8 +96,8 @@ class CloudPipe(object): def launch_vpn_instance(self, project_id): LOG.debug(_("Launching VPN for %s") % (project_id)) project = self.manager.get_project(project_id) - ctxt = context.RequestContext(user=project.project_manager, - project=project) + ctxt = context.RequestContext(user=project.project_manager_id, + project=project.id) key_name = self.setup_key_pair(ctxt) group_name = self.setup_security_group(ctxt) @@ -112,11 +112,11 @@ class CloudPipe(object): security_group=[group_name]) def setup_security_group(self, context): - group_name = '%s%s' % (context.project.id, FLAGS.vpn_key_suffix) - if db.security_group_exists(context, context.project.id, group_name): + group_name = '%s%s' % (context.project_id, FLAGS.vpn_key_suffix) + if db.security_group_exists(context, context.project_id, group_name): return group_name - group = {'user_id': context.user.id, - 'project_id': context.project.id, + group = {'user_id': context.user_id, + 'project_id': context.project_id, 'name': group_name, 'description': 'Group for vpn'} group_ref = db.security_group_create(context, group) @@ -137,12 +137,12 @@ class CloudPipe(object): return group_name def setup_key_pair(self, context): - key_name = '%s%s' % (context.project.id, FLAGS.vpn_key_suffix) + key_name = '%s%s' % (context.project_id, FLAGS.vpn_key_suffix) try: - result = cloud._gen_key(context, context.user.id, key_name) + result = cloud._gen_key(context, context.user_id, key_name) private_key = result['private_key'] try: - key_dir = os.path.join(FLAGS.keys_path, context.user.id) + key_dir = os.path.join(FLAGS.keys_path, context.user_id) if not os.path.exists(key_dir): os.makedirs(key_dir) key_path = os.path.join(key_dir, '%s.pem' % key_name) diff --git a/nova/compute/api.py b/nova/compute/api.py index 67aa3c20f..51a903d40 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -688,7 +688,7 @@ class API(base.Base): raise instances = None elif project_id or not context.is_admin: - if not context.project: + if not context.project_id: instances = self.db.instance_get_all_by_user( context, context.user_id) else: diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ba03cabbc..6be3f483e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -62,7 +62,7 @@ def is_user_context(context): def authorize_project_context(context, project_id): """Ensures a request has permission to access the given project.""" if is_user_context(context): - if not context.project: + if not context.project_id: raise exception.NotAuthorized() elif context.project_id != project_id: raise exception.NotAuthorized() @@ -71,7 +71,7 @@ def authorize_project_context(context, project_id): def authorize_user_context(context, user_id): """Ensures a request has permission to access the given user.""" if is_user_context(context): - if not context.user: + if not context.user_id: raise exception.NotAuthorized() elif context.user_id != user_id: raise exception.NotAuthorized() diff --git a/nova/image/s3.py b/nova/image/s3.py index 4a3df98ba..dd5c957a5 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -84,9 +84,9 @@ class S3ImageService(service.BaseImageService): def _conn(context): # TODO(vish): is there a better way to get creds to sign # for the user? - access = manager.AuthManager().get_access_key(context.user, - context.project) - secret = str(context.user.secret) + authman = manager.AuthManager() + access = authman.get_access_key(context.user_id, context.project_id) + secret = str(authman.get_user(context.user_id).secret) calling = boto.s3.connection.OrdinaryCallingFormat() return boto.s3.connection.S3Connection(aws_access_key_id=access, aws_secret_access_key=secret, diff --git a/nova/log.py b/nova/log.py index f8c0ba68d..b4f6c1d2e 100644 --- a/nova/log.py +++ b/nova/log.py @@ -43,8 +43,8 @@ from nova import version FLAGS = flags.FLAGS flags.DEFINE_string('logging_context_format_string', '%(asctime)s %(levelname)s %(name)s ' - '[%(request_id)s %(user)s ' - '%(project)s] %(message)s', + '[%(request_id)s %(user_id)s ' + '%(project_id)s] %(message)s', 'format string to use for log messages with context') flags.DEFINE_string('logging_default_format_string', '%(asctime)s %(levelname)s %(name)s [-] ' diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index af3478c7d..25fd2e8c5 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -24,7 +24,6 @@ import webob.dec import nova.api import nova.api.openstack.auth import nova.auth.manager -from nova import auth from nova import context from nova import db from nova import test diff --git a/nova/tests/hyperv_unittest.py b/nova/tests/hyperv_unittest.py index 042819b9c..ab2995923 100644 --- a/nova/tests/hyperv_unittest.py +++ b/nova/tests/hyperv_unittest.py @@ -38,7 +38,7 @@ class HyperVTestCase(test.TestCase): self.user = self.manager.create_user('fake', 'fake', 'fake', admin=True) self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.RequestContext(self.user, self.project) + self.context = context.RequestContext(self.user.id, self.project.id) def test_create_destroy(self): """Create a VM and destroy it""" diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py index daea826fd..ef4ef156c 100644 --- a/nova/tests/scheduler/test_scheduler.py +++ b/nova/tests/scheduler/test_scheduler.py @@ -23,7 +23,6 @@ import datetime import mox import novaclient.exceptions import stubout -import webob from mox import IgnoreArg from nova import context @@ -34,12 +33,10 @@ from nova import service from nova import test from nova import rpc from nova import utils -from nova.auth import manager as auth_manager from nova.scheduler import api from nova.scheduler import manager from nova.scheduler import driver from nova.compute import power_state -from nova.db.sqlalchemy import models FLAGS = flags.FLAGS @@ -250,23 +247,17 @@ class SimpleDriverTestCase(test.TestCase): volume_driver='nova.volume.driver.FakeISCSIDriver', scheduler_driver='nova.scheduler.simple.SimpleScheduler') self.scheduler = manager.SchedulerManager() - self.manager = auth_manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake') - self.project = self.manager.create_project('fake', 'fake', 'fake') self.context = context.get_admin_context() - - def tearDown(self): - self.manager.delete_user(self.user) - self.manager.delete_project(self.project) - super(SimpleDriverTestCase, self).tearDown() + self.user_id = 'fake' + self.project_id = 'fake' def _create_instance(self, **kwargs): """Create a test instance""" inst = {} inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' - inst['user_id'] = self.user.id - inst['project_id'] = self.project.id + 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 diff --git a/nova/tests/test_access.py b/nova/tests/test_access.py index e170ccee6..6069c5d71 100644 --- a/nova/tests/test_access.py +++ b/nova/tests/test_access.py @@ -93,7 +93,7 @@ class AccessTestCase(test.TestCase): super(AccessTestCase, self).tearDown() def response_status(self, user, methodName): - ctxt = context.RequestContext(user, self.project) + ctxt = context.RequestContext(user.id, self.project.id) environ = self._env_for(ctxt, methodName) req = webob.Request.blank('/', environ) resp = req.get_response(self.mw) diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py index 877cf4ea1..f8abe609d 100644 --- a/nova/tests/test_adminapi.py +++ b/nova/tests/test_adminapi.py @@ -54,8 +54,8 @@ class AdminApiTestCase(test.TestCase): self.manager = manager.AuthManager() self.user = self.manager.create_user('admin', 'admin', 'admin', True) self.project = self.manager.create_project('proj', 'admin', 'proj') - self.context = context.RequestContext(user=self.user, - project=self.project) + self.context = context.RequestContext(user_id=self.user.id, + project_id=self.project.id) def fake_show(meh, context, id): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 8cdc73a66..71ac7f473 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -65,8 +65,8 @@ class CloudTestCase(test.TestCase): self.manager = manager.AuthManager() self.user = self.manager.create_user('admin', 'admin', 'admin', True) self.project = self.manager.create_project('proj', 'admin', 'proj') - self.context = context.RequestContext(user=self.user, - project=self.project) + self.context = context.RequestContext(user_id=self.user.id, + project_id=self.project.id) host = self.network.host def fake_show(meh, context, id): @@ -97,7 +97,7 @@ class CloudTestCase(test.TestCase): def _create_key(self, name): # NOTE(vish): create depends on pool, so just call helper directly - return cloud._gen_key(self.context, self.context.user.id, name) + return cloud._gen_key(self.context, self.context.user_id, name) def test_describe_regions(self): """Makes sure describe regions runs without raising an exception""" @@ -936,7 +936,7 @@ class CloudTestCase(test.TestCase): key = RSA.load_key_string(private_key, callback=lambda: None) bio = BIO.MemoryBuffer() public_key = db.key_pair_get(self.context, - self.context.user.id, + self.context.user_id, 'test')['public_key'] key.save_pub_key_bio(bio) converted = crypto.ssl_pub_to_ssh_pub(bio.read()) @@ -960,7 +960,7 @@ class CloudTestCase(test.TestCase): 'mytestfprint') self.assertTrue(result1) keydata = db.key_pair_get(self.context, - self.context.user.id, + self.context.user_id, 'testimportkey1') self.assertEqual('mytestpubkey', keydata['public_key']) self.assertEqual('mytestfprint', keydata['fingerprint']) @@ -977,7 +977,7 @@ class CloudTestCase(test.TestCase): dummypub) self.assertTrue(result2) keydata = db.key_pair_get(self.context, - self.context.user.id, + self.context.user_id, 'testimportkey2') self.assertEqual(dummypub, keydata['public_key']) self.assertEqual(dummyfprint, keydata['fingerprint']) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 6e2ec7ed6..948ca215f 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -429,8 +429,8 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(parameters[1].get('value'), 'fake') def _check_xml_and_container(self, instance): - user_context = context.RequestContext(project=self.project, - user=self.user) + user_context = context.RequestContext(self.user.id, + self.project.id) instance_ref = db.instance_create(user_context, instance) # Re-get the instance so it's bound to an actual session instance_ref = db.instance_get(user_context, instance_ref['id']) @@ -475,8 +475,7 @@ class LibvirtConnTestCase(test.TestCase): def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel, rescue=False): - user_context = context.RequestContext(project=self.project, - user=self.user) + user_context = context.RequestContext(self.user.id, self.project.id) instance_ref = db.instance_create(user_context, instance) network_ref = db.project_get_networks(context.get_admin_context(), self.project.id)[0] @@ -1166,7 +1165,7 @@ class NWFilterTestCase(test.TestCase): self.user = self.manager.create_user('fake', 'fake', 'fake', admin=True) self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.RequestContext(self.user, self.project) + self.context = context.RequestContext(self.user.id, self.project.id) self.fake_libvirt_connection = Mock() diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 69d2deafe..fcb99b7c9 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -25,7 +25,6 @@ from nova import quota from nova import test from nova import utils from nova import volume -from nova.auth import manager from nova.compute import instance_types @@ -48,25 +47,20 @@ class QuotaTestCase(test.TestCase): quota_gigabytes=20, quota_floating_ips=1) - self.manager = manager.AuthManager() - self.user = self.manager.create_user('admin', 'admin', 'admin', True) - self.project = self.manager.create_project('admin', 'admin', 'admin') self.network = self.network = self.start_service('network') - self.context = context.RequestContext(project=self.project, - user=self.user) - - def tearDown(self): - manager.AuthManager().delete_project(self.project) - manager.AuthManager().delete_user(self.user) - super(QuotaTestCase, self).tearDown() + self.user_id = 'admin' + self.project_id = 'admin' + self.context = context.RequestContext(self.user_id, + self.project_id, + True) def _create_instance(self, cores=2): """Create a test instance""" inst = {} inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' - inst['user_id'] = self.user.id - inst['project_id'] = self.project.id + inst['user_id'] = self.user_id + inst['project_id'] = self.project_id inst['instance_type_id'] = '3' # m1.large inst['vcpus'] = cores return db.instance_create(self.context, inst)['id'] @@ -74,8 +68,8 @@ class QuotaTestCase(test.TestCase): def _create_volume(self, size=10): """Create a test volume""" vol = {} - vol['user_id'] = self.user.id - vol['project_id'] = self.project.id + vol['user_id'] = self.user_id + vol['project_id'] = self.project_id vol['size'] = size return db.volume_create(self.context, vol)['id'] @@ -95,15 +89,15 @@ class QuotaTestCase(test.TestCase): num_instances = quota.allowed_instances(self.context, 100, self._get_instance_type('m1.small')) self.assertEqual(num_instances, 2) - db.quota_create(self.context, self.project.id, 'instances', 10) + db.quota_create(self.context, self.project_id, 'instances', 10) num_instances = quota.allowed_instances(self.context, 100, self._get_instance_type('m1.small')) self.assertEqual(num_instances, 4) - db.quota_create(self.context, self.project.id, 'cores', 100) + db.quota_create(self.context, self.project_id, 'cores', 100) num_instances = quota.allowed_instances(self.context, 100, self._get_instance_type('m1.small')) self.assertEqual(num_instances, 10) - db.quota_create(self.context, self.project.id, 'ram', 3 * 2048) + db.quota_create(self.context, self.project_id, 'ram', 3 * 2048) num_instances = quota.allowed_instances(self.context, 100, self._get_instance_type('m1.small')) self.assertEqual(num_instances, 3) @@ -113,13 +107,13 @@ class QuotaTestCase(test.TestCase): num_metadata_items = quota.allowed_metadata_items(self.context, too_many_items) self.assertEqual(num_metadata_items, FLAGS.quota_metadata_items) - db.quota_create(self.context, self.project.id, 'metadata_items', 5) + db.quota_create(self.context, self.project_id, 'metadata_items', 5) num_metadata_items = quota.allowed_metadata_items(self.context, too_many_items) self.assertEqual(num_metadata_items, 5) # Cleanup - db.quota_destroy_all_by_project(self.context, self.project.id) + db.quota_destroy_all_by_project(self.context, self.project_id) def test_unlimited_instances(self): FLAGS.quota_instances = 2 @@ -129,7 +123,7 @@ class QuotaTestCase(test.TestCase): num_instances = quota.allowed_instances(self.context, 100, instance_type) self.assertEqual(num_instances, 2) - db.quota_create(self.context, self.project.id, 'instances', None) + db.quota_create(self.context, self.project_id, 'instances', None) num_instances = quota.allowed_instances(self.context, 100, instance_type) self.assertEqual(num_instances, 100) @@ -145,7 +139,7 @@ class QuotaTestCase(test.TestCase): num_instances = quota.allowed_instances(self.context, 100, instance_type) self.assertEqual(num_instances, 2) - db.quota_create(self.context, self.project.id, 'ram', None) + db.quota_create(self.context, self.project_id, 'ram', None) num_instances = quota.allowed_instances(self.context, 100, instance_type) self.assertEqual(num_instances, 100) @@ -161,7 +155,7 @@ class QuotaTestCase(test.TestCase): num_instances = quota.allowed_instances(self.context, 100, instance_type) self.assertEqual(num_instances, 2) - db.quota_create(self.context, self.project.id, 'cores', None) + db.quota_create(self.context, self.project_id, 'cores', None) num_instances = quota.allowed_instances(self.context, 100, instance_type) self.assertEqual(num_instances, 100) @@ -174,7 +168,7 @@ class QuotaTestCase(test.TestCase): FLAGS.quota_gigabytes = -1 volumes = quota.allowed_volumes(self.context, 100, 1) self.assertEqual(volumes, 10) - db.quota_create(self.context, self.project.id, 'volumes', None) + db.quota_create(self.context, self.project_id, 'volumes', None) volumes = quota.allowed_volumes(self.context, 100, 1) self.assertEqual(volumes, 100) volumes = quota.allowed_volumes(self.context, 101, 1) @@ -185,7 +179,7 @@ class QuotaTestCase(test.TestCase): FLAGS.quota_gigabytes = 10 volumes = quota.allowed_volumes(self.context, 100, 1) self.assertEqual(volumes, 10) - db.quota_create(self.context, self.project.id, 'gigabytes', None) + db.quota_create(self.context, self.project_id, 'gigabytes', None) volumes = quota.allowed_volumes(self.context, 100, 1) self.assertEqual(volumes, 100) volumes = quota.allowed_volumes(self.context, 101, 1) @@ -195,7 +189,7 @@ class QuotaTestCase(test.TestCase): FLAGS.quota_floating_ips = 10 floating_ips = quota.allowed_floating_ips(self.context, 100) self.assertEqual(floating_ips, 10) - db.quota_create(self.context, self.project.id, 'floating_ips', None) + db.quota_create(self.context, self.project_id, 'floating_ips', None) floating_ips = quota.allowed_floating_ips(self.context, 100) self.assertEqual(floating_ips, 100) floating_ips = quota.allowed_floating_ips(self.context, 101) @@ -205,7 +199,7 @@ class QuotaTestCase(test.TestCase): FLAGS.quota_metadata_items = 10 items = quota.allowed_metadata_items(self.context, 100) self.assertEqual(items, 10) - db.quota_create(self.context, self.project.id, 'metadata_items', None) + db.quota_create(self.context, self.project_id, 'metadata_items', None) items = quota.allowed_metadata_items(self.context, 100) self.assertEqual(items, 100) items = quota.allowed_metadata_items(self.context, 101) @@ -274,11 +268,11 @@ class QuotaTestCase(test.TestCase): address = '192.168.0.100' db.floating_ip_create(context.get_admin_context(), {'address': address, 'host': FLAGS.host, - 'project_id': self.project.id}) + 'project_id': self.project_id}) self.assertRaises(quota.QuotaError, self.network.allocate_floating_ip, self.context, - self.project.id) + self.project_id) db.floating_ip_destroy(context.get_admin_context(), address) def test_too_many_metadata_items(self): @@ -300,7 +294,7 @@ class QuotaTestCase(test.TestCase): def test_overridden_allowed_injected_files(self): FLAGS.quota_max_injected_files = 5 - db.quota_create(self.context, self.project.id, 'injected_files', 77) + db.quota_create(self.context, self.project_id, 'injected_files', 77) self.assertEqual(quota.allowed_injected_files(self.context, 100), 77) def test_unlimited_default_allowed_injected_files(self): @@ -309,7 +303,7 @@ class QuotaTestCase(test.TestCase): def test_unlimited_db_allowed_injected_files(self): FLAGS.quota_max_injected_files = 5 - db.quota_create(self.context, self.project.id, 'injected_files', None) + db.quota_create(self.context, self.project_id, 'injected_files', None) self.assertEqual(quota.allowed_injected_files(self.context, 100), 100) def test_default_allowed_injected_file_content_bytes(self): @@ -319,7 +313,7 @@ class QuotaTestCase(test.TestCase): def test_overridden_allowed_injected_file_content_bytes(self): FLAGS.quota_max_injected_file_content_bytes = 12345 - db.quota_create(self.context, self.project.id, + db.quota_create(self.context, self.project_id, 'injected_file_content_bytes', 5678) limit = quota.allowed_injected_file_content_bytes(self.context, 23456) self.assertEqual(limit, 5678) @@ -331,7 +325,7 @@ class QuotaTestCase(test.TestCase): def test_unlimited_db_allowed_injected_file_content_bytes(self): FLAGS.quota_max_injected_file_content_bytes = 12345 - db.quota_create(self.context, self.project.id, + db.quota_create(self.context, self.project_id, 'injected_file_content_bytes', None) limit = quota.allowed_injected_file_content_bytes(self.context, 23456) self.assertEqual(limit, 23456) -- cgit From e1cf345fa82c3a9b8088237f1025c41db0f4e829 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 00:39:53 +0000 Subject: fix a whole bunch of tests --- nova/api/ec2/__init__.py | 20 +++++++--- nova/tests/hyperv_unittest.py | 9 ++--- nova/tests/test_adminapi.py | 16 +++----- nova/tests/test_api.py | 74 ++++------------------------------- nova/tests/test_cloud.py | 16 +++----- nova/tests/test_compute.py | 37 +++++++----------- nova/tests/test_console.py | 19 +++------ nova/tests/test_libvirt.py | 66 ++++++++------------------------ nova/tests/test_objectstore.py | 24 +++--------- nova/tests/test_vmwareapi.py | 15 +++----- nova/tests/test_xenapi.py | 85 +++++++++++++++++++++-------------------- nova/virt/hyperv.py | 6 +-- nova/virt/images.py | 2 +- nova/virt/libvirt/connection.py | 19 ++++----- nova/virt/xenapi/vm_utils.py | 27 +++++++------ nova/virt/xenapi/vmops.py | 17 +++------ 16 files changed, 155 insertions(+), 297 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 8bb2ea944..edae94331 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -138,8 +138,19 @@ class Lockout(wsgi.Middleware): return res -class Authenticate(wsgi.Middleware): +class InjectContext(wsgi.Middleware): + """Always add a fake 'ec2.context' to WSGI environ.""" + def __init__(self, context, *args, **kwargs): + self.context = context + super(InjectContext, self).__init__(*args, **kwargs) + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + req.environ['ec2.context'] = self.context + return self.application + + +class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'ec2.context' to WSGI environ.""" @webob.dec.wsgify(RequestClass=wsgi.Request) @@ -295,16 +306,13 @@ class Authorizer(wsgi.Middleware): def _matches_any_role(self, context, roles): """Return True if any role in roles is allowed in context.""" - authman = manager.AuthManager() - user = authman.get_user(context.user_id) - if user.is_superuser(): + if context.is_admin: return True if 'all' in roles: return True if 'none' in roles: return False - return any(authman.has_role(context.user_id, role, context.project_id) - for role in roles) + return any(role in context.roles for role in roles) class Executor(wsgi.Application): diff --git a/nova/tests/hyperv_unittest.py b/nova/tests/hyperv_unittest.py index ab2995923..0ea196950 100644 --- a/nova/tests/hyperv_unittest.py +++ b/nova/tests/hyperv_unittest.py @@ -23,7 +23,6 @@ from nova import context from nova import db from nova import flags from nova import test -from nova.auth import manager from nova.virt import hyperv FLAGS = flags.FLAGS @@ -34,11 +33,9 @@ class HyperVTestCase(test.TestCase): """Test cases for the Hyper-V driver""" def setUp(self): super(HyperVTestCase, self).setUp() - self.manager = manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake', - admin=True) - self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.RequestContext(self.user.id, self.project.id) + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) def test_create_destroy(self): """Create a VM and destroy it""" diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py index f8abe609d..fde26e31a 100644 --- a/nova/tests/test_adminapi.py +++ b/nova/tests/test_adminapi.py @@ -25,7 +25,6 @@ from nova import log as logging from nova import rpc from nova import test from nova import utils -from nova.auth import manager from nova.api.ec2 import admin from nova.image import fake @@ -51,11 +50,11 @@ class AdminApiTestCase(test.TestCase): self.volume = self.start_service('volume') self.image_service = utils.import_object(FLAGS.image_service) - self.manager = manager.AuthManager() - self.user = self.manager.create_user('admin', 'admin', 'admin', True) - self.project = self.manager.create_project('proj', 'admin', 'proj') - self.context = context.RequestContext(user_id=self.user.id, - project_id=self.project.id) + self.user_id = 'admin' + self.project_id = 'admin' + self.context = context.RequestContext(self.user_id, + self.project_id, + True) def fake_show(meh, context, id): return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, @@ -73,11 +72,6 @@ class AdminApiTestCase(test.TestCase): self.stubs.Set(rpc, 'cast', finish_cast) - def tearDown(self): - self.manager.delete_project(self.project) - self.manager.delete_user(self.user) - super(AdminApiTestCase, self).tearDown() - def test_block_external_ips(self): """Make sure provider firewall rules are created.""" result = self.api.block_external_addresses(self.context, '1.1.1.1/32') diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 26ac5ff24..978e43abd 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -34,7 +34,6 @@ from nova.api import ec2 from nova.api.ec2 import apirequest from nova.api.ec2 import cloud from nova.api.ec2 import ec2utils -from nova.auth import manager class FakeHttplibSocket(object): @@ -192,10 +191,13 @@ class ApiEc2TestCase(test.TestCase): """Unit test for the cloud controller on an EC2 API""" def setUp(self): super(ApiEc2TestCase, self).setUp() - self.manager = manager.AuthManager() self.host = '127.0.0.1' - self.app = ec2.Authenticate(ec2.Requestify(ec2.Executor(), - 'nova.api.ec2.cloud.CloudController')) + # NOTE(vish): skipping the Authorizer + roles = ['sysadmin', 'netadmin'] + ctxt = context.RequestContext('fake', 'fake', roles=roles) + self.app = ec2.InjectContext(ctxt, + ec2.Requestify(ec2.Authorizer(ec2.Executor()), + 'nova.api.ec2.cloud.CloudController')) def expect_http(self, host=None, is_secure=False, api_version=None): """Returns a new EC2 connection""" @@ -242,39 +244,25 @@ class ApiEc2TestCase(test.TestCase): self.expect_http(api_version='2010-10-30') self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake') - project = self.manager.create_project('fake', 'fake', 'fake') - # Any request should be fine self.ec2.get_all_instances() self.assertTrue(self.ec2.APIVersion in self.http.getresponsebody(), 'The version in the xmlns of the response does ' 'not match the API version given in the request.') - self.manager.delete_project(project) - self.manager.delete_user(user) - def test_describe_instances(self): """Test that, after creating a user and a project, the describe instances call to the API works properly""" self.expect_http() self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake') - project = self.manager.create_project('fake', 'fake', 'fake') self.assertEqual(self.ec2.get_all_instances(), []) - self.manager.delete_project(project) - self.manager.delete_user(user) def test_terminate_invalid_instance(self): """Attempt to terminate an invalid instance""" self.expect_http() self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake') - project = self.manager.create_project('fake', 'fake', 'fake') self.assertRaises(EC2ResponseError, self.ec2.terminate_instances, "i-00000005") - self.manager.delete_project(project) - self.manager.delete_user(user) def test_get_all_key_pairs(self): """Test that, after creating a user and project and generating @@ -283,16 +271,12 @@ class ApiEc2TestCase(test.TestCase): self.mox.ReplayAll() keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") \ for x in range(random.randint(4, 8))) - user = self.manager.create_user('fake', 'fake', 'fake') - project = self.manager.create_project('fake', 'fake', 'fake') # NOTE(vish): create depends on pool, so call helper directly - cloud._gen_key(context.get_admin_context(), user.id, keyname) + cloud._gen_key(context.get_admin_context(), 'fake', keyname) rv = self.ec2.get_all_key_pairs() results = [k for k in rv if k.name == keyname] self.assertEquals(len(results), 1) - self.manager.delete_project(project) - self.manager.delete_user(user) def test_create_duplicate_key_pair(self): """Test that, after successfully generating a keypair, @@ -301,8 +285,6 @@ class ApiEc2TestCase(test.TestCase): self.mox.ReplayAll() keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") \ for x in range(random.randint(4, 8))) - user = self.manager.create_user('fake', 'fake', 'fake') - project = self.manager.create_project('fake', 'fake', 'fake') # NOTE(vish): create depends on pool, so call helper directly self.ec2.create_key_pair('test') @@ -321,27 +303,16 @@ class ApiEc2TestCase(test.TestCase): """Test that we can retrieve security groups""" self.expect_http() self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake', admin=True) - project = self.manager.create_project('fake', 'fake', 'fake') rv = self.ec2.get_all_security_groups() self.assertEquals(len(rv), 1) self.assertEquals(rv[0].name, 'default') - self.manager.delete_project(project) - self.manager.delete_user(user) - def test_create_delete_security_group(self): """Test that we can create a security group""" self.expect_http() self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake', admin=True) - project = self.manager.create_project('fake', 'fake', 'fake') - - # At the moment, you need both of these to actually be netadmin - self.manager.add_role('fake', 'netadmin') - project.add_role('fake', 'netadmin') security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8))) @@ -360,9 +331,6 @@ class ApiEc2TestCase(test.TestCase): self.ec2.delete_security_group(security_group_name) - self.manager.delete_project(project) - self.manager.delete_user(user) - def test_authorize_revoke_security_group_cidr(self): """ Test that we can add and remove CIDR based rules @@ -370,12 +338,6 @@ class ApiEc2TestCase(test.TestCase): """ self.expect_http() self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake') - project = self.manager.create_project('fake', 'fake', 'fake') - - # At the moment, you need both of these to actually be netadmin - self.manager.add_role('fake', 'netadmin') - project.add_role('fake', 'netadmin') security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8))) @@ -422,9 +384,6 @@ class ApiEc2TestCase(test.TestCase): self.assertEqual(len(rv), 1) self.assertEqual(rv[0].name, 'default') - self.manager.delete_project(project) - self.manager.delete_user(user) - return def test_authorize_revoke_security_group_cidr_v6(self): @@ -434,12 +393,7 @@ class ApiEc2TestCase(test.TestCase): """ self.expect_http() self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake') - project = self.manager.create_project('fake', 'fake', 'fake') - # At the moment, you need both of these to actually be netadmin - self.manager.add_role('fake', 'netadmin') - project.add_role('fake', 'netadmin') security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8))) @@ -485,9 +439,6 @@ class ApiEc2TestCase(test.TestCase): self.assertEqual(len(rv), 1) self.assertEqual(rv[0].name, 'default') - self.manager.delete_project(project) - self.manager.delete_user(user) - return def test_authorize_revoke_security_group_foreign_group(self): @@ -497,12 +448,6 @@ class ApiEc2TestCase(test.TestCase): """ self.expect_http() self.mox.ReplayAll() - user = self.manager.create_user('fake', 'fake', 'fake', admin=True) - project = self.manager.create_project('fake', 'fake', 'fake') - - # At the moment, you need both of these to actually be netadmin - self.manager.add_role('fake', 'netadmin') - project.add_role('fake', 'netadmin') rand_string = 'sdiuisudfsdcnpaqwertasd' security_group_name = "".join(random.choice(rand_string) @@ -556,8 +501,3 @@ class ApiEc2TestCase(test.TestCase): self.mox.ReplayAll() self.ec2.delete_security_group(security_group_name) - - self.manager.delete_project(project) - self.manager.delete_user(user) - - return diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 71ac7f473..c414e0ddc 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -34,7 +34,6 @@ from nova import network from nova import rpc from nova import test from nova import utils -from nova.auth import manager from nova.api.ec2 import cloud from nova.api.ec2 import ec2utils from nova.image import fake @@ -62,12 +61,11 @@ class CloudTestCase(test.TestCase): self.volume = self.start_service('volume') self.image_service = utils.import_object(FLAGS.image_service) - self.manager = manager.AuthManager() - self.user = self.manager.create_user('admin', 'admin', 'admin', True) - self.project = self.manager.create_project('proj', 'admin', 'proj') - self.context = context.RequestContext(user_id=self.user.id, - project_id=self.project.id) - host = self.network.host + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, + self.project_id, + True) def fake_show(meh, context, id): return {'id': 1, 'container_format': 'ami', @@ -87,12 +85,10 @@ class CloudTestCase(test.TestCase): self.stubs.Set(rpc, 'cast', finish_cast) def tearDown(self): - networks = db.project_get_networks(self.context, self.project.id, + networks = db.project_get_networks(self.context, self.project_id, associate=False) for network in networks: db.network_disassociate(self.context, network['id']) - self.manager.delete_project(self.project) - self.manager.delete_user(self.user) super(CloudTestCase, self).tearDown() def _create_key(self, name): diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 5d59b628a..a1b86276f 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -19,10 +19,6 @@ Tests For Compute """ -import mox -import stubout - -from nova.auth import manager from nova import compute from nova.compute import instance_types from nova.compute import manager as compute_manager @@ -67,10 +63,9 @@ class ComputeTestCase(test.TestCase): network_manager='nova.network.manager.FlatManager') self.compute = utils.import_object(FLAGS.compute_manager) self.compute_api = compute.API() - self.manager = manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake') - self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.RequestContext('fake', 'fake', False) + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) test_notifier.NOTIFICATIONS = [] def fake_show(meh, context, id): @@ -78,19 +73,14 @@ class ComputeTestCase(test.TestCase): self.stubs.Set(nova.image.fake._FakeImageService, 'show', fake_show) - def tearDown(self): - self.manager.delete_user(self.user) - self.manager.delete_project(self.project) - super(ComputeTestCase, self).tearDown() - def _create_instance(self, params={}): """Create a test instance""" inst = {} inst['image_ref'] = 1 inst['reservation_id'] = 'r-fakeres' inst['launch_time'] = '10' - inst['user_id'] = self.user.id - inst['project_id'] = self.project.id + inst['user_id'] = self.user_id + inst['project_id'] = self.project_id type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] inst['instance_type_id'] = type_id inst['ami_launch_index'] = 0 @@ -115,8 +105,8 @@ class ComputeTestCase(test.TestCase): def _create_group(self): values = {'name': 'testgroup', 'description': 'testgroup', - 'user_id': self.user.id, - 'project_id': self.project.id} + 'user_id': self.user_id, + 'project_id': self.project_id} return db.security_group_create(self.context, values) def _get_dummy_instance(self): @@ -350,8 +340,8 @@ class ComputeTestCase(test.TestCase): self.assertEquals(msg['priority'], 'INFO') self.assertEquals(msg['event_type'], 'compute.instance.create') payload = msg['payload'] - self.assertEquals(payload['tenant_id'], self.project.id) - self.assertEquals(payload['user_id'], self.user.id) + self.assertEquals(payload['tenant_id'], self.project_id) + self.assertEquals(payload['user_id'], self.user_id) self.assertEquals(payload['instance_id'], instance_id) self.assertEquals(payload['instance_type'], 'm1.tiny') type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] @@ -374,8 +364,8 @@ class ComputeTestCase(test.TestCase): self.assertEquals(msg['priority'], 'INFO') self.assertEquals(msg['event_type'], 'compute.instance.delete') payload = msg['payload'] - self.assertEquals(payload['tenant_id'], self.project.id) - self.assertEquals(payload['user_id'], self.user.id) + self.assertEquals(payload['tenant_id'], self.project_id) + self.assertEquals(payload['user_id'], self.user_id) self.assertEquals(payload['instance_id'], instance_id) self.assertEquals(payload['instance_type'], 'm1.tiny') type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] @@ -457,8 +447,8 @@ class ComputeTestCase(test.TestCase): self.assertEquals(msg['priority'], 'INFO') self.assertEquals(msg['event_type'], 'compute.instance.resize.prep') payload = msg['payload'] - self.assertEquals(payload['tenant_id'], self.project.id) - self.assertEquals(payload['user_id'], self.user.id) + self.assertEquals(payload['tenant_id'], self.project_id) + self.assertEquals(payload['user_id'], self.user_id) self.assertEquals(payload['instance_id'], instance_id) self.assertEquals(payload['instance_type'], 'm1.tiny') type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] @@ -850,7 +840,6 @@ class ComputeTestCase(test.TestCase): def test_run_kill_vm(self): """Detect when a vm is terminated behind the scenes""" - self.stubs = stubout.StubOutForTesting() self.stubs.Set(compute_manager.ComputeManager, '_report_driver_status', nop_report_driver_status) diff --git a/nova/tests/test_console.py b/nova/tests/test_console.py index 1806cc1ea..cf7f592cf 100644 --- a/nova/tests/test_console.py +++ b/nova/tests/test_console.py @@ -26,10 +26,9 @@ from nova import exception from nova import flags from nova import test from nova import utils -from nova.auth import manager -from nova.console import manager as console_manager FLAGS = flags.FLAGS +flags.DECLARE('console_driver', 'nova.console.manager') class ConsoleTestCase(test.TestCase): @@ -39,17 +38,11 @@ class ConsoleTestCase(test.TestCase): self.flags(console_driver='nova.console.fake.FakeConsoleProxy', stub_compute=True) self.console = utils.import_object(FLAGS.console_manager) - self.manager = manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake') - self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.get_admin_context() + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) self.host = 'test_compute_host' - def tearDown(self): - self.manager.delete_user(self.user) - self.manager.delete_project(self.project) - super(ConsoleTestCase, self).tearDown() - def _create_instance(self): """Create a test instance""" inst = {} @@ -58,8 +51,8 @@ class ConsoleTestCase(test.TestCase): inst['image_id'] = 1 inst['reservation_id'] = 'r-fakeres' inst['launch_time'] = '10' - inst['user_id'] = self.user.id - inst['project_id'] = self.project.id + inst['user_id'] = self.user_id + inst['project_id'] = self.project_id inst['instance_type_id'] = 1 inst['ami_launch_index'] = 0 return db.instance_create(self.context, inst)['id'] diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 948ca215f..61e95c05e 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -32,7 +32,6 @@ from nova import flags from nova import test from nova import utils from nova.api.ec2 import cloud -from nova.auth import manager from nova.compute import power_state from nova.virt.libvirt import connection from nova.virt.libvirt import firewall @@ -150,35 +149,14 @@ class LibvirtConnTestCase(test.TestCase): super(LibvirtConnTestCase, self).setUp() connection._late_load_cheetah() self.flags(fake_call=True) - self.manager = manager.AuthManager() - - try: - pjs = self.manager.get_projects() - pjs = [p for p in pjs if p.name == 'fake'] - if 0 != len(pjs): - self.manager.delete_project(pjs[0]) - - users = self.manager.get_users() - users = [u for u in users if u.name == 'fake'] - if 0 != len(users): - self.manager.delete_user(users[0]) - except Exception, e: - pass - - users = self.manager.get_users() - self.user = self.manager.create_user('fake', 'fake', 'fake', - admin=True) - self.project = self.manager.create_project('fake', 'fake', 'fake') + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) self.network = utils.import_object(FLAGS.network_manager) self.context = context.get_admin_context() FLAGS.instances_path = '' self.call_libvirt_dependant_setup = False - def tearDown(self): - self.manager.delete_project(self.project) - self.manager.delete_user(self.user) - super(LibvirtConnTestCase, self).tearDown() - test_ip = '10.11.12.13' test_instance = {'memory_kb': '1024000', 'basepath': '/some/path', @@ -429,13 +407,13 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(parameters[1].get('value'), 'fake') def _check_xml_and_container(self, instance): - user_context = context.RequestContext(self.user.id, - self.project.id) + user_context = context.RequestContext(self.user_id, + self.project_id) instance_ref = db.instance_create(user_context, instance) # Re-get the instance so it's bound to an actual session instance_ref = db.instance_get(user_context, instance_ref['id']) network_ref = db.project_get_networks(context.get_admin_context(), - self.project.id)[0] + self.project_id)[0] vif = {'address': '56:12:12:12:12:12', 'network_id': network_ref['id'], @@ -475,10 +453,10 @@ class LibvirtConnTestCase(test.TestCase): def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel, rescue=False): - user_context = context.RequestContext(self.user.id, self.project.id) + user_context = context.RequestContext(self.user_id, self.project_id) instance_ref = db.instance_create(user_context, instance) network_ref = db.project_get_networks(context.get_admin_context(), - self.project.id)[0] + self.project_id)[0] _setup_networking(instance_ref['id'], ip=self.test_ip) @@ -759,7 +737,7 @@ class LibvirtConnTestCase(test.TestCase): conn.firewall_driver.setattr('prepare_instance_filter', fake_none) network = db.project_get_networks(context.get_admin_context(), - self.project.id)[0] + self.project_id)[0] ip_dict = {'ip': self.test_ip, 'netmask': network['netmask'], 'enabled': '1'} @@ -814,11 +792,9 @@ class IptablesFirewallTestCase(test.TestCase): def setUp(self): super(IptablesFirewallTestCase, self).setUp() - self.manager = manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake', - admin=True) - self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.RequestContext('fake', 'fake') + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) self.network = utils.import_object(FLAGS.network_manager) class FakeLibvirtConnection(object): @@ -843,11 +819,6 @@ class IptablesFirewallTestCase(test.TestCase): connection.libxml2 = __import__('libxml2') return True - def tearDown(self): - self.manager.delete_project(self.project) - self.manager.delete_user(self.user) - super(IptablesFirewallTestCase, self).tearDown() - in_nat_rules = [ '# Generated by iptables-save v1.4.10 on Sat Feb 19 00:03:19 2011', '*nat', @@ -1161,22 +1132,15 @@ class NWFilterTestCase(test.TestCase): class Mock(object): pass - self.manager = manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake', - admin=True) - self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.RequestContext(self.user.id, self.project.id) + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) self.fake_libvirt_connection = Mock() self.fw = firewall.NWFilterFirewall( lambda: self.fake_libvirt_connection) - def tearDown(self): - self.manager.delete_project(self.project) - self.manager.delete_user(self.user) - super(NWFilterTestCase, self).tearDown() - def test_cidr_rule_nwfilter_xml(self): cloud_controller = cloud.CloudController() cloud_controller.create_security_group(self.context, diff --git a/nova/tests/test_objectstore.py b/nova/tests/test_objectstore.py index 39b4e18d7..af4ee27cd 100644 --- a/nova/tests/test_objectstore.py +++ b/nova/tests/test_objectstore.py @@ -21,8 +21,6 @@ Unittets for S3 objectstore clone. """ import boto -import glob -import hashlib import os import shutil import tempfile @@ -30,12 +28,9 @@ import tempfile from boto import exception as boto_exception from boto.s3 import connection as s3 -from nova import context -from nova import exception from nova import flags from nova import wsgi from nova import test -from nova.auth import manager from nova.objectstore import s3server @@ -61,11 +56,6 @@ class S3APITestCase(test.TestCase): buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'), s3_host='127.0.0.1') - self.auth_manager = manager.AuthManager() - self.admin_user = self.auth_manager.create_user('admin', admin=True) - self.admin_project = self.auth_manager.create_project('admin', - self.admin_user) - shutil.rmtree(FLAGS.buckets_path) os.mkdir(FLAGS.buckets_path) @@ -80,8 +70,8 @@ class S3APITestCase(test.TestCase): boto.config.add_section('Boto') boto.config.set('Boto', 'num_retries', '0') - conn = s3.S3Connection(aws_access_key_id=self.admin_user.access, - aws_secret_access_key=self.admin_user.secret, + conn = s3.S3Connection(aws_access_key_id='fake', + aws_secret_access_key='fake', host=FLAGS.s3_host, port=FLAGS.s3_port, is_secure=False, @@ -104,11 +94,11 @@ class S3APITestCase(test.TestCase): self.assertEquals(buckets[0].name, name, "Wrong name") return True - def test_000_list_buckets(self): + def test_list_buckets(self): """Make sure we are starting with no buckets.""" self._ensure_no_buckets(self.conn.get_all_buckets()) - def test_001_create_and_delete_bucket(self): + def test_create_and_delete_bucket(self): """Test bucket creation and deletion.""" bucket_name = 'testbucket' @@ -117,7 +107,7 @@ class S3APITestCase(test.TestCase): self.conn.delete_bucket(bucket_name) self._ensure_no_buckets(self.conn.get_all_buckets()) - def test_002_create_bucket_and_key_and_delete_key_again(self): + def test_create_bucket_and_key_and_delete_key_again(self): """Test key operations on buckets.""" bucket_name = 'testbucket' key_name = 'somekey' @@ -146,8 +136,6 @@ class S3APITestCase(test.TestCase): bucket_name) def tearDown(self): - """Tear down auth and test server.""" - self.auth_manager.delete_user('admin') - self.auth_manager.delete_project('admin') + """Tear down test server.""" self.server.stop() super(S3APITestCase, self).tearDown() diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py index cbf7801cf..52b5debf5 100644 --- a/nova/tests/test_vmwareapi.py +++ b/nova/tests/test_vmwareapi.py @@ -26,7 +26,6 @@ from nova import db from nova import flags from nova import test from nova import utils -from nova.auth import manager from nova.compute import power_state from nova.tests.glance import stubs as glance_stubs from nova.tests.vmwareapi import db_fakes @@ -48,12 +47,10 @@ class VMWareAPIVMTestCase(test.TestCase): # self.flags(vmwareapi_host_ip='test_url', # vmwareapi_host_username='test_username', # vmwareapi_host_password='test_pass') - # self.manager = manager.AuthManager() - # self.user = self.manager.create_user('fake', 'fake', 'fake', - # admin=True) - # self.project = self.manager.create_project('fake', 'fake', 'fake') # self.network = utils.import_object(FLAGS.network_manager) - # self.stubs = stubout.StubOutForTesting() + # self.user_id = 'fake' + # self.project_id = 'fake' + # self.context = context.RequestContext(self.user_id, self.project_id) # vmwareapi_fake.reset() # db_fakes.stub_out_db_instance_api(self.stubs) # stubs.set_stubs(self.stubs) @@ -64,15 +61,13 @@ class VMWareAPIVMTestCase(test.TestCase): #def tearDown(self): # super(VMWareAPIVMTestCase, self).tearDown() # vmwareapi_fake.cleanup() - # self.manager.delete_project(self.project) - # self.manager.delete_user(self.user) # self.stubs.UnsetAll() def _create_instance_in_the_db(self): values = {'name': 1, 'id': 1, - 'project_id': self.project.id, - 'user_id': self.user.id, + 'project_id': self.project_id, + 'user_id': self.user_id, 'image_id': "1", 'kernel_id': "1", 'ramdisk_id': "1", diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 4cb7447d3..651c7f9e7 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -30,7 +30,6 @@ from nova import flags from nova import log as logging from nova import test from nova import utils -from nova.auth import manager from nova.compute import instance_types from nova.compute import power_state from nova import exception @@ -69,7 +68,9 @@ class XenAPIVolumeTestCase(test.TestCase): def setUp(self): super(XenAPIVolumeTestCase, self).setUp() self.stubs = stubout.StubOutForTesting() - self.context = context.RequestContext('fake', 'fake', False) + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) FLAGS.target_host = '127.0.0.1' FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_password = 'test_pass' @@ -77,7 +78,7 @@ class XenAPIVolumeTestCase(test.TestCase): stubs.stub_out_get_target(self.stubs) xenapi_fake.reset() self.values = {'id': 1, - 'project_id': 'fake', + 'project_id': self.user_id, 'user_id': 'fake', 'image_ref': 1, 'kernel_id': 2, @@ -173,10 +174,6 @@ class XenAPIVMTestCase(test.TestCase): """Unit tests for VM operations.""" def setUp(self): super(XenAPIVMTestCase, self).setUp() - self.manager = manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake', - admin=True) - self.project = self.manager.create_project('fake', 'fake', 'fake') self.network = utils.import_object(FLAGS.network_manager) self.stubs = stubout.StubOutForTesting() self.flags(xenapi_connection_url='test_url', @@ -195,7 +192,9 @@ class XenAPIVMTestCase(test.TestCase): stubs.stub_out_vm_methods(self.stubs) glance_stubs.stubout_glance_client(self.stubs) fake_utils.stub_out_utils_execute(self.stubs) - self.context = context.RequestContext('fake', 'fake', False) + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) self.conn = xenapi_conn.get_connection(False) def test_parallel_builds(self): @@ -229,8 +228,8 @@ class XenAPIVMTestCase(test.TestCase): instance = db.instance_create(self.context, values) self.conn.spawn(instance, network_info) - gt1 = eventlet.spawn(_do_build, 1, self.project.id, self.user.id) - gt2 = eventlet.spawn(_do_build, 2, self.project.id, self.user.id) + gt1 = eventlet.spawn(_do_build, 1, self.project_id, self.user_id) + gt2 = eventlet.spawn(_do_build, 2, self.project_id, self.user_id) gt1.wait() gt2.wait() @@ -399,8 +398,8 @@ class XenAPIVMTestCase(test.TestCase): check_injection=False): stubs.stubout_loopingcall_start(self.stubs) values = {'id': instance_id, - 'project_id': self.project.id, - 'user_id': self.user.id, + 'project_id': self.project_id, + 'user_id': self.user_id, 'image_ref': image_ref, 'kernel_id': kernel_id, 'ramdisk_id': ramdisk_id, @@ -465,12 +464,30 @@ class XenAPIVMTestCase(test.TestCase): self._check_vdis(vdi_recs_start, vdi_recs_end) def test_spawn_raw_objectstore(self): - FLAGS.xenapi_image_service = 'objectstore' - self._test_spawn(1, None, None) + # TODO(vish): deprecated + from nova.auth import manager + authman = manager.AuthManager() + authman.create_user('fake', 'fake') + authman.create_project('fake', 'fake') + try: + FLAGS.xenapi_image_service = 'objectstore' + self._test_spawn(1, None, None) + finally: + authman.delete_project('fake') + authman.delete_user('fake') def test_spawn_objectstore(self): - FLAGS.xenapi_image_service = 'objectstore' - self._test_spawn(1, 2, 3) + # TODO(vish): deprecated + from nova.auth import manager + authman = manager.AuthManager() + authman.create_user('fake', 'fake') + authman.create_project('fake', 'fake') + try: + FLAGS.xenapi_image_service = 'objectstore' + self._test_spawn(1, 2, 3) + finally: + authman.delete_project('fake') + authman.delete_user('fake') @stub_vm_utils_with_vdi_attached_here def test_spawn_raw_glance(self): @@ -599,7 +616,7 @@ class XenAPIVMTestCase(test.TestCase): # guest agent is detected self.assertFalse(self._tee_executed) - @test.skip_test("Never gets an address, not sure why") + @test.skip_test("Key Error on domid") def test_spawn_vlanmanager(self): self.flags(xenapi_image_service='glance', network_manager='nova.network.manager.VlanManager', @@ -609,7 +626,7 @@ class XenAPIVMTestCase(test.TestCase): def dummy(*args, **kwargs): pass - self.stubs.Set(VMOps, 'create_vifs', dummy) + self.stubs.Set(vmops.VMOps, 'create_vifs', dummy) # Reset network table xenapi_fake.reset_table('network') # Instance id = 2 will use vlan network (see db/fakes.py) @@ -623,7 +640,7 @@ class XenAPIVMTestCase(test.TestCase): self.network.set_network_host(ctxt, network['id']) self.network.allocate_for_instance(ctxt, instance_id=instance_ref.id, - instance_type_id=1, project_id=self.project.id) + instance_type_id=1, project_id=self.project_id) self.network.setup_compute_network(ctxt, instance_ref.id) self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, glance_stubs.FakeGlance.IMAGE_KERNEL, @@ -655,21 +672,13 @@ class XenAPIVMTestCase(test.TestCase): # Ensure that it will not unrescue a non-rescued instance. self.assertRaises(Exception, conn.unrescue, instance, None) - def tearDown(self): - super(XenAPIVMTestCase, self).tearDown() - self.manager.delete_project(self.project) - self.manager.delete_user(self.user) - self.vm_info = None - self.vm = None - self.stubs.UnsetAll() - def _create_instance(self, instance_id=1): """Creates and spawns a test instance.""" stubs.stubout_loopingcall_start(self.stubs) values = { 'id': instance_id, - 'project_id': self.project.id, - 'user_id': self.user.id, + 'project_id': self.project_id, + 'user_id': self.user_id, 'image_ref': 1, 'kernel_id': 2, 'ramdisk_id': 3, @@ -750,14 +759,12 @@ class XenAPIMigrateInstance(test.TestCase): stubs.stub_out_get_target(self.stubs) xenapi_fake.reset() xenapi_fake.create_network('fake', FLAGS.flat_network_bridge) - self.manager = manager.AuthManager() - self.user = self.manager.create_user('fake', 'fake', 'fake', - admin=True) - self.project = self.manager.create_project('fake', 'fake', 'fake') - self.context = context.RequestContext('fake', 'fake', False) + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, self.project_id) self.values = {'id': 1, - 'project_id': self.project.id, - 'user_id': self.user.id, + 'project_id': self.project_id, + 'user_id': self.user_id, 'image_ref': 1, 'kernel_id': None, 'ramdisk_id': None, @@ -771,12 +778,6 @@ class XenAPIMigrateInstance(test.TestCase): stubs.stubout_get_this_vm_uuid(self.stubs) glance_stubs.stubout_glance_client(self.stubs) - def tearDown(self): - super(XenAPIMigrateInstance, self).tearDown() - self.manager.delete_project(self.project) - self.manager.delete_user(self.user) - self.stubs.UnsetAll() - def test_migrate_disk_and_power_off(self): instance = db.instance_create(self.context, self.values) stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 5c1dc772d..490c9174b 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -66,7 +66,6 @@ import time from nova import exception from nova import flags from nova import log as logging -from nova.auth import manager from nova.compute import power_state from nova.virt import driver from nova.virt import images @@ -145,13 +144,12 @@ class HyperVConnection(driver.ComputeDriver): if vm is not None: raise exception.InstanceExists(name=instance.name) - user = manager.AuthManager().get_user(instance['user_id']) - project = manager.AuthManager().get_project(instance['project_id']) #Fetch the file, assume it is a VHD file. base_vhd_filename = os.path.join(FLAGS.instances_path, instance.name) vhdfile = "%s.vhd" % (base_vhd_filename) - images.fetch(instance['image_ref'], vhdfile, user, project) + images.fetch(instance['image_ref'], vhdfile, + instance['user_id'], instance['project_id']) try: self._create_vm(instance) diff --git a/nova/virt/images.py b/nova/virt/images.py index 40bf6107c..2e9fca3d6 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -33,7 +33,7 @@ FLAGS = flags.FLAGS LOG = logging.getLogger('nova.virt.images') -def fetch(image_href, path, _user, _project): +def fetch(image_href, path, _user_id, _project_id): # TODO(vish): Improve context handling and add owner and auth data # when it is added to glance. Right now there is no # auth checking in glance, so we assume that access was diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 342dea98f..9c57d43b5 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -757,9 +757,9 @@ class LibvirtConnection(driver.ComputeDriver): else: utils.execute('cp', base, target) - def _fetch_image(self, target, image_id, user, project, size=None): + def _fetch_image(self, target, image_id, user_id, project_id, size=None): """Grab image and optionally attempt to resize it""" - images.fetch(image_id, target, user, project) + images.fetch(image_id, target, user_id, project_id) if size: disk.extend(target, size) @@ -797,9 +797,6 @@ class LibvirtConnection(driver.ComputeDriver): os.close(os.open(basepath('console.log', ''), os.O_CREAT | os.O_WRONLY, 0660)) - user = manager.AuthManager().get_user(inst['user_id']) - project = manager.AuthManager().get_project(inst['project_id']) - if not disk_images: disk_images = {'image_id': inst['image_ref'], 'kernel_id': inst['kernel_id'], @@ -811,16 +808,16 @@ class LibvirtConnection(driver.ComputeDriver): target=basepath('kernel'), fname=fname, image_id=disk_images['kernel_id'], - user=user, - project=project) + user_id=inst['user_id'], + project_id=inst['project_id']) if disk_images['ramdisk_id']: fname = '%08x' % int(disk_images['ramdisk_id']) self._cache_image(fn=self._fetch_image, target=basepath('ramdisk'), fname=fname, image_id=disk_images['ramdisk_id'], - user=user, - project=project) + user_id=inst['user_id'], + project_id=inst['project_id']) root_fname = hashlib.sha1(disk_images['image_id']).hexdigest() size = FLAGS.minimum_root_size @@ -838,8 +835,8 @@ class LibvirtConnection(driver.ComputeDriver): fname=root_fname, cow=FLAGS.use_cow_images, image_id=disk_images['image_id'], - user=user, - project=project, + user_id=inst['user_id'], + project_id=inst['project_id'], size=size) if inst_type['local_gb'] and not self._volume_in_mapping( diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 71107aff4..d146ee2c7 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -37,7 +37,6 @@ import nova.image from nova.image import glance as glance_image_service from nova import log as logging from nova import utils -from nova.auth.manager import AuthManager from nova.compute import instance_types from nova.compute import power_state from nova.virt import disk @@ -406,7 +405,7 @@ class VMHelper(HelperBase): session.wait_for_task(task, instance.id) @classmethod - def fetch_image(cls, session, instance_id, image, user, project, + def fetch_image(cls, session, instance_id, image, user_id, project_id, image_type): """ image_type is interpreted as an ImageType instance @@ -418,18 +417,23 @@ class VMHelper(HelperBase): Returns: A single filename if image_type is KERNEL_RAMDISK A list of dictionaries that describe VDIs, otherwise """ - access = AuthManager().get_access_key(user, project) if FLAGS.xenapi_image_service == 'glance': - return cls._fetch_image_glance(session, instance_id, image, - access, image_type) + return cls._fetch_image_glance(session, instance_id, + image, image_type) else: + # TODO(vish): this shouldn't be used anywhere anymore and + # can probably be removed + from nova.auth.manager import AuthManager + manager = AuthManager() + access = manager.get_access_key(user_id, project_id) + secret = manager.get_user(user_id).secret return cls._fetch_image_objectstore(session, instance_id, image, - access, user.secret, + access, secret, image_type) @classmethod - def _fetch_image_glance_vhd(cls, session, instance_id, image, access, + def _fetch_image_glance_vhd(cls, session, instance_id, image, image_type): """Tell glance to download an image and put the VHDs into the SR @@ -477,7 +481,7 @@ class VMHelper(HelperBase): return vdis @classmethod - def _fetch_image_glance_disk(cls, session, instance_id, image, access, + def _fetch_image_glance_disk(cls, session, instance_id, image, image_type): """Fetch the image from Glance @@ -611,8 +615,7 @@ class VMHelper(HelperBase): return image_type @classmethod - def _fetch_image_glance(cls, session, instance_id, image, access, - image_type): + def _fetch_image_glance(cls, session, instance_id, image, image_type): """Fetch image from glance based on image type. Returns: A single filename if image_type is KERNEL or RAMDISK @@ -620,10 +623,10 @@ class VMHelper(HelperBase): """ if image_type == ImageType.DISK_VHD: return cls._fetch_image_glance_vhd( - session, instance_id, image, access, image_type) + session, instance_id, image, image_type) else: return cls._fetch_image_glance_disk( - session, instance_id, image, access, image_type) + session, instance_id, image, image_type) @classmethod def _fetch_image_objectstore(cls, session, instance_id, image, access, diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 7995576a6..7c6f12ce2 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -38,7 +38,6 @@ from nova import ipv6 from nova import log as logging from nova import utils -from nova.auth.manager import AuthManager from nova.compute import power_state from nova.virt import driver from nova.virt.xenapi.network_utils import NetworkHelper @@ -130,11 +129,10 @@ class VMOps(object): self._session.call_xenapi('VM.start', vm_ref, False, False) def _create_disks(self, instance): - user = AuthManager().get_user(instance.user_id) - project = AuthManager().get_project(instance.project_id) disk_image_type = VMHelper.determine_disk_image_type(instance) vdis = VMHelper.fetch_image(self._session, - instance.id, instance.image_ref, user, project, + instance.id, instance.image_ref, + instance.user_id, instance.project_id, disk_image_type) return vdis @@ -172,21 +170,18 @@ class VMOps(object): power_state.SHUTDOWN) return - user = AuthManager().get_user(instance.user_id) - project = AuthManager().get_project(instance.project_id) - disk_image_type = VMHelper.determine_disk_image_type(instance) kernel = None ramdisk = None try: if instance.kernel_id: kernel = VMHelper.fetch_image(self._session, instance.id, - instance.kernel_id, user, project, - ImageType.KERNEL)[0] + instance.kernel_id, instance.user_id, + instance.project_id, ImageType.KERNEL)[0] if instance.ramdisk_id: ramdisk = VMHelper.fetch_image(self._session, instance.id, - instance.ramdisk_id, user, project, - ImageType.RAMDISK)[0] + instance.kernel_id, instance.user_id, + instance.project_id, ImageType.RAMDISK)[0] # Create the VM ref and attach the first disk first_vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdis[0]['vdi_uuid']) -- cgit From 44d1024a53b8150cf9542d08d5886f430365f161 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 19:47:41 +0000 Subject: fix all tests --- nova/api/ec2/__init__.py | 29 +-- nova/api/openstack/auth.py | 20 ++- nova/compute/api.py | 2 + .../extensions/test_flavors_extra_specs.py | 198 --------------------- nova/tests/api/openstack/fakes.py | 40 +++-- nova/tests/api/openstack/test_auth.py | 37 ++-- nova/tests/api/openstack/test_extensions.py | 29 ++- .../api/openstack/test_flavors_extra_specs.py | 180 +++++++++++++++++++ nova/tests/api/openstack/test_images.py | 27 ++- nova/tests/api/openstack/test_servers.py | 45 +++-- nova/tests/test_access.py | 2 +- nova/tests/test_api.py | 3 +- nova/wsgi.py | 12 ++ 13 files changed, 313 insertions(+), 311 deletions(-) delete mode 100644 nova/tests/api/openstack/extensions/test_flavors_extra_specs.py create mode 100644 nova/tests/api/openstack/test_flavors_extra_specs.py diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index edae94331..0a743075c 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -66,7 +66,7 @@ class RequestLogging(wsgi.Middleware): else: controller = None action = None - ctxt = request.environ.get('ec2.context', None) + ctxt = request.environ.get('nova.context', None) delta = utils.utcnow() - start seconds = delta.seconds microseconds = delta.microseconds @@ -138,20 +138,8 @@ class Lockout(wsgi.Middleware): return res -class InjectContext(wsgi.Middleware): - """Always add a fake 'ec2.context' to WSGI environ.""" - def __init__(self, context, *args, **kwargs): - self.context = context - super(InjectContext, self).__init__(*args, **kwargs) - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - req.environ['ec2.context'] = self.context - return self.application - - class Authenticate(wsgi.Middleware): - """Authenticate an EC2 request and add 'ec2.context' to WSGI environ.""" + """Authenticate an EC2 request and add 'nova.context' to WSGI environ.""" @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): @@ -187,12 +175,13 @@ class Authenticate(wsgi.Middleware): remote_address = req.headers.get('X-Forwarded-For', remote_address) ctxt = context.RequestContext(user_id=user.id, project_id=project.id, + is_admin=user.is_admin(), remote_address=remote_address) - req.environ['ec2.context'] = ctxt + req.environ['nova.context'] = ctxt uname = user.name pname = project.name msg = _('Authenticated Request For %(uname)s:%(pname)s)') % locals() - LOG.audit(msg, context=req.environ['ec2.context']) + LOG.audit(msg, context=req.environ['nova.context']) return self.application @@ -239,7 +228,7 @@ class Authorizer(wsgi.Middleware): """Authorize an EC2 API request. Return a 401 if ec2.controller and ec2.action in WSGI environ may not be - executed in ec2.context. + executed in nova.context. """ def __init__(self, application): @@ -293,7 +282,7 @@ class Authorizer(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - context = req.environ['ec2.context'] + context = req.environ['nova.context'] controller = req.environ['ec2.request'].controller.__class__.__name__ action = req.environ['ec2.request'].action allowed_roles = self.action_roles[controller].get(action, ['none']) @@ -319,14 +308,14 @@ class Executor(wsgi.Application): """Execute an EC2 API request. - Executes 'ec2.action' upon 'ec2.controller', passing 'ec2.context' and + Executes 'ec2.action' upon 'ec2.controller', passing 'nova.context' and 'ec2.action_args' (all variables in WSGI environ.) Returns an XML response, or a 400 upon failure. """ @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - context = req.environ['ec2.context'] + context = req.environ['nova.context'] api_request = req.environ['ec2.request'] result = None try: diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 5b387c081..9caa14a4e 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -58,9 +58,25 @@ class AuthMiddleware(wsgi.Middleware): try: project_id = req.headers["X-Auth-Project-Id"] except KeyError: - project_id = user_id + # FIXME(usrleon): It needed only for compatibility + # while osapi clients don't use this header + projects = self.auth.get_projects(user_id) + if projects: + project_id = projects[0] + else: + return faults.Fault(webob.exc.HTTPUnauthorized()) + + is_admin = self.auth.is_admin(user_id) + req.environ['nova.context'] = context.RequestContext(user_id, + project_id, + is_admin) + if not is_admin and not self.auth.is_project_member(user_id, + project_id): + msg = _("%(user_id)s must be an admin or a " + "member of %(project_id)s") + LOG.warn(msg % locals()) + return faults.Fault(webob.exc.HTTPUnauthorized()) - req.environ['nova.context'] = context.RequestContext(user_id, project_id) return self.application def has_authentication(self, req): diff --git a/nova/compute/api.py b/nova/compute/api.py index 51a903d40..9a1ce7452 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -676,6 +676,7 @@ class API(base.Base): all instances in the system. """ + LOG.info(locals()) if reservation_id is not None: recurse_zones = True instances = self.db.instance_get_all_by_reservation( @@ -688,6 +689,7 @@ class API(base.Base): raise instances = None elif project_id or not context.is_admin: + LOG.info(context.project_id) if not context.project_id: instances = self.db.instance_get_all_by_user( context, context.user_id) diff --git a/nova/tests/api/openstack/extensions/test_flavors_extra_specs.py b/nova/tests/api/openstack/extensions/test_flavors_extra_specs.py deleted file mode 100644 index 2c1c335b0..000000000 --- a/nova/tests/api/openstack/extensions/test_flavors_extra_specs.py +++ /dev/null @@ -1,198 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 University of Southern California -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json -import stubout -import unittest -import webob -import os.path - - -from nova import flags -from nova.api import openstack -from nova.api.openstack import auth -from nova.api.openstack import extensions -from nova.tests.api.openstack import fakes -import nova.wsgi - -FLAGS = flags.FLAGS - - -def return_create_flavor_extra_specs(context, flavor_id, extra_specs): - return stub_flavor_extra_specs() - - -def return_flavor_extra_specs(context, flavor_id): - return stub_flavor_extra_specs() - - -def return_flavor_extra_specs(context, flavor_id): - return stub_flavor_extra_specs() - - -def return_empty_flavor_extra_specs(context, flavor_id): - return {} - - -def delete_flavor_extra_specs(context, flavor_id, key): - pass - - -def stub_flavor_extra_specs(): - specs = { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5"} - return specs - - -class FlavorsExtraSpecsTest(unittest.TestCase): - - def setUp(self): - super(FlavorsExtraSpecsTest, self).setUp() - FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__), - "extensions") - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.auth_data = {} - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_auth(self.stubs) - fakes.stub_out_key_pair_funcs(self.stubs) - self.mware = auth.AuthMiddleware( - extensions.ExtensionMiddleware( - openstack.APIRouterV11())) - - def tearDown(self): - self.stubs.UnsetAll() - super(FlavorsExtraSpecsTest, self).tearDown() - - def test_index(self): - self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', - return_flavor_extra_specs) - request = webob.Request.blank('/flavors/1/os-extra_specs') - res = request.get_response(self.mware) - self.assertEqual(200, res.status_int) - res_dict = json.loads(res.body) - self.assertEqual('application/json', res.headers['Content-Type']) - self.assertEqual('value1', res_dict['extra_specs']['key1']) - - def test_index_no_data(self): - self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', - return_empty_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs') - res = req.get_response(self.mware) - res_dict = json.loads(res.body) - self.assertEqual(200, res.status_int) - self.assertEqual('application/json', res.headers['Content-Type']) - self.assertEqual(0, len(res_dict['extra_specs'])) - - def test_show(self): - self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', - return_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs/key5') - res = req.get_response(self.mware) - self.assertEqual(200, res.status_int) - res_dict = json.loads(res.body) - self.assertEqual('application/json', res.headers['Content-Type']) - self.assertEqual('value5', res_dict['key5']) - - def test_show_spec_not_found(self): - self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', - return_empty_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs/key6') - res = req.get_response(self.mware) - res_dict = json.loads(res.body) - self.assertEqual(404, res.status_int) - - def test_delete(self): - self.stubs.Set(nova.db.api, 'instance_type_extra_specs_delete', - delete_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs/key5') - req.method = 'DELETE' - res = req.get_response(self.mware) - self.assertEqual(200, res.status_int) - - def test_create(self): - self.stubs.Set(nova.db.api, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs') - req.method = 'POST' - req.body = '{"extra_specs": {"key1": "value1"}}' - req.headers["content-type"] = "application/json" - res = req.get_response(self.mware) - res_dict = json.loads(res.body) - self.assertEqual(200, res.status_int) - self.assertEqual('application/json', res.headers['Content-Type']) - self.assertEqual('value1', res_dict['extra_specs']['key1']) - - def test_create_empty_body(self): - self.stubs.Set(nova.db.api, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs') - req.method = 'POST' - req.headers["content-type"] = "application/json" - res = req.get_response(self.mware) - self.assertEqual(400, res.status_int) - - def test_update_item(self): - self.stubs.Set(nova.db.api, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs/key1') - req.method = 'PUT' - req.body = '{"key1": "value1"}' - req.headers["content-type"] = "application/json" - res = req.get_response(self.mware) - self.assertEqual(200, res.status_int) - self.assertEqual('application/json', res.headers['Content-Type']) - res_dict = json.loads(res.body) - self.assertEqual('value1', res_dict['key1']) - - def test_update_item_empty_body(self): - self.stubs.Set(nova.db.api, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs/key1') - req.method = 'PUT' - req.headers["content-type"] = "application/json" - res = req.get_response(self.mware) - self.assertEqual(400, res.status_int) - - def test_update_item_too_many_keys(self): - self.stubs.Set(nova.db.api, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs/key1') - req.method = 'PUT' - req.body = '{"key1": "value1", "key2": "value2"}' - req.headers["content-type"] = "application/json" - res = req.get_response(self.mware) - self.assertEqual(400, res.status_int) - - def test_update_item_body_uri_mismatch(self): - self.stubs.Set(nova.db.api, - 'instance_type_extra_specs_update_or_create', - return_create_flavor_extra_specs) - req = webob.Request.blank('/flavors/1/os-extra_specs/bad') - req.method = 'PUT' - req.body = '{"key1": "value1"}' - req.headers["content-type"] = "application/json" - res = req.get_response(self.mware) - self.assertEqual(400, res.status_int) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 26b1de818..28969d5f8 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -29,6 +29,7 @@ from glance.common import exception as glance_exc from nova import context from nova import exception as exc from nova import utils +from nova import wsgi import nova.api.openstack.auth from nova.api import openstack from nova.api.openstack import auth @@ -40,14 +41,13 @@ import nova.image.fake from nova.image import glance from nova.image import service from nova.tests import fake_flags -from nova.wsgi import Router class Context(object): pass -class FakeRouter(Router): +class FakeRouter(wsgi.Router): def __init__(self): pass @@ -68,21 +68,30 @@ def fake_auth_init(self, application): @webob.dec.wsgify def fake_wsgi(self, req): - req.environ['nova.context'] = context.RequestContext(1, 1) return self.application -def wsgi_app(inner_app10=None, inner_app11=None): +def wsgi_app(inner_app10=None, inner_app11=None, fake_auth=True): if not inner_app10: inner_app10 = openstack.APIRouterV10() if not inner_app11: inner_app11 = openstack.APIRouterV11() - mapper = urlmap.URLMap() - api10 = openstack.FaultWrapper(auth.AuthMiddleware( + + if fake_auth: + ctxt = context.RequestContext('fake', 'fake') + api10 = openstack.FaultWrapper(wsgi.InjectContext(ctxt, + limits.RateLimitingMiddleware(inner_app10))) + api11 = openstack.FaultWrapper(wsgi.InjectContext(ctxt, + limits.RateLimitingMiddleware( + extensions.ExtensionMiddleware(inner_app11)))) + else: + api10 = openstack.FaultWrapper(auth.AuthMiddleware( limits.RateLimitingMiddleware(inner_app10))) - api11 = openstack.FaultWrapper(auth.AuthMiddleware( + api11 = openstack.FaultWrapper(auth.AuthMiddleware( limits.RateLimitingMiddleware( extensions.ExtensionMiddleware(inner_app11)))) + Auth = auth + mapper = urlmap.URLMap() mapper['/v1.0'] = api10 mapper['/v1.1'] = api11 mapper['/'] = openstack.FaultWrapper(versions.Versions()) @@ -359,17 +368,18 @@ class FakeAuthManager(object): if admin is not None: user.admin = admin - def is_admin(self, user): + def is_admin(self, user_id): + user = self.get_user(user_id) return user.admin - def is_project_member(self, user, project): + def is_project_member(self, user_id, project): if not isinstance(project, Project): try: project = self.get_project(project) except exc.NotFound: raise webob.exc.HTTPUnauthorized() - return ((user.id in project.member_ids) or - (user.id == project.project_manager_id)) + return ((user_id in project.member_ids) or + (user_id == project.project_manager_id)) def create_project(self, name, manager_user, description=None, member_users=None): @@ -396,13 +406,13 @@ class FakeAuthManager(object): else: raise exc.NotFound - def get_projects(self, user=None): - if not user: + def get_projects(self, user_id=None): + if not user_id: return FakeAuthManager.projects.values() else: return [p for p in FakeAuthManager.projects.values() - if (user.id in p.member_ids) or - (user.id == p.project_manager_id)] + if (user_id in p.member_ids) or + (user_id == p.project_manager_id)] class FakeRateLimiter(object): diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 25fd2e8c5..1b1d44368 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -34,7 +34,6 @@ class Test(test.TestCase): def setUp(self): super(Test, self).setUp() - self.stubs = stubout.StubOutForTesting() self.stubs.Set(nova.api.openstack.auth.AuthMiddleware, '__init__', fakes.fake_auth_init) self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext) @@ -56,7 +55,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'user1' req.headers['X-Auth-Key'] = 'user1_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '204 No Content') self.assertEqual(len(result.headers['X-Auth-Token']), 40) self.assertEqual(result.headers['X-CDN-Management-Url'], @@ -72,7 +71,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'}) req.headers['X-Auth-User'] = 'user1' req.headers['X-Auth-Key'] = 'user1_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '204 No Content') self.assertEqual(len(result.headers['X-Auth-Token']), 40) self.assertEqual(result.headers['X-Server-Management-Url'], @@ -85,7 +84,7 @@ class Test(test.TestCase): self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter) req = webob.Request.blank('/v1.0/fake') req.headers['X-Auth-Token'] = token - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '200 OK') self.assertEqual(result.headers['X-Test-Success'], 'True') @@ -109,7 +108,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-Token'] = 'token_hash' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') self.assertEqual(self.destroy_called, True) @@ -123,7 +122,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'}) req.headers['X-Auth-User'] = 'user1' req.headers['X-Auth-Key'] = 'user1_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '204 No Content') token = result.headers['X-Auth-Token'] @@ -131,7 +130,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/fake') req.headers['X-Auth-Token'] = token req.headers['X-Auth-Project-Id'] = 'user2_project' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '200 OK') self.assertEqual(result.headers['X-Test-Success'], 'True') @@ -139,7 +138,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'unknown_user' req.headers['X-Auth-Key'] = 'unknown_user_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') def test_bad_user_good_key(self): @@ -150,18 +149,18 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'unknown_user' req.headers['X-Auth-Key'] = 'user1_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') def test_no_user(self): req = webob.Request.blank('/v1.0/') - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') def test_bad_token(self): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-Token'] = 'unknown_token' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') def test_bad_project(self): @@ -176,7 +175,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'}) req.headers['X-Auth-User'] = 'user1' req.headers['X-Auth-Key'] = 'user1_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '204 No Content') token = result.headers['X-Auth-Token'] @@ -184,7 +183,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/fake') req.headers['X-Auth-Token'] = token req.headers['X-Auth-Project-Id'] = 'user2_project' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') def test_not_existing_project(self): @@ -196,7 +195,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'}) req.headers['X-Auth-User'] = 'user1' req.headers['X-Auth-Key'] = 'user1_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '204 No Content') token = result.headers['X-Auth-Token'] @@ -204,7 +203,7 @@ class Test(test.TestCase): req = webob.Request.blank('/v1.0/fake') req.headers['X-Auth-Token'] = token req.headers['X-Auth-Project-Id'] = 'unknown_project' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') @@ -225,13 +224,13 @@ class TestFunctional(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-Token'] = 'test_token_hash' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') def test_token_doesnotexist(self): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-Token'] = 'nonexistant_token_hash' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '401 Unauthorized') @@ -260,7 +259,7 @@ class TestLimiter(test.TestCase): req = webob.Request.blank('/v1.0/') req.headers['X-Auth-User'] = 'user1' req.headers['X-Auth-Key'] = 'user1_key' - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(len(result.headers['X-Auth-Token']), 40) token = result.headers['X-Auth-Token'] @@ -268,6 +267,6 @@ class TestLimiter(test.TestCase): req = webob.Request.blank('/v1.0/fake') req.method = 'POST' req.headers['X-Auth-Token'] = token - result = req.get_response(fakes.wsgi_app()) + result = req.get_response(fakes.wsgi_app(fake_auth=False)) self.assertEqual(result.status, '200 OK') self.assertEqual(result.headers['X-Test-Success'], 'True') diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index 697c62e5c..0a5603fc3 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -16,13 +16,12 @@ # under the License. import json -import stubout -import unittest import webob import os.path from nova import context from nova import flags +from nova import test from nova.api import openstack from nova.api.openstack import extensions from nova.api.openstack import flavors @@ -78,7 +77,7 @@ class StubExtensionManager(object): return request_extensions -class ExtensionControllerTest(unittest.TestCase): +class ExtensionControllerTest(test.TestCase): def test_index(self): app = openstack.APIRouterV11() @@ -95,7 +94,7 @@ class ExtensionControllerTest(unittest.TestCase): self.assertEqual(200, response.status_int) -class ResourceExtensionTest(unittest.TestCase): +class ResourceExtensionTest(test.TestCase): def test_no_extension_present(self): manager = StubExtensionManager(None) @@ -133,13 +132,14 @@ class InvalidExtension(object): return "THIRD" -class ExtensionManagerTest(unittest.TestCase): +class ExtensionManagerTest(test.TestCase): response_body = "Try to say this Mr. Knox, sir..." def setUp(self): - FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__), - "extensions") + super(ExtensionManagerTest, self).setUp() + ext_path = os.path.join(os.path.dirname(__file__), "extensions") + self.flags(osapi_extensions_path=ext_path) def test_get_resources(self): app = openstack.APIRouterV11() @@ -158,11 +158,12 @@ class ExtensionManagerTest(unittest.TestCase): self.assertTrue('THIRD' not in ext_mgr.extensions) -class ActionExtensionTest(unittest.TestCase): +class ActionExtensionTest(test.TestCase): def setUp(self): - FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__), - "extensions") + super(ActionExtensionTest, self).setUp() + ext_path = os.path.join(os.path.dirname(__file__), "extensions") + self.flags(osapi_extensions_path=ext_path) def _send_server_action_request(self, url, body): app = openstack.APIRouterV11() @@ -196,20 +197,14 @@ class ActionExtensionTest(unittest.TestCase): self.assertEqual(404, response.status_int) -class RequestExtensionTest(unittest.TestCase): +class RequestExtensionTest(test.TestCase): def setUp(self): super(RequestExtensionTest, self).setUp() - self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.reset_fake_data() fakes.FakeAuthDatabase.data = {} - fakes.stub_out_auth(self.stubs) self.context = context.get_admin_context() - def tearDown(self): - self.stubs.UnsetAll() - super(RequestExtensionTest, self).tearDown() - def test_get_resources_with_stub_mgr(self): def _req_handler(req, res): diff --git a/nova/tests/api/openstack/test_flavors_extra_specs.py b/nova/tests/api/openstack/test_flavors_extra_specs.py new file mode 100644 index 000000000..d386958db --- /dev/null +++ b/nova/tests/api/openstack/test_flavors_extra_specs.py @@ -0,0 +1,180 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 University of Southern California +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import stubout +import webob +import os.path + + +from nova import flags +from nova import test +from nova.api import openstack +from nova.api.openstack import extensions +from nova.tests.api.openstack import fakes +import nova.wsgi + +FLAGS = flags.FLAGS + + +def return_create_flavor_extra_specs(context, flavor_id, extra_specs): + return stub_flavor_extra_specs() + + +def return_flavor_extra_specs(context, flavor_id): + return stub_flavor_extra_specs() + + +def return_empty_flavor_extra_specs(context, flavor_id): + return {} + + +def delete_flavor_extra_specs(context, flavor_id, key): + pass + + +def stub_flavor_extra_specs(): + specs = { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5"} + return specs + + +class FlavorsExtraSpecsTest(test.TestCase): + + def setUp(self): + super(FlavorsExtraSpecsTest, self).setUp() + fakes.stub_out_key_pair_funcs(self.stubs) + + def test_index(self): + self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', + return_flavor_extra_specs) + request = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + res = request.get_response(fakes.wsgi_app()) + self.assertEqual(200, res.status_int) + res_dict = json.loads(res.body) + self.assertEqual('application/json', res.headers['Content-Type']) + self.assertEqual('value1', res_dict['extra_specs']['key1']) + + def test_index_no_data(self): + self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', + return_empty_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(200, res.status_int) + self.assertEqual('application/json', res.headers['Content-Type']) + self.assertEqual(0, len(res_dict['extra_specs'])) + + def test_show(self): + self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', + return_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key5') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(200, res.status_int) + res_dict = json.loads(res.body) + self.assertEqual('application/json', res.headers['Content-Type']) + self.assertEqual('value5', res_dict['key5']) + + def test_show_spec_not_found(self): + self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get', + return_empty_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key6') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(404, res.status_int) + + def test_delete(self): + self.stubs.Set(nova.db.api, 'instance_type_extra_specs_delete', + delete_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key5') + req.method = 'DELETE' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(200, res.status_int) + + def test_create(self): + self.stubs.Set(nova.db.api, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + req.method = 'POST' + req.body = '{"extra_specs": {"key1": "value1"}}' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(200, res.status_int) + self.assertEqual('application/json', res.headers['Content-Type']) + self.assertEqual('value1', res_dict['extra_specs']['key1']) + + def test_create_empty_body(self): + self.stubs.Set(nova.db.api, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs') + req.method = 'POST' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, res.status_int) + + def test_update_item(self): + self.stubs.Set(nova.db.api, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1') + req.method = 'PUT' + req.body = '{"key1": "value1"}' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(200, res.status_int) + self.assertEqual('application/json', res.headers['Content-Type']) + res_dict = json.loads(res.body) + self.assertEqual('value1', res_dict['key1']) + + def test_update_item_empty_body(self): + self.stubs.Set(nova.db.api, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1') + req.method = 'PUT' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, res.status_int) + + def test_update_item_too_many_keys(self): + self.stubs.Set(nova.db.api, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1') + req.method = 'PUT' + req.body = '{"key1": "value1", "key2": "value2"}' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, res.status_int) + + def test_update_item_body_uri_mismatch(self): + self.stubs.Set(nova.db.api, + 'instance_type_extra_specs_update_or_create', + return_create_flavor_extra_specs) + req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/bad') + req.method = 'PUT' + req.body = '{"key1": "value1"}' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, res.status_int) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 17f2fb755..3f42bc1db 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -155,7 +155,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests): fakes.stub_out_compute_api_snapshot(self.stubs) service_class = 'nova.image.glance.GlanceImageService' self.service = utils.import_object(service_class) - self.context = context.RequestContext(1, None) + self.context = context.RequestContext('fake', 'fake') self.service.delete_all() self.sent_to_glance = {} fakes.stub_out_glance_add_image(self.stubs, self.sent_to_glance) @@ -168,7 +168,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests): """Ensure instance_id is persisted as an image-property""" fixture = {'name': 'test image', 'is_public': False, - 'properties': {'instance_id': '42', 'user_id': '1'}} + 'properties': {'instance_id': '42', 'user_id': 'fake'}} image_id = self.service.create(self.context, fixture)['id'] expected = fixture @@ -178,7 +178,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests): expected = {'id': image_id, 'name': 'test image', 'is_public': False, - 'properties': {'instance_id': '42', 'user_id': '1'}} + 'properties': {'instance_id': '42', 'user_id': 'fake'}} self.assertDictMatch(image_meta, expected) image_metas = self.service.detail(self.context) @@ -331,11 +331,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.orig_image_service = FLAGS.image_service FLAGS.image_service = 'nova.image.glance.GlanceImageService' self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) fakes.stub_out_key_pair_funcs(self.stubs) self.fixtures = self._make_image_fixtures() fakes.stub_out_glance(self.stubs, initial_fixtures=self.fixtures) @@ -352,7 +349,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): """Determine if this fixture is applicable for given user id.""" is_public = fixture["is_public"] try: - uid = int(fixture["properties"]["user_id"]) + uid = fixture["properties"]["user_id"] except KeyError: uid = None return uid == user_id or is_public @@ -424,7 +421,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }, "metadata": { "instance_ref": "http://localhost/v1.1/servers/42", - "user_id": "1", + "user_id": "fake", }, "links": [{ "rel": "self", @@ -559,7 +556,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): fixtures = copy.copy(self.fixtures) for image in fixtures: - if not self._applicable_fixture(image, 1): + if not self._applicable_fixture(image, "fake"): fixtures.remove(image) continue @@ -666,7 +663,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'name': 'queued snapshot', 'metadata': { u'instance_ref': u'http://localhost/v1.1/servers/42', - u'user_id': u'1', + u'user_id': u'fake', }, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, @@ -696,7 +693,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'name': 'saving snapshot', 'metadata': { u'instance_ref': u'http://localhost/v1.1/servers/42', - u'user_id': u'1', + u'user_id': u'fake', }, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, @@ -727,7 +724,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'name': 'active snapshot', 'metadata': { u'instance_ref': u'http://localhost/v1.1/servers/42', - u'user_id': u'1', + u'user_id': u'fake', }, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, @@ -757,7 +754,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'name': 'killed snapshot', 'metadata': { u'instance_ref': u'http://localhost/v1.1/servers/42', - u'user_id': u'1', + u'user_id': u'fake', }, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, @@ -1259,7 +1256,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): # Snapshot for User 1 server_ref = 'http://localhost/v1.1/servers/42' - snapshot_properties = {'instance_ref': server_ref, 'user_id': '1'} + snapshot_properties = {'instance_ref': server_ref, 'user_id': 'fake'} for status in ('queued', 'saving', 'active', 'killed'): add_fixture(id=image_id, name='%s snapshot' % status, is_public=False, status=status, @@ -1267,7 +1264,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): image_id += 1 # Snapshot for User 2 - other_snapshot_properties = {'instance_id': '43', 'user_id': '2'} + other_snapshot_properties = {'instance_id': '43', 'user_id': 'other'} add_fixture(id=image_id, name='someone elses snapshot', is_public=False, status='active', properties=other_snapshot_properties) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 3fc38b73c..e0456781a 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -96,8 +96,8 @@ def return_server_with_power_state(power_state): return _return_server -def return_servers(context, user_id=1): - return [stub_instance(i, user_id) for i in xrange(5)] +def return_servers(context, *args, **kwargs): + return [stub_instance(i, 'fake', 'fake') for i in xrange(5)] def return_servers_by_reservation(context, reservation_id=""): @@ -140,9 +140,9 @@ def instance_addresses(context, instance_id): return None -def stub_instance(id, user_id=1, private_address=None, public_addresses=None, - host=None, power_state=0, reservation_id="", - uuid=FAKE_UUID, interfaces=None): +def stub_instance(id, user_id='fake', project_id='fake', private_address=None, + public_addresses=None, host=None, power_state=0, + reservation_id="", uuid=FAKE_UUID, interfaces=None): metadata = [] metadata.append(InstanceMetadata(key='seq', value=id)) @@ -166,7 +166,7 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None, "id": int(id), "admin_pass": "", "user_id": user_id, - "project_id": "", + "project_id": project_id, "image_ref": "10", "kernel_id": "", "ramdisk_id": "", @@ -225,11 +225,9 @@ class ServersTest(test.TestCase): def setUp(self): super(ServersTest, self).setUp() self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() fakes.FakeAuthDatabase.data = {} fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) fakes.stub_out_key_pair_funcs(self.stubs) fakes.stub_out_image_service(self.stubs) self.stubs.Set(utils, 'gen_uuid', fake_gen_uuid) @@ -237,7 +235,7 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.db.api, 'instance_get', return_server_by_id) self.stubs.Set(nova.db, 'instance_get_by_uuid', return_server_by_uuid) - self.stubs.Set(nova.db.api, 'instance_get_all_by_user', + self.stubs.Set(nova.db.api, 'instance_get_all_by_project', return_servers) self.stubs.Set(nova.db.api, 'instance_add_security_group', return_security_group) @@ -636,6 +634,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) + self.assertEqual(len(res_dict['servers']), 5) i = 0 for s in res_dict['servers']: self.assertEqual(s['id'], i) @@ -699,23 +698,24 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) + self.assertEqual(len(res_dict['servers']), 5) for i, s in enumerate(res_dict['servers']): self.assertEqual(s['id'], i) self.assertEqual(s['name'], 'server%d' % i) self.assertEqual(s.get('imageId', None), None) expected_links = [ - { - "rel": "self", - "href": "http://localhost/v1.1/servers/%d" % (i,), - }, - { - "rel": "bookmark", - "href": "http://localhost/servers/%d" % (i,), - }, - ] + { + "rel": "self", + "href": "http://localhost/v1.1/servers/%d" % (i,), + }, + { + "rel": "bookmark", + "href": "http://localhost/servers/%d" % (i,), + }, + ] - self.assertEqual(s['links'], expected_links) + self.assertEqual(s['links'], expected_links) def test_get_servers_with_limit(self): req = webob.Request.blank('/v1.0/servers?limit=3') @@ -1282,10 +1282,10 @@ class ServersTest(test.TestCase): instances - 2 on one host and 3 on another. ''' - def return_servers_with_host(context, user_id=1): - return [stub_instance(i, 1, None, None, i % 2) for i in xrange(5)] + def return_servers_with_host(context, *args, **kwargs): + return [stub_instance(i, 'fake', 'fake', None, None, i % 2) for i in xrange(5)] - self.stubs.Set(nova.db.api, 'instance_get_all_by_user', + self.stubs.Set(nova.db.api, 'instance_get_all_by_project', return_servers_with_host) req = webob.Request.blank('/v1.0/servers/detail') @@ -2161,7 +2161,6 @@ class TestServerInstanceCreation(test.TestCase): def setUp(self): super(TestServerInstanceCreation, 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) diff --git a/nova/tests/test_access.py b/nova/tests/test_access.py index 6069c5d71..39558b1cf 100644 --- a/nova/tests/test_access.py +++ b/nova/tests/test_access.py @@ -41,7 +41,7 @@ class FakeApiRequest(object): class AccessTestCase(test.TestCase): def _env_for(self, ctxt, action): env = {} - env['ec2.context'] = ctxt + env['nova.context'] = ctxt env['ec2.request'] = FakeApiRequest(action) return env diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 978e43abd..292f9d668 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -30,6 +30,7 @@ import webob from nova import context from nova import exception from nova import test +from nova import wsgi from nova.api import ec2 from nova.api.ec2 import apirequest from nova.api.ec2 import cloud @@ -195,7 +196,7 @@ class ApiEc2TestCase(test.TestCase): # NOTE(vish): skipping the Authorizer roles = ['sysadmin', 'netadmin'] ctxt = context.RequestContext('fake', 'fake', roles=roles) - self.app = ec2.InjectContext(ctxt, + self.app = wsgi.InjectContext(ctxt, ec2.Requestify(ec2.Authorizer(ec2.Executor()), 'nova.api.ec2.cloud.CloudController')) diff --git a/nova/wsgi.py b/nova/wsgi.py index eae3afcb4..c8ddb97d7 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -274,6 +274,18 @@ class Middleware(Application): return self.process_response(response) +class InjectContext(Middleware): + """Add a 'nova.context' to WSGI environ.""" + def __init__(self, context, *args, **kwargs): + self.context = context + super(InjectContext, self).__init__(*args, **kwargs) + + @webob.dec.wsgify(RequestClass=Request) + def __call__(self, req): + req.environ['nova.context'] = self.context + return self.application + + class Debug(Middleware): """Helper class for debugging a WSGI application. -- cgit From f9fb313ca2acaf2fd491d5b695381846969b132c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 19:54:11 +0000 Subject: fix extensions tests --- nova/tests/api/openstack/test_extensions.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index 0a5603fc3..0e709d3af 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -79,6 +79,11 @@ class StubExtensionManager(object): class ExtensionControllerTest(test.TestCase): + def setUp(self): + super(ExtensionControllerTest, self).setUp() + ext_path = os.path.join(os.path.dirname(__file__), "extensions") + self.flags(osapi_extensions_path=ext_path) + def test_index(self): app = openstack.APIRouterV11() ext_midware = extensions.ExtensionMiddleware(app) @@ -96,6 +101,11 @@ class ExtensionControllerTest(test.TestCase): class ResourceExtensionTest(test.TestCase): + def setUp(self): + super(ResourceExtensionTest, self).setUp() + ext_path = os.path.join(os.path.dirname(__file__), "extensions") + self.flags(osapi_extensions_path=ext_path) + def test_no_extension_present(self): manager = StubExtensionManager(None) app = openstack.APIRouterV11() @@ -201,9 +211,8 @@ class RequestExtensionTest(test.TestCase): def setUp(self): super(RequestExtensionTest, self).setUp() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} - self.context = context.get_admin_context() + ext_path = os.path.join(os.path.dirname(__file__), "extensions") + self.flags(osapi_extensions_path=ext_path) def test_get_resources_with_stub_mgr(self): -- cgit From 0f8eee7ff32a91c866742939b1f551f3610f1276 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 20:20:31 +0000 Subject: fix auth tests --- nova/api/openstack/auth.py | 2 +- nova/auth/manager.py | 2 +- nova/tests/test_auth.py | 19 +++++++++---------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 9caa14a4e..d42abe1f8 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -62,7 +62,7 @@ class AuthMiddleware(wsgi.Middleware): # while osapi clients don't use this header projects = self.auth.get_projects(user_id) if projects: - project_id = projects[0] + project_id = projects[0].id else: return faults.Fault(webob.exc.HTTPUnauthorized()) diff --git a/nova/auth/manager.py b/nova/auth/manager.py index b6131fb7f..06af7e781 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -785,7 +785,7 @@ class AuthManager(object): return read_buffer def get_environment_rc(self, user, project=None, use_dmz=True): - """Get credential zip for user in project""" + """Get environment rc for user in project""" if not isinstance(user, User): user = self.get_user(user) if project is None: diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py index 71e0d17c9..7c0f783bb 100644 --- a/nova/tests/test_auth.py +++ b/nova/tests/test_auth.py @@ -102,7 +102,7 @@ class _AuthManagerBaseTestCase(test.TestCase): self.assertEqual('classified', u.secret) self.assertEqual('private-party', u.access) - def test_004_signature_is_valid(self): + def test_signature_is_valid(self): with user_generator(self.manager, name='admin', secret='admin', access='admin'): with project_generator(self.manager, name="admin", @@ -141,15 +141,14 @@ class _AuthManagerBaseTestCase(test.TestCase): '127.0.0.1', '/services/Cloud')) - def test_005_can_get_credentials(self): - return - credentials = self.manager.get_user('test1').get_credentials() - self.assertEqual(credentials, - 'export EC2_ACCESS_KEY="access"\n' + - 'export EC2_SECRET_KEY="secret"\n' + - 'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' + - 'export S3_URL="http://127.0.0.1:3333/"\n' + - 'export EC2_USER_ID="test1"\n') + def test_can_get_credentials(self): + st = {'access': 'access', 'secret': 'secret'} + with user_and_project_generator(self.manager, user_state=st) as (u, p): + credentials = self.manager.get_environment_rc(u, p) + LOG.debug(credentials) + self.assertTrue('export EC2_ACCESS_KEY="access:testproj"\n' + in credentials) + self.assertTrue('export EC2_SECRET_KEY="secret"\n' in credentials) def test_can_list_users(self): with user_generator(self.manager): -- cgit From e8defa6bdd5af85486d0d3acce8956670ca16882 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 20:41:46 +0000 Subject: fix test_access --- nova/api/ec2/__init__.py | 7 ++++++- nova/auth/manager.py | 9 +++++++++ nova/tests/test_access.py | 19 +++++++++---------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 0a743075c..1ea26fdeb 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -156,8 +156,9 @@ class Authenticate(wsgi.Middleware): auth_params.pop('Signature') # Authenticate the request. + authman = manager.AuthManager() try: - (user, project) = manager.AuthManager().authenticate( + (user, project) = authman.authenticate( access, signature, auth_params, @@ -173,9 +174,12 @@ class Authenticate(wsgi.Middleware): remote_address = req.remote_addr if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) + roles = authman.get_active_roles(user, project) + LOG.warn(roles) ctxt = context.RequestContext(user_id=user.id, project_id=project.id, is_admin=user.is_admin(), + roles=roles, remote_address=remote_address) req.environ['nova.context'] = ctxt uname = user.name @@ -295,6 +299,7 @@ class Authorizer(wsgi.Middleware): def _matches_any_role(self, context, roles): """Return True if any role in roles is allowed in context.""" + LOG.info(context.roles) if context.is_admin: return True if 'all' in roles: diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 06af7e781..7f99d9016 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -518,6 +518,15 @@ class AuthManager(object): return drv.get_user_roles(User.safe_id(user), Project.safe_id(project)) + def get_active_roles(self, user, project=None): + """Get all active roles for context""" + if project: + roles = FLAGS.allowed_roles + roles.append('projectmanager') + else: + roles = FLAGS.global_roles + return [role for role in roles if self.has_role(user, role, project)] + def get_project(self, pid): """Get project object by id""" with self.driver() as drv: diff --git a/nova/tests/test_access.py b/nova/tests/test_access.py index 39558b1cf..3b54fc249 100644 --- a/nova/tests/test_access.py +++ b/nova/tests/test_access.py @@ -16,7 +16,6 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest import webob from nova import context @@ -93,7 +92,11 @@ class AccessTestCase(test.TestCase): super(AccessTestCase, self).tearDown() def response_status(self, user, methodName): - ctxt = context.RequestContext(user.id, self.project.id) + roles = manager.AuthManager().get_active_roles(user, self.project) + ctxt = context.RequestContext(user.id, + self.project.id, + is_admin=user.is_admin(), + roles=roles) environ = self._env_for(ctxt, methodName) req = webob.Request.blank('/', environ) resp = req.get_response(self.mw) @@ -105,30 +108,26 @@ class AccessTestCase(test.TestCase): def shouldDeny(self, user, methodName): self.assertEqual(401, self.response_status(user, methodName)) - def test_001_allow_all(self): + def test_allow_all(self): users = [self.testadmin, self.testpmsys, self.testnet, self.testsys] for user in users: self.shouldAllow(user, '_allow_all') - def test_002_allow_none(self): + def test_allow_none(self): self.shouldAllow(self.testadmin, '_allow_none') users = [self.testpmsys, self.testnet, self.testsys] for user in users: self.shouldDeny(user, '_allow_none') - def test_003_allow_project_manager(self): + def test_allow_project_manager(self): for user in [self.testadmin, self.testpmsys]: self.shouldAllow(user, '_allow_project_manager') for user in [self.testnet, self.testsys]: self.shouldDeny(user, '_allow_project_manager') - def test_004_allow_sys_and_net(self): + def test_allow_sys_and_net(self): for user in [self.testadmin, self.testnet, self.testsys]: self.shouldAllow(user, '_allow_sys_and_net') # denied because it doesn't have the per project sysadmin for user in [self.testpmsys]: self.shouldDeny(user, '_allow_sys_and_net') - -if __name__ == "__main__": - # TODO: Implement use_fake as an option - unittest.main() -- cgit From ccb5119280d341a2ea1b3e8352acbf32b7f243af Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 21:36:41 +0000 Subject: clean up fake auth manager in other places --- nova/api/ec2/__init__.py | 2 -- nova/auth/manager.py | 3 +- .../api/openstack/contrib/test_floating_ips.py | 5 --- .../api/openstack/contrib/test_multinic_xs.py | 8 ----- nova/tests/api/openstack/test_adminapi.py | 17 ++-------- nova/tests/api/openstack/test_flavors.py | 5 --- nova/tests/api/openstack/test_image_metadata.py | 14 +------- nova/tests/api/openstack/test_server_metadata.py | 14 ++------ nova/tests/api/openstack/test_servers.py | 38 ++++++---------------- nova/tests/api/openstack/test_shared_ip_groups.py | 13 -------- nova/tests/api/openstack/test_zones.py | 24 +++----------- nova/tests/test_objectstore.py | 3 +- 12 files changed, 21 insertions(+), 125 deletions(-) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 1ea26fdeb..af232edda 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -175,7 +175,6 @@ class Authenticate(wsgi.Middleware): if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) roles = authman.get_active_roles(user, project) - LOG.warn(roles) ctxt = context.RequestContext(user_id=user.id, project_id=project.id, is_admin=user.is_admin(), @@ -299,7 +298,6 @@ class Authorizer(wsgi.Middleware): def _matches_any_role(self, context, roles): """Return True if any role in roles is allowed in context.""" - LOG.info(context.roles) if context.is_admin: return True if 'all' in roles: diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 7f99d9016..5118abba2 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -521,8 +521,7 @@ class AuthManager(object): def get_active_roles(self, user, project=None): """Get all active roles for context""" if project: - roles = FLAGS.allowed_roles - roles.append('projectmanager') + roles = FLAGS.allowed_roles + ['projectmanager'] else: roles = FLAGS.global_roles return [role for role in roles if self.has_role(user, role, project)] diff --git a/nova/tests/api/openstack/contrib/test_floating_ips.py b/nova/tests/api/openstack/contrib/test_floating_ips.py index de006d088..50ad7de08 100644 --- a/nova/tests/api/openstack/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/contrib/test_floating_ips.py @@ -74,12 +74,8 @@ class FloatingIpTest(test.TestCase): def setUp(self): super(FloatingIpTest, self).setUp() self.controller = FloatingIPController() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) self.stubs.Set(network.api.API, "get_floating_ip", network_api_get_floating_ip) self.stubs.Set(network.api.API, "list_floating_ips", @@ -96,7 +92,6 @@ class FloatingIpTest(test.TestCase): self._create_floating_ip() def tearDown(self): - self.stubs.UnsetAll() self._delete_floating_ip() super(FloatingIpTest, self).tearDown() diff --git a/nova/tests/api/openstack/contrib/test_multinic_xs.py b/nova/tests/api/openstack/contrib/test_multinic_xs.py index b0a9f7676..ac28f6be6 100644 --- a/nova/tests/api/openstack/contrib/test_multinic_xs.py +++ b/nova/tests/api/openstack/contrib/test_multinic_xs.py @@ -42,22 +42,14 @@ def compute_api_remove_fixed_ip(self, context, instance_id, address): class FixedIpTest(test.TestCase): def setUp(self): super(FixedIpTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) self.stubs.Set(compute.api.API, "add_fixed_ip", compute_api_add_fixed_ip) self.stubs.Set(compute.api.API, "remove_fixed_ip", compute_api_remove_fixed_ip) self.context = context.get_admin_context() - def tearDown(self): - self.stubs.UnsetAll() - super(FixedIpTest, self).tearDown() - def test_add_fixed_ip(self): global last_add_fixed_ip last_add_fixed_ip = (None, None) diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py index e87255b18..b83de40cf 100644 --- a/nova/tests/api/openstack/test_adminapi.py +++ b/nova/tests/api/openstack/test_adminapi.py @@ -16,14 +16,10 @@ # under the License. -import stubout import webob -from paste import urlmap from nova import flags from nova import test -from nova.api import openstack -from nova.api.openstack import auth from nova.tests.api.openstack import fakes FLAGS = flags.FLAGS @@ -33,21 +29,12 @@ class AdminAPITest(test.TestCase): def setUp(self): super(AdminAPITest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) self.allow_admin = FLAGS.allow_admin_api - def tearDown(self): - self.stubs.UnsetAll() - FLAGS.allow_admin_api = self.allow_admin - super(AdminAPITest, self).tearDown() - def test_admin_enabled(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) # We should still be able to access public operations. req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) @@ -55,7 +42,7 @@ class AdminAPITest(test.TestCase): # TODO: Confirm admin operations are available. def test_admin_disabled(self): - FLAGS.allow_admin_api = False + self.flags(allow_admin_api=False) # We should still be able to access public operations. req = webob.Request.blank('/v1.0/flavors') res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py index 4ac35b26b..d0fe72001 100644 --- a/nova/tests/api/openstack/test_flavors.py +++ b/nova/tests/api/openstack/test_flavors.py @@ -16,7 +16,6 @@ # under the License. import json -import stubout import webob import xml.dom.minidom as minidom @@ -56,12 +55,8 @@ def return_instance_type_not_found(context, flavor_id): class FlavorsTest(test.TestCase): def setUp(self): super(FlavorsTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) self.stubs.Set(nova.db.api, "instance_type_get_all", return_instance_types) self.stubs.Set(nova.db.api, "instance_type_get_by_flavor_id", diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index d9fb61e2a..0e9d9fe55 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -16,8 +16,6 @@ # under the License. import json -import stubout -import unittest import webob import xml.dom.minidom as minidom @@ -85,23 +83,13 @@ class ImageMetaDataTest(test.TestCase): def setUp(self): super(ImageMetaDataTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - self.orig_image_service = FLAGS.image_service - FLAGS.image_service = 'nova.image.glance.GlanceImageService' - fakes.FakeAuthManager.auth_data = {} - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_auth(self.stubs) + self.flags(image_service='nova.image.glance.GlanceImageService') # NOTE(dprince) max out properties/metadata in image 3 for testing img3 = self.IMAGE_FIXTURES[2] for num in range(FLAGS.quota_metadata_items): img3['properties']['key%i' % num] = "blah" fakes.stub_out_glance(self.stubs, self.IMAGE_FIXTURES) - def tearDown(self): - self.stubs.UnsetAll() - FLAGS.image_service = self.orig_image_service - super(ImageMetaDataTest, self).tearDown() - def test_index(self): req = webob.Request.blank('/v1.1/images/1/meta') req.environ['api.version'] = '1.1' diff --git a/nova/tests/api/openstack/test_server_metadata.py b/nova/tests/api/openstack/test_server_metadata.py index 0431e68d2..f90485067 100644 --- a/nova/tests/api/openstack/test_server_metadata.py +++ b/nova/tests/api/openstack/test_server_metadata.py @@ -16,14 +16,12 @@ # under the License. import json -import stubout -import unittest import webob from nova import exception from nova import flags -from nova.api import openstack +from nova import test from nova.tests.api.openstack import fakes import nova.wsgi @@ -76,21 +74,13 @@ def return_server_nonexistant(context, server_id): raise exception.InstanceNotFound() -class ServerMetaDataTest(unittest.TestCase): +class ServerMetaDataTest(test.TestCase): def setUp(self): super(ServerMetaDataTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.auth_data = {} - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_auth(self.stubs) fakes.stub_out_key_pair_funcs(self.stubs) self.stubs.Set(nova.db.api, 'instance_get', return_server) - def tearDown(self): - self.stubs.UnsetAll() - super(ServerMetaDataTest, self).tearDown() - def test_index(self): self.stubs.Set(nova.db.api, 'instance_metadata_get', return_server_metadata) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index e0456781a..91025fcb9 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -20,7 +20,6 @@ import json import unittest from xml.dom import minidom -import stubout import webob from nova import context @@ -224,8 +223,6 @@ class ServersTest(test.TestCase): def setUp(self): super(ServersTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthDatabase.data = {} fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) fakes.stub_out_key_pair_funcs(self.stubs) @@ -250,15 +247,9 @@ class ServersTest(test.TestCase): self.stubs.Set(nova.compute.API, 'resume', fake_compute_api) self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api) self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) - self.allow_admin = FLAGS.allow_admin_api self.webreq = common.webob_factory('/v1.0/servers') - def tearDown(self): - self.stubs.UnsetAll() - FLAGS.allow_admin_api = self.allow_admin - super(ServersTest, self).tearDown() - def test_get_server_by_id(self): req = webob.Request.blank('/v1.0/servers/1') res = req.get_response(fakes.wsgi_app()) @@ -853,7 +844,7 @@ class ServersTest(test.TestCase): def test_create_instance_via_zones(self): """Server generated ReservationID""" self._setup_for_create_instance() - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=3, flavorId=2, @@ -875,7 +866,7 @@ class ServersTest(test.TestCase): def test_create_instance_via_zones_with_resid(self): """User supplied ReservationID""" self._setup_for_create_instance() - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=3, flavorId=2, @@ -1305,7 +1296,7 @@ class ServersTest(test.TestCase): self.assertEqual(s['flavorId'], 1) def test_server_pause(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, personality={})) @@ -1317,7 +1308,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 202) def test_server_unpause(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, personality={})) @@ -1329,7 +1320,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 202) def test_server_suspend(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, personality={})) @@ -1341,7 +1332,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 202) def test_server_resume(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, personality={})) @@ -1353,7 +1344,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 202) def test_server_reset_network(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, personality={})) @@ -1365,7 +1356,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 202) def test_server_inject_network_info(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, personality={})) @@ -1652,7 +1643,7 @@ class ServersTest(test.TestCase): self.assertEqual(self.server_delete_called, True) def test_rescue_accepted(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = {} self.called = False @@ -1671,7 +1662,7 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 202) def test_rescue_raises_handled(self): - FLAGS.allow_admin_api = True + self.flags(allow_admin_api=True) body = {} def rescue_mock(*args, **kwargs): @@ -2160,17 +2151,8 @@ class TestServerInstanceCreation(test.TestCase): def setUp(self): super(TestServerInstanceCreation, self).setUp() - self.stubs = stubout.StubOutForTesting() - 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(TestServerInstanceCreation, self).tearDown() def _setup_mock_compute_api_for_personality(self): diff --git a/nova/tests/api/openstack/test_shared_ip_groups.py b/nova/tests/api/openstack/test_shared_ip_groups.py index c2bd7e45a..36fa1de0f 100644 --- a/nova/tests/api/openstack/test_shared_ip_groups.py +++ b/nova/tests/api/openstack/test_shared_ip_groups.py @@ -15,26 +15,13 @@ # License for the specific language governing permissions and limitations # under the License. -import stubout import webob from nova import test -from nova.api.openstack import shared_ip_groups from nova.tests.api.openstack import fakes class SharedIpGroupsTest(test.TestCase): - def setUp(self): - super(SharedIpGroupsTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} - fakes.stub_out_auth(self.stubs) - - def tearDown(self): - self.stubs.UnsetAll() - super(SharedIpGroupsTest, self).tearDown() - def test_get_shared_ip_groups(self): req = webob.Request.blank('/v1.0/shared_ip_groups') res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 6a6e13d93..4a208ea24 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -95,31 +95,15 @@ def zone_select(context, specs): class ZonesTest(test.TestCase): def setUp(self): super(ZonesTest, self).setUp() - self.stubs = stubout.StubOutForTesting() - fakes.FakeAuthManager.reset_fake_data() - fakes.FakeAuthDatabase.data = {} + self.flags(allow_admin_api=True) fakes.stub_out_networking(self.stubs) fakes.stub_out_rate_limiting(self.stubs) - fakes.stub_out_auth(self.stubs) - - self.allow_admin = FLAGS.allow_admin_api - FLAGS.allow_admin_api = True self.stubs.Set(nova.db, 'zone_get', zone_get) self.stubs.Set(nova.db, 'zone_update', zone_update) self.stubs.Set(nova.db, 'zone_create', zone_create) self.stubs.Set(nova.db, 'zone_delete', zone_delete) - self.old_zone_name = FLAGS.zone_name - self.old_zone_capabilities = FLAGS.zone_capabilities - - def tearDown(self): - self.stubs.UnsetAll() - FLAGS.allow_admin_api = self.allow_admin - FLAGS.zone_name = self.old_zone_name - FLAGS.zone_capabilities = self.old_zone_capabilities - super(ZonesTest, self).tearDown() - def test_get_zone_list_scheduler(self): self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler) req = webob.Request.blank('/v1.0/zones') @@ -190,8 +174,8 @@ class ZonesTest(test.TestCase): self.assertFalse('username' in res_dict['zone']) def test_zone_info(self): - FLAGS.zone_name = 'darksecret' - FLAGS.zone_capabilities = ['cap1=a;b', 'cap2=c;d'] + caps = ['cap1=a;b', 'cap2=c;d'] + self.flags(zone_name='darksecret', zone_capabilities=caps) self.stubs.Set(api, '_call_scheduler', zone_capabilities) body = dict(zone=dict(username='zeb', password='sneaky')) @@ -205,7 +189,7 @@ class ZonesTest(test.TestCase): self.assertEqual(res_dict['zone']['cap2'], 'c;d') def test_zone_select(self): - FLAGS.build_plan_encryption_key = 'c286696d887c9aa0611bbb3e2025a45a' + self.flags(build_plan_encryption_key='c286696d887c9aa0611bbb3e2025a45a') self.stubs.Set(api, 'select', zone_select) req = webob.Request.blank('/v1.0/zones/select') diff --git a/nova/tests/test_objectstore.py b/nova/tests/test_objectstore.py index af4ee27cd..0b2dce20e 100644 --- a/nova/tests/test_objectstore.py +++ b/nova/tests/test_objectstore.py @@ -52,8 +52,7 @@ class S3APITestCase(test.TestCase): def setUp(self): """Setup users, projects, and start a test server.""" super(S3APITestCase, self).setUp() - self.flags(auth_driver='nova.auth.ldapdriver.FakeLdapDriver', - buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'), + self.flags(buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'), s3_host='127.0.0.1') shutil.rmtree(FLAGS.buckets_path) -- cgit From 634a195da129fb043184ac1589efd0bdac5df256 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 21:40:10 +0000 Subject: remove some more stubouts and fakes --- nova/tests/api/openstack/test_accounts.py | 10 +--------- nova/tests/api/openstack/test_auth.py | 4 ---- nova/tests/api/openstack/test_users.py | 9 +-------- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py index 64abcf48c..89dbf5213 100644 --- a/nova/tests/api/openstack/test_accounts.py +++ b/nova/tests/api/openstack/test_accounts.py @@ -16,7 +16,6 @@ import json -import stubout import webob from nova import flags @@ -41,7 +40,7 @@ def fake_admin_check(self, req): class AccountsTest(test.TestCase): def setUp(self): super(AccountsTest, self).setUp() - self.stubs = stubout.StubOutForTesting() + self.flags(allow_admin_api=True) self.stubs.Set(accounts.Controller, '__init__', fake_init) self.stubs.Set(accounts.Controller, '_check_admin', @@ -52,8 +51,6 @@ class AccountsTest(test.TestCase): fakes.stub_out_rate_limiting(self.stubs) fakes.stub_out_auth(self.stubs) - self.allow_admin = FLAGS.allow_admin_api - FLAGS.allow_admin_api = True fakemgr = fakes.FakeAuthManager() joeuser = User('id1', 'guy1', 'acc1', 'secret1', False) superuser = User('id2', 'guy2', 'acc2', 'secret2', True) @@ -62,11 +59,6 @@ class AccountsTest(test.TestCase): fakemgr.create_project('test1', joeuser) fakemgr.create_project('test2', superuser) - def tearDown(self): - self.stubs.UnsetAll() - FLAGS.allow_admin_api = self.allow_admin - super(AccountsTest, self).tearDown() - def test_get_account(self): req = webob.Request.blank('/v1.0/accounts/test1') res = req.get_response(fakes.wsgi_app()) diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py index 1b1d44368..306ae1aa0 100644 --- a/nova/tests/api/openstack/test_auth.py +++ b/nova/tests/api/openstack/test_auth.py @@ -17,7 +17,6 @@ import datetime -import stubout import webob import webob.dec @@ -43,7 +42,6 @@ class Test(test.TestCase): fakes.stub_out_networking(self.stubs) def tearDown(self): - self.stubs.UnsetAll() fakes.fake_data_store = {} super(Test, self).tearDown() @@ -237,7 +235,6 @@ class TestFunctional(test.TestCase): class TestLimiter(test.TestCase): def setUp(self): super(TestLimiter, self).setUp() - self.stubs = stubout.StubOutForTesting() self.stubs.Set(nova.api.openstack.auth.AuthMiddleware, '__init__', fakes.fake_auth_init) self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext) @@ -246,7 +243,6 @@ class TestLimiter(test.TestCase): fakes.stub_out_networking(self.stubs) def tearDown(self): - self.stubs.UnsetAll() fakes.fake_data_store = {} super(TestLimiter, self).tearDown() diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py index effb2f592..705c02f6b 100644 --- a/nova/tests/api/openstack/test_users.py +++ b/nova/tests/api/openstack/test_users.py @@ -15,7 +15,6 @@ import json -import stubout import webob from nova import flags @@ -41,7 +40,7 @@ def fake_admin_check(self, req): class UsersTest(test.TestCase): def setUp(self): super(UsersTest, self).setUp() - self.stubs = stubout.StubOutForTesting() + self.flags(allow_admin_api=True) self.stubs.Set(users.Controller, '__init__', fake_init) self.stubs.Set(users.Controller, '_check_admin', @@ -58,16 +57,10 @@ class UsersTest(test.TestCase): fakes.stub_out_auth(self.stubs) self.allow_admin = FLAGS.allow_admin_api - FLAGS.allow_admin_api = True fakemgr = fakes.FakeAuthManager() fakemgr.add_user(User('id1', 'guy1', 'acc1', 'secret1', False)) fakemgr.add_user(User('id2', 'guy2', 'acc2', 'secret2', True)) - def tearDown(self): - self.stubs.UnsetAll() - FLAGS.allow_admin_api = self.allow_admin - super(UsersTest, self).tearDown() - def test_get_user_list(self): req = webob.Request.blank('/v1.0/users') res = req.get_response(fakes.wsgi_app()) -- cgit From 3b9af8a63abb21aac1ef9ef8dcb801b7a3686ce8 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 21:52:11 +0000 Subject: pull out auth manager from db --- nova/db/sqlalchemy/models.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index c1150f7ca..14dc09e9f 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -177,14 +177,6 @@ class Instance(BASE, NovaBase): user_id = Column(String(255)) project_id = Column(String(255)) - @property - def user(self): - return auth.manager.AuthManager().get_user(self.user_id) - - @property - def project(self): - return auth.manager.AuthManager().get_project(self.project_id) - image_ref = Column(String(255)) kernel_id = Column(String(255)) ramdisk_id = Column(String(255)) @@ -465,14 +457,6 @@ class SecurityGroup(BASE, NovaBase): 'Instance.deleted == False)', backref='security_groups') - @property - def user(self): - return auth.manager.AuthManager().get_user(self.user_id) - - @property - def project(self): - return auth.manager.AuthManager().get_project(self.project_id) - class SecurityGroupIngressRule(BASE, NovaBase): """Represents a rule in a security group.""" -- cgit From 164afd51017721b9cbaf2880b9dada3d4cd9b42c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 22:04:52 +0000 Subject: remove auth manager from instance helper --- nova/api/openstack/create_instance_helper.py | 5 +++-- nova/auth/manager.py | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 7249f1261..2034e8ada 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -20,6 +20,7 @@ import webob from webob import exc from xml.dom import minidom +from nova import db from nova import exception from nova import flags from nova import log as logging @@ -29,7 +30,6 @@ from nova import utils from nova.compute import instance_types from nova.api.openstack import wsgi -from nova.auth import manager as auth_manager LOG = logging.getLogger('nova.api.openstack.create_instance_helper') @@ -77,7 +77,8 @@ class CreateInstanceHelper(object): key_name = None key_data = None - key_pairs = auth_manager.AuthManager.get_key_pairs(context) + key_pairs = db.key_pair_get_all_by_user(context.elevated(), + context.user_id) if key_pairs: key_pair = key_pairs[0] key_name = key_pair['name'] diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 5118abba2..6205cfb56 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -738,10 +738,6 @@ class AuthManager(object): with self.driver() as drv: drv.modify_user(uid, access_key, secret_key, admin) - @staticmethod - def get_key_pairs(context): - return db.key_pair_get_all_by_user(context.elevated(), context.user_id) - def get_credentials(self, user, project=None, use_dmz=True): """Get credential zip for user in project""" if not isinstance(user, User): -- cgit From 4e4bbda2a15df9f2366488d092bc466655a170b9 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 22 Jul 2011 22:12:22 +0000 Subject: pep cleanup --- nova/context.py | 3 ++- nova/tests/api/openstack/test_servers.py | 3 ++- nova/tests/api/openstack/test_zones.py | 3 ++- nova/tests/test_api.py | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/context.py b/nova/context.py index e7c60142c..db19f136c 100644 --- a/nova/context.py +++ b/nova/context.py @@ -31,7 +31,8 @@ class RequestContext(object): """ def __init__(self, user_id, project_id, is_admin=None, read_deleted=False, - roles=None, remote_address=None, timestamp=None, request_id=None): + roles=None, remote_address=None, timestamp=None, + request_id=None): self.user_id = user_id self.project_id = project_id self.roles = roles or [] diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index f05310325..98d4bed52 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1306,7 +1306,8 @@ class ServersTest(test.TestCase): ''' def return_servers_with_host(context, *args, **kwargs): - return [stub_instance(i, 'fake', 'fake', None, None, i % 2) for i in xrange(5)] + return [stub_instance(i, 'fake', 'fake', None, None, i % 2) + for i in xrange(5)] self.stubs.Set(nova.db.api, 'instance_get_all_by_project', return_servers_with_host) diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py index 4a208ea24..3deb844aa 100644 --- a/nova/tests/api/openstack/test_zones.py +++ b/nova/tests/api/openstack/test_zones.py @@ -189,7 +189,8 @@ class ZonesTest(test.TestCase): self.assertEqual(res_dict['zone']['cap2'], 'c;d') def test_zone_select(self): - self.flags(build_plan_encryption_key='c286696d887c9aa0611bbb3e2025a45a') + key = 'c286696d887c9aa0611bbb3e2025a45a' + self.flags(build_plan_encryption_key=key) self.stubs.Set(api, 'select', zone_select) req = webob.Request.blank('/v1.0/zones/select') diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 292f9d668..3ec1c9abf 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -395,7 +395,6 @@ class ApiEc2TestCase(test.TestCase): self.expect_http() self.mox.ReplayAll() - security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") for x in range(random.randint(4, 8))) -- cgit From 8de3c0fcaee546fae3d415ef5ddcbb51fb1db6d7 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 25 Jul 2011 17:49:09 +0000 Subject: fix for reviews --- nova/api/openstack/create_instance_helper.py | 2 ++ nova/compute/api.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 2034e8ada..573153f68 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -77,6 +77,8 @@ class CreateInstanceHelper(object): key_name = None key_data = None + # TODO(vish): Key pair access should move into a common library + # instead of being accessed directly from the db. key_pairs = db.key_pair_get_all_by_user(context.elevated(), context.user_id) if key_pairs: diff --git a/nova/compute/api.py b/nova/compute/api.py index d1c3fd6fd..487d23b0d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -676,7 +676,6 @@ class API(base.Base): all instances in the system. """ - LOG.info(locals()) if reservation_id is not None: recurse_zones = True instances = self.db.instance_get_all_by_reservation( -- cgit From 422d5329276f5c2252d7328d4112be7c696a274a Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Tue, 26 Jul 2011 09:57:39 -0400 Subject: Updated ServerXMLSerializer to utilize the IPXMLSerializer --- nova/api/openstack/servers.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 4e6f0d7b5..11dafc272 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -28,6 +28,7 @@ from nova import log as logging from nova import utils from nova.api.openstack import common from nova.api.openstack import create_instance_helper as helper +from nova.api.openstack import ips import nova.api.openstack.views.addresses import nova.api.openstack.views.flavors import nova.api.openstack.views.images @@ -608,6 +609,7 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): def __init__(self): self.metadata_serializer = common.MetadataXMLSerializer() + self.addresses_serializer = ips.IPXMLSerializer() def _create_basic_entity_node(self, xml_doc, id, links, name): basic_node = xml_doc.createElement(name) @@ -621,23 +623,7 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata) def _create_addresses_node(self, xml_doc, addresses): - addresses_node = xml_doc.createElement('addresses') - for name, network_dict in addresses.items(): - network_node = self._create_network_node(xml_doc, - name, - network_dict) - addresses_node.appendChild(network_node) - return addresses_node - - def _create_network_node(self, xml_doc, network_name, network_dict): - network_node = xml_doc.createElement('network') - network_node.setAttribute('id', network_name) - for ip in network_dict: - ip_node = xml_doc.createElement('ip') - ip_node.setAttribute('version', str(ip['version'])) - ip_node.setAttribute('addr', ip['addr']) - network_node.appendChild(ip_node) - return network_node + return self.addresses_serializer.networks_to_xml(xml_doc, addresses) def _add_server_attributes(self, node, server): node.setAttribute('id', str(server['id'])) -- cgit From 1ad5f2eaf49904d8e14546d59699b1472a1a5bb2 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 26 Jul 2011 12:57:06 -0400 Subject: xml deserialization works now --- nova/api/openstack/create_instance_helper.py | 79 +++---------- nova/api/openstack/wsgi.py | 9 +- nova/tests/api/openstack/test_servers.py | 162 ++++++++++++--------------- 3 files changed, 90 insertions(+), 160 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 03272443a..b717b8ac0 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -303,79 +303,30 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): """Marshal the server attribute of a parsed request""" server = {} server_node = self.find_first_child_named(node, 'server') - for attr in ["name", "imageId", "flavorId"]: + + attributes = ["name", "imageId", "flavorId", "imageRef", + "flavorRef", "adminPass"] + for attr in attributes: if server_node.getAttribute(attr): server[attr] = server_node.getAttribute(attr) - image = self._extract_image(server_node) - if image is not None: - server["image"] = image - flavor = self._extract_flavor(server_node) - if flavor is not None: - server["flavor"] = flavor - metadata_node = self.find_first_child_named(server_node, "metadata") - metadata = self.extract_metadata(metadata_node) - if metadata is not None: - server["metadata"] = metadata - personality = self._extract_personality(server_node) - if personality is not None: - server["personality"] = personality - return server - - def _extract_image(self, server_node): - """Retrieve an image entity from the server node""" - image_node = self.find_first_child_named(server_node, "image") - if image_node is None: - return None - - image = {} - image_id = image_node.getAttribute('id') - if image_id is not None: - image['id'] = image_id - - image['links'] = self._extract_links_from_node(image_node) - - return image - def _extract_flavor(self, server_node): - """Retrieve a flavor entity from the server node""" - flavor_node = self.find_first_child_named(server_node, "flavor") - if flavor_node is None: - return None - - flavor = {} - flavor_id = flavor_node.getAttribute('id') - if flavor_id: - flavor['id'] = flavor_id - - flavor['links'] = self._extract_links_from_node(flavor_node) - - return flavor - - def _extract_links_from_node(self, parent_node): - """Retrieve link entities from a links container provided node""" - links = [] + metadata_node = self.find_first_child_named(server_node, "metadata") + server["metadata"] = self.extract_metadata(metadata_node) - for link_node in self.find_children_named(parent_node, 'atom:link'): - link = { - 'rel': link_node.getAttribute('rel'), - 'href': link_node.getAttribute('href'), - } - if link['rel'] is not None and link['href'] is not None: - links.append(link) + server["personality"] = self._extract_personality(server_node) - return links + return server def _extract_personality(self, server_node): """Marshal the personality attribute of a parsed request""" personality_node = \ self.find_first_child_named(server_node, "personality") - if personality_node is None: - return None personality = [] - for file_node in self.find_children_named(personality_node, "file"): - item = {} - if file_node.hasAttribute("path"): - item["path"] = file_node.getAttribute("path") - item["contents"] = self.extract_text(file_node) - personality.append(item) + if personality_node is not None: + for file_node in self.find_children_named(personality_node, "file"): + item = {} + if file_node.hasAttribute("path"): + item["path"] = file_node.getAttribute("path") + item["contents"] = self.extract_text(file_node) + personality.append(item) return personality diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index a28443d12..53dab22e8 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -165,12 +165,11 @@ class MetadataXMLDeserializer(XMLDeserializer): def extract_metadata(self, metadata_node): """Marshal the metadata attribute of a parsed request""" - if metadata_node is None: - return None metadata = {} - for meta_node in self.find_children_named(metadata_node, "meta"): - key = meta_node.getAttribute("key") - metadata[key] = self.extract_text(meta_node) + if metadata_node is not None: + for meta_node in self.find_children_named(metadata_node, "meta"): + key = meta_node.getAttribute("key") + metadata[key] = self.extract_text(meta_node) return metadata diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 9ff0a3d04..97cebd29c 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2530,77 +2530,85 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): def test_minimal_request(self): serial_request = """ - - - -""" +""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_admin_pass(self): + serial_request = """ +""" request = self.deserializer.deserialize(serial_request, 'create') expected = { "server": { "name": "new-server-test", - "image": {"id": "1", "links": []}, - "flavor": {"id": "2", "links": []}, + "imageRef": "1", + "flavorRef": "2", + "adminPass": "1234", + "metadata": {}, + "personality": [], }, } self.assertEquals(request['body'], expected) + def test_image_link(self): serial_request = """ - - - - - -""" +""" request = self.deserializer.deserialize(serial_request, 'create') expected = { "server": { "name": "new-server-test", - "image": { - "id": "1", - "links": [ - { - "rel": "bookmark", - "href": "http://localhost:8774/v1.1/images/2", - }, - ], - }, - "flavor": {"id": "3", "links": []}, + "imageRef": "http://localhost:8774/v1.1/images/2", + "flavorRef": "3", + "metadata": {}, + "personality": [], }, } self.assertEquals(request['body'], expected) def test_flavor_link(self): serial_request = """ - - - - - -""" +""" request = self.deserializer.deserialize(serial_request, 'create') expected = { "server": { "name": "new-server-test", - "image": {"id": "1", "links": []}, - "flavor": { - "id": "2", - "links": [ - { - "rel": "bookmark", - "href": "http://localhost:8774/v1.1/flavors/3", - }, - ], - }, + "imageRef": "1", + "flavorRef": "http://localhost:8774/v1.1/flavors/3", + "metadata": {}, + "personality": [], }, } self.assertEquals(request['body'], expected) def test_empty_metadata_personality(self): serial_request = """ - - - + """ @@ -2608,8 +2616,8 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1", "links": []}, - "flavor": {"id": "2", "links": []}, + "imageRef": "1", + "flavorRef": "2", "metadata": {}, "personality": [], }, @@ -2618,9 +2626,10 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): def test_multiple_metadata_items(self): serial_request = """ - - - + two snack @@ -2630,18 +2639,20 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1", "links": []}, - "flavor": {"id": "2", "links": []}, + "imageRef": "1", + "flavorRef": "2", "metadata": {"one": "two", "open": "snack"}, + "personality": [], }, } self.assertEquals(request['body'], expected) def test_multiple_personality_files(self): serial_request = """ - - - + MQ== Mg== @@ -2651,8 +2662,9 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): expected = { "server": { "name": "new-server-test", - "image": {"id": "1", "links": []}, - "flavor": {"id": "2", "links": []}, + "imageRef": "1", + "flavorRef": "2", + "metadata": {}, "personality": [ {"path": "/etc/banner.txt", "contents": "MQ=="}, {"path": "/etc/hosts", "contents": "Mg=="}, @@ -2662,59 +2674,27 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): self.assertEquals(request['body'], expected) def test_spec_request(self): - image_self_link = "http://servers.api.openstack.org/v1.1/1234/" + \ - "images/52415800-8b69-11e0-9b19-734f6f006e54" image_bookmark_link = "http://servers.api.openstack.org/1234/" + \ "images/52415800-8b69-11e0-9b19-734f6f006e54" serial_request = """ - - - - - Apache1 Mg== -""" % (image_self_link, image_bookmark_link) +""" % (image_bookmark_link) request = self.deserializer.deserialize(serial_request, 'create') expected = { "server": { "name": "new-server-test", - "image": { - "id": "52415800-8b69-11e0-9b19-734f6f006e54", - "links": [ - { - "rel": "self", - "href": "http://servers.api.openstack.org/" + \ - "v1.1/1234/images/52415800-8b69-11" + \ - "e0-9b19-734f6f006e54", - }, - { - "rel": "bookmark", - "href": "http://servers.api.openstack.org/" + \ - "1234/images/52415800-8b69-11e0-9b" + \ - "19-734f6f006e54", - }, - ], - }, - "flavor": { - "id": "52415800-8b69-11e0-9b19-734f1195ff37", - "links": [], - }, + "imageRef": "http://servers.api.openstack.org/1234/" + \ + "images/52415800-8b69-11e0-9b19-734f6f006e54", + "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37", "metadata": {"My Server Name": "Apache1"}, "personality": [ { -- cgit From 3db1c53486fdb669ac2bab303335548d7a7c617d Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 26 Jul 2011 13:28:11 -0400 Subject: updating imageRef and flavorRef parsing --- nova/api/openstack/servers.py | 42 ++++---------------------------- nova/tests/api/openstack/test_servers.py | 29 ++++++++-------------- 2 files changed, 15 insertions(+), 56 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index e1f845f36..06112eee3 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -493,51 +493,19 @@ class ControllerV11(Controller): def _image_ref_from_req_data(self, data): try: - image = data['server']['image'] + return data['server']['imageRef'] except (TypeError, KeyError): - msg = _("Missing image entity") + msg = _("Missing imageRef attribute") raise exc.HTTPBadRequest(explanation=msg) - try: - links = image.get('links', []) - except AttributeError: - msg = _("Malformed image entity") - raise exc.HTTPBadRequest(explanation=msg) - - image_ref = self._href_from_bookmark_links(links) - - if image_ref is None: - try: - return image['id'] - except KeyError: - msg = _("Missing id attribute on image entity") - raise exc.HTTPBadRequest(explanation=msg) - else: - return image_ref - def _flavor_id_from_req_data(self, data): try: - flavor = data['server']['flavor'] + flavor_ref = data['server']['flavorRef'] except (TypeError, KeyError): - msg = _("Missing flavor entity") + msg = _("Missing flavorRef attribute") raise exc.HTTPBadRequest(explanation=msg) - try: - links = flavor.get('links', []) - except AttributeError: - msg = _("Malformed flavor entity") - raise exc.HTTPBadRequest(explanation=msg) - - flavor_ref = self._href_from_bookmark_links(links) - - if flavor_ref is None: - try: - return flavor['id'] - except (KeyError, AttributeError): - msg = _("Missing id attribute in flavor entity") - raise exc.HTTPBadRequest(explanation=msg) - else: - return common.get_id_from_href(flavor_ref) + return common.get_id_from_href(flavor_ref) def _build_view(self, req, instance, is_detail=False): base_url = req.application_url diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 97cebd29c..cae04e6ab 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1267,18 +1267,8 @@ class ServersTest(test.TestCase): body = { 'server': { 'name': 'server_test', - 'image': { - 'id': 2, - 'links': [ - {'rel': 'bookmark', 'href': image_href}, - ], - }, - 'flavor': { - 'id': 3, - 'links': [ - {'rel': 'bookmark', 'href': flavor_ref}, - ], - }, + 'imageRef': image_href, + 'flavorRef': flavor_ref, 'metadata': { 'hello': 'world', 'open': 'stack', @@ -1299,14 +1289,14 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + print res.body self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(16, len(server['adminPass'])) + self.assertEqual(1, server['id']) self.assertEqual('server_test', server['name']) self.assertEqual(expected_flavor, server['flavor']) self.assertEqual(expected_image, server['image']) - self.assertEqual(res.status_int, 200) - #self.assertEqual(1, server['id']) def test_create_instance_v1_1_invalid_flavor_href(self): self._setup_for_create_instance() @@ -1360,7 +1350,7 @@ class ServersTest(test.TestCase): self._setup_for_create_instance() image_id = "2" - flavor_ref = 'http://localhost/flavors/3' + flavor_ref = 'http://localhost/v1.1/flavors/3' expected_flavor = { "id": "3", "links": [ @@ -1394,6 +1384,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + print res.body self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(expected_flavor, server['flavor']) @@ -1428,8 +1419,8 @@ class ServersTest(test.TestCase): body = { 'server': { 'name': 'server_test', - 'image': {'id': 3}, - 'flavor': {'id': 3}, + 'imageRef': 3, + 'flavorRef': 3, 'adminPass': 'testpass', }, } @@ -1452,8 +1443,8 @@ class ServersTest(test.TestCase): body = { 'server': { 'name': 'server_test', - 'image': {'id': 3}, - 'flavor': {'id': 3}, + 'imageRef': 3, + 'flavorRef': 3, 'adminPass': '', }, } -- cgit From 94866fef798a6b72061720cb654442cd194b9f5f Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 26 Jul 2011 13:47:37 -0400 Subject: reverting tests to use imageRef, flavorRef --- nova/api/openstack/create_instance_helper.py | 7 +++-- nova/tests/api/openstack/test_servers.py | 5 +++- nova/tests/integrated/integrated_helpers.py | 4 +-- nova/tests/integrated/test_servers.py | 40 ++++++++-------------------- 4 files changed, 20 insertions(+), 36 deletions(-) diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index b717b8ac0..1342397c4 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -319,11 +319,10 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): def _extract_personality(self, server_node): """Marshal the personality attribute of a parsed request""" - personality_node = \ - self.find_first_child_named(server_node, "personality") + node = self.find_first_child_named(server_node, "personality") personality = [] - if personality_node is not None: - for file_node in self.find_children_named(personality_node, "file"): + if node is not None: + for file_node in self.find_children_named(node, "file"): item = {} if file_node.hasAttribute("path"): item["path"] = file_node.getAttribute("path") diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index cae04e6ab..ff6f5d13a 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2247,6 +2247,8 @@ class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase): "name": "new-server-test", "imageId": "1", "flavorId": "1", + "metadata": {}, + "personality": [], }} self.assertEquals(request['body'], expected) @@ -2262,6 +2264,7 @@ class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase): "imageId": "1", "flavorId": "1", "metadata": {}, + "personality": [], }} self.assertEquals(request['body'], expected) @@ -2276,6 +2279,7 @@ class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase): "name": "new-server-test", "imageId": "1", "flavorId": "1", + "metadata": {}, "personality": [], }} self.assertEquals(request['body'], expected) @@ -2557,7 +2561,6 @@ class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): } self.assertEquals(request['body'], expected) - def test_image_link(self): serial_request = """ Date: Tue, 26 Jul 2011 13:49:04 -0400 Subject: fixed minor issues --- nova/api/openstack/servers.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 11dafc272..194ec82d5 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -630,8 +630,8 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): node.setAttribute('uuid', str(server['uuid'])) node.setAttribute('hostId', str(server['hostId'])) node.setAttribute('name', server['name']) - node.setAttribute('created', server['created']) - node.setAttribute('updated', server['updated']) + node.setAttribute('created', str(server['created'])) + node.setAttribute('updated', str(server['updated'])) node.setAttribute('status', server['status']) if 'progress' in server: node.setAttribute('progress', str(server['progress'])) @@ -655,17 +655,19 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): for link_node in link_nodes: server_node.appendChild(link_node) - image_node = self._create_basic_entity_node(xml_doc, + if 'image' in server: + image_node = self._create_basic_entity_node(xml_doc, server['image']['id'], server['image']['links'], 'image') - server_node.appendChild(image_node) + server_node.appendChild(image_node) - flavor_node = self._create_basic_entity_node(xml_doc, + if 'flavor' in server: + flavor_node = self._create_basic_entity_node(xml_doc, server['flavor']['id'], server['flavor']['links'], 'flavor') - server_node.appendChild(flavor_node) + server_node.appendChild(flavor_node) metadata = server.get('metadata', {}).items() if len(metadata) > 0: -- cgit From e56d49721a22a1c337c6543c37bdd798a5c5230d Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 26 Jul 2011 13:55:07 -0400 Subject: cleanup --- nova/tests/api/openstack/test_servers.py | 5 ----- nova/tests/integrated/test_servers.py | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index ff6f5d13a..719c4592b 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1289,7 +1289,6 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) - print res.body self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(16, len(server['adminPass'])) @@ -1384,7 +1383,6 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) - print res.body self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(expected_flavor, server['flavor']) @@ -1429,11 +1427,8 @@ class ServersTest(test.TestCase): 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, 200) - server = json.loads(res.body)['server'] self.assertEqual(server['adminPass'], body['server']['adminPass']) diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index fcb517cf5..67b3c485a 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -285,6 +285,26 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # Cleanup self._delete_server(created_server_id) + def test_rename_server(self): + """Test building and renaming a server.""" + + # Create a server + server = self._build_minimal_create_server_request() + created_server = self.api.post_server({'server': server}) + LOG.debug("created_server: %s" % created_server) + server_id = created_server['id'] + self.assertTrue(server_id) + + # Rename the server to 'new-name' + self.api.put_server(server_id, {'server': {'name': 'new-name'}}) + + # Check the name of the server + created_server = self.api.get_server(server_id) + self.assertEqual(created_server['name'], 'new-name') + + # Cleanup + self._delete_server(server_id) + if __name__ == "__main__": unittest.main() -- cgit From 24e1f23dbaf9ffd3f42fe05c24b980a6a0f09499 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 26 Jul 2011 14:06:02 -0400 Subject: removing extra function --- nova/api/openstack/servers.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 06112eee3..618778ea3 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -480,17 +480,6 @@ class ControllerV11(Controller): except exception.NotFound: raise exc.HTTPNotFound() - def _href_from_bookmark_links(self, links): - for link in links: - try: - if link.get('rel') == 'bookmark': - href = link.get('href') - if href is not None: - return href - except AttributeError: - msg = _("Malformed link entity") - raise exc.HTTPBadRequest(explanation=msg) - def _image_ref_from_req_data(self, data): try: return data['server']['imageRef'] -- cgit From f8a182d6196a9f1ba0065912e2b703ea61a1c260 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 26 Jul 2011 14:09:49 -0400 Subject: adding assert to check for progress attribute --- nova/tests/api/openstack/test_servers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 719c4592b..5e88686c4 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1293,6 +1293,7 @@ class ServersTest(test.TestCase): server = json.loads(res.body)['server'] self.assertEqual(16, len(server['adminPass'])) self.assertEqual(1, server['id']) + self.assertEqual(0, server['progress']) self.assertEqual('server_test', server['name']) self.assertEqual(expected_flavor, server['flavor']) self.assertEqual(expected_image, server['image']) -- cgit From 2e652f4cc72976ecc471a6c6f3b48afb3eb5a420 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Tue, 26 Jul 2011 16:42:16 -0400 Subject: Updated test stubs to contain the correct data Updated created and updated in responses to use correct time format --- nova/api/openstack/views/servers.py | 15 +++++++++++++-- nova/tests/api/openstack/test_servers.py | 19 +++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index be25e1e40..c90801724 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime import hashlib import os @@ -149,8 +150,10 @@ class ViewBuilderV11(ViewBuilder): def _build_detail(self, inst): response = super(ViewBuilderV11, self)._build_detail(inst) - response['server']['created'] = inst['created_at'] - response['server']['updated'] = inst['updated_at'] + response['server']['created'] = \ + self._convert_timeformat(inst['created_at']) + response['server']['updated'] = \ + self._convert_timeformat(inst['updated_at']) if 'status' in response['server']: if response['server']['status'] == "ACTIVE": response['server']['progress'] = 100 @@ -221,3 +224,11 @@ class ViewBuilderV11(ViewBuilder): """Create an url that refers to a specific flavor id.""" return os.path.join(common.remove_version_from_href(self.base_url), "servers", str(server_id)) + + def _convert_timeformat(self, time): + """Converts the given time into the common time format + + :param time: should be a datetime + + """ + return time.strftime(utils.TIME_FORMAT) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 4ca79434f..e154fb4fa 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -16,6 +16,7 @@ # under the License. import base64 +import datetime import json import unittest from xml.dom import minidom @@ -172,8 +173,10 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None, instance = { "id": int(id), - "created_at": "2010-10-10T12:00:00Z", - "updated_at": "2010-11-11T11:00:00Z", + "created_at": datetime.datetime.strptime("2010-10-10 12:00:00", + "%Y-%m-%d %H:%M:%S"), + "updated_at": datetime.datetime.strptime("2010-11-11 11:00:00", + "%Y-%m-%d %H:%M:%S"), "admin_pass": "", "user_id": user_id, "project_id": "", @@ -1048,8 +1051,10 @@ class ServersTest(test.TestCase): 'uuid': FAKE_UUID, 'instance_type': dict(inst_type), 'image_ref': image_ref, - 'created_at': '2010-10-10T12:00:00Z', - 'updated_at': '2010-11-11T11:00:00Z', + "created_at": datetime.datetime.strptime("2010-10-10 12:00:00", + "%Y-%m-%d %H:%M:%S"), + "updated_at": datetime.datetime.strptime("2010-11-11 11:00:00", + "%Y-%m-%d %H:%M:%S"), } def server_update(context, id, params): @@ -2901,8 +2906,10 @@ class ServersViewBuilderV11Test(test.TestCase): def _get_instance(self): instance = { "id": 1, - "created_at": "2010-10-10T12:00:00Z", - "updated_at": "2010-11-11T11:00:00Z", + "created_at": datetime.datetime.strptime("2010-10-10 12:00:00", + "%Y-%m-%d %H:%M:%S"), + "updated_at": datetime.datetime.strptime("2010-11-11 11:00:00", + "%Y-%m-%d %H:%M:%S"), "admin_pass": "", "user_id": "", "project_id": "", -- cgit From e4e9d7550c2eb29c5d8fed3af0b9112976d262de Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 20:44:29 +0000 Subject: Update security gropu rules to properly support new format and boto 2.0 --- nova/api/ec2/cloud.py | 53 +++++++++++++++++++++++++++++++----------------- nova/tests/test_api.py | 8 +++++++- nova/tests/test_cloud.py | 19 ++++++++++++++++- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 10720a804..4b8c4cf80 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -539,15 +539,20 @@ class CloudController(object): return rules if 'ip_ranges' in kwargs: rules = self._cidr_args_split(kwargs) + else: + rules = [kwargs] finalset = [] for rule in rules: if 'groups' in rule: groups_values = self._groups_args_split(rule) for groups_value in groups_values: - finalset.append(groups_value) + final = self._rule_dict_last_step(context, **groups_value) + finalset.append(final) else: if rule: - finalset.append(rule) + final = self._rule_dict_last_step(context, **rule) + finalset.append(final) + LOG.warn(finalset) return finalset def _cidr_args_split(self, kwargs): @@ -590,6 +595,9 @@ class CloudController(object): db.security_group_get_by_name(context.elevated(), source_project_id, source_security_group_name) + notfound = exception.SecurityGroupNotFound + if not source_security_group: + raise notfound(security_group_id=source_security_group_name) values['group_id'] = source_security_group['id'] elif cidr_ip: # If this fails, it throws an exception. This is what we want. @@ -628,7 +636,7 @@ class CloudController(object): for rule in security_group.rules: if 'group_id' in values: if rule['group_id'] == values['group_id']: - return True + return rule['id'] else: is_duplicate = True for key in ('cidr', 'from_port', 'to_port', 'protocol'): @@ -636,7 +644,7 @@ class CloudController(object): is_duplicate = False break if is_duplicate: - return True + return rule['id'] return False def revoke_security_group_ingress(self, context, group_name=None, @@ -659,23 +667,30 @@ class CloudController(object): msg = "Revoke security group ingress %s" LOG.audit(_(msg), security_group['name'], context=context) + prevalues = [] + try: + prevalues = kwargs['ip_permissions'] + except KeyError: + prevalues.append(kwargs) + postvalues = [] + for values in prevalues: + rulesvalues = self._rule_args_to_dict(context, values) + if not rulesvalues: + err = "%s Not enough parameters to build a valid rule" + raise exception.ApiError(_(err % rulesvalues)) - criteria = self._rule_args_to_dict(context, kwargs)[0] - if criteria is None: - raise exception.ApiError(_("Not enough parameters to build a " - "valid rule.")) - - for rule in security_group.rules: - match = True - for (k, v) in criteria.iteritems(): - if getattr(rule, k, False) != v: - match = False - if match: - db.security_group_rule_destroy(context, rule['id']) - self.compute_api.trigger_security_group_rules_refresh(context, - security_group_id=security_group['id']) + rule_id = None + for values_for_rule in rulesvalues: + values_for_rule['parent_group_id'] = security_group.id + rule_id = self._security_group_rule_exists(security_group, + values_for_rule) + if rule_id: + db.security_group_rule_destroy(context, rule_id) + self.compute_api.trigger_security_group_rules_refresh(context, + security_group_id=security_group['id']) + if rule_id: return True - raise exception.ApiError(_("No rule for the specified parameters.")) + raise exception.ApiError(_("No rule for the specified parameters.")) # TODO(soren): This has only been tested with Boto as the client. # Unfortunately, it seems Boto is using an old API diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 26ac5ff24..cd5609834 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -213,7 +213,11 @@ class ApiEc2TestCase(test.TestCase): self.http = FakeHttplibConnection( self.app, '%s:8773' % (self.host), False) # pylint: disable=E1103 - self.ec2.new_http_connection(host, is_secure).AndReturn(self.http) + if boto.Version >= '2': + self.ec2.new_http_connection(host or '%s:8773' % (self.host), + is_secure).AndReturn(self.http) + else: + self.ec2.new_http_connection(host, is_secure).AndReturn(self.http) return self.http def test_return_valid_isoformat(self): @@ -400,6 +404,8 @@ class ApiEc2TestCase(test.TestCase): self.assertEquals(int(group.rules[0].from_port), 80) self.assertEquals(int(group.rules[0].to_port), 81) self.assertEquals(len(group.rules[0].grants), 1) + from nova import log + log.warn(group.rules[0].grants[0].__dict__) self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0') self.expect_http() diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 136082cc1..f87edc407 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -287,13 +287,30 @@ class CloudTestCase(test.TestCase): 'ip_protocol': u'tcp'}]} self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs)) - def test_authorize_security_group_ingress_ip_permissions_groups(self): + def test_authorize_security_group_fail_missing_source_group(self): kwargs = {'project_id': self.context.project_id, 'name': 'test'} sec = db.security_group_create(self.context, kwargs) authz = self.cloud.authorize_security_group_ingress kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81, 'ip_ranges':{'1': {'cidr_ip': u'0.0.0.0/0'}, '2': {'cidr_ip': u'10.10.10.10/32'}}, + 'groups': {'1': {'user_id': u'someuser', + 'group_name': u'somegroup1'}}, + 'ip_protocol': u'tcp'}]} + self.assertRaises(exception.SecurityGroupNotFound, authz, + self.context, group_name=sec['name'], **kwargs) + + def test_authorize_security_group_ingress_ip_permissions_groups(self): + kwargs = {'project_id': self.context.project_id, 'name': 'test'} + sec = db.security_group_create(self.context, + {'project_id': 'someuser', + 'name': 'somegroup1'}) + sec = db.security_group_create(self.context, + {'project_id': 'someuser', + 'name': 'othergroup2'}) + sec = db.security_group_create(self.context, kwargs) + authz = self.cloud.authorize_security_group_ingress + kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81, 'groups': {'1': {'user_id': u'someuser', 'group_name': u'somegroup1'}, '2': {'user_id': u'someuser', -- cgit From 5bd11c44f791cfe4c371b33cd1b1253013a8f836 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 20:49:46 +0000 Subject: remove some logging, remove extra if --- nova/api/ec2/cloud.py | 6 ++---- nova/tests/test_api.py | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4b8c4cf80..4045dfc61 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -549,10 +549,8 @@ class CloudController(object): final = self._rule_dict_last_step(context, **groups_value) finalset.append(final) else: - if rule: - final = self._rule_dict_last_step(context, **rule) - finalset.append(final) - LOG.warn(finalset) + final = self._rule_dict_last_step(context, **rule) + finalset.append(final) return finalset def _cidr_args_split(self, kwargs): diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index cd5609834..fe7fd8402 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -404,8 +404,6 @@ class ApiEc2TestCase(test.TestCase): self.assertEquals(int(group.rules[0].from_port), 80) self.assertEquals(int(group.rules[0].to_port), 81) self.assertEquals(len(group.rules[0].grants), 1) - from nova import log - log.warn(group.rules[0].grants[0].__dict__) self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0') self.expect_http() -- cgit From b97203f88c8e7926d32e8ddf664ba356869c9642 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Tue, 26 Jul 2011 16:55:02 -0400 Subject: pep8 --- nova/tests/api/openstack/test_servers.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index e154fb4fa..e355ac014 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1046,15 +1046,17 @@ class ServersTest(test.TestCase): def instance_create(context, inst): inst_type = instance_types.get_instance_type_by_flavor_id(3) image_ref = 'http://localhost/images/2' + created_at = datetime.datetime.strptime("2010-10-10 12:00:00", + "%Y-%m-%d %H:%M:%S"), + updated_at = datetime.datetime.strptime("2010-11-11 11:00:00", + "%Y-%m-%d %H:%M:%S"), return {'id': 1, 'display_name': 'server_test', 'uuid': FAKE_UUID, 'instance_type': dict(inst_type), 'image_ref': image_ref, - "created_at": datetime.datetime.strptime("2010-10-10 12:00:00", - "%Y-%m-%d %H:%M:%S"), - "updated_at": datetime.datetime.strptime("2010-11-11 11:00:00", - "%Y-%m-%d %H:%M:%S"), + "created_at": created_at, + "updated_at": updated_at, } def server_update(context, id, params): @@ -2904,12 +2906,14 @@ class ServersViewBuilderV11Test(test.TestCase): pass def _get_instance(self): + created_at = datetime.datetime.strptime("2010-10-10 12:00:00", + "%Y-%m-%d %H:%M:%S"), + updated_at = datetime.datetime.strptime("2010-11-11 11:00:00", + "%Y-%m-%d %H:%M:%S"), instance = { "id": 1, - "created_at": datetime.datetime.strptime("2010-10-10 12:00:00", - "%Y-%m-%d %H:%M:%S"), - "updated_at": datetime.datetime.strptime("2010-11-11 11:00:00", - "%Y-%m-%d %H:%M:%S"), + "created_at": created_at, + "updated_at": updated_at, "admin_pass": "", "user_id": "", "project_id": "", -- cgit From d4b2a2b3d552103414e4052773ac97939c66fa53 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 20:58:02 +0000 Subject: pep8 and simplify rule refresh logic --- nova/api/ec2/cloud.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4045dfc61..0294c09c5 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -670,25 +670,26 @@ class CloudController(object): prevalues = kwargs['ip_permissions'] except KeyError: prevalues.append(kwargs) - postvalues = [] + rule_id = None for values in prevalues: rulesvalues = self._rule_args_to_dict(context, values) if not rulesvalues: err = "%s Not enough parameters to build a valid rule" raise exception.ApiError(_(err % rulesvalues)) - rule_id = None for values_for_rule in rulesvalues: values_for_rule['parent_group_id'] = security_group.id rule_id = self._security_group_rule_exists(security_group, values_for_rule) if rule_id: db.security_group_rule_destroy(context, rule_id) - self.compute_api.trigger_security_group_rules_refresh(context, - security_group_id=security_group['id']) - if rule_id: - return True - raise exception.ApiError(_("No rule for the specified parameters.")) + if rule_id: + # NOTE(vish): we removed a rule, so refresh + self.compute_api.trigger_security_group_rules_refresh( + context, + security_group_id=security_group['id']) + return True + raise exception.ApiError(_("No rule for the specified parameters.")) # TODO(soren): This has only been tested with Boto as the client. # Unfortunately, it seems Boto is using an old API @@ -734,15 +735,17 @@ class CloudController(object): postvalues.append(values_for_rule) for values_for_rule in postvalues: - security_group_rule = db.security_group_rule_create(context, - values_for_rule) + security_group_rule = db.security_group_rule_create( + context, + values_for_rule) - self.compute_api.trigger_security_group_rules_refresh(context, - security_group_id=security_group['id']) + if postvalues: + self.compute_api.trigger_security_group_rules_refresh( + context, + security_group_id=security_group['id']) + return True - group = db.security_group_get_by_name(context, context.project_id, - security_group['name']) - return True + raise exception.ApiError(_("No rule for the specified parameters.")) def _get_source_project_id(self, context, source_security_group_owner_id): if source_security_group_owner_id: -- cgit From 9da71385e90a281483aac86a48e36a0e63bfe155 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Tue, 26 Jul 2011 17:18:27 -0400 Subject: cleanup --- nova/tests/api/openstack/test_servers.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index e355ac014..7c465314f 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -173,10 +173,8 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None, instance = { "id": int(id), - "created_at": datetime.datetime.strptime("2010-10-10 12:00:00", - "%Y-%m-%d %H:%M:%S"), - "updated_at": datetime.datetime.strptime("2010-11-11 11:00:00", - "%Y-%m-%d %H:%M:%S"), + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), "admin_pass": "", "user_id": user_id, "project_id": "", @@ -1046,17 +1044,13 @@ class ServersTest(test.TestCase): def instance_create(context, inst): inst_type = instance_types.get_instance_type_by_flavor_id(3) image_ref = 'http://localhost/images/2' - created_at = datetime.datetime.strptime("2010-10-10 12:00:00", - "%Y-%m-%d %H:%M:%S"), - updated_at = datetime.datetime.strptime("2010-11-11 11:00:00", - "%Y-%m-%d %H:%M:%S"), return {'id': 1, 'display_name': 'server_test', 'uuid': FAKE_UUID, 'instance_type': dict(inst_type), 'image_ref': image_ref, - "created_at": created_at, - "updated_at": updated_at, + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), } def server_update(context, id, params): @@ -2906,10 +2900,8 @@ class ServersViewBuilderV11Test(test.TestCase): pass def _get_instance(self): - created_at = datetime.datetime.strptime("2010-10-10 12:00:00", - "%Y-%m-%d %H:%M:%S"), - updated_at = datetime.datetime.strptime("2010-11-11 11:00:00", - "%Y-%m-%d %H:%M:%S"), + created_at = datetime.datetime(2010, 10, 10, 12, 0, 0) + updated_at = datetime.datetime(2010, 11, 11, 11, 0, 0) instance = { "id": 1, "created_at": created_at, -- cgit From 5c812bbdc15e0cf974026fcfaacd54e7a9f144cc Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 21:29:24 +0000 Subject: cherry-pick tr3buchet's fix for milestone branch --- nova/compute/manager.py | 2 +- nova/network/api.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 31627fe3b..893c18aef 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -868,7 +868,7 @@ class ComputeManager(manager.SchedulerDependentManager): """ self.network_api.add_fixed_ip_to_instance(context, instance_id, - network_id) + self.host, network_id) self.inject_network_info(context, instance_id) self.reset_network(context, instance_id) diff --git a/nova/network/api.py b/nova/network/api.py index 33a9fe239..247768722 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -164,9 +164,10 @@ class API(base.Base): {'method': 'deallocate_for_instance', 'args': args}) - def add_fixed_ip_to_instance(self, context, instance_id, network_id): + def add_fixed_ip_to_instance(self, context, instance_id, host, network_id): """Adds a fixed ip to instance from specified network.""" args = {'instance_id': instance_id, + 'host': host, 'network_id': network_id} rpc.cast(context, FLAGS.network_topic, {'method': 'add_fixed_ip_to_instance', -- cgit From 4f313565881608a19dd77be818e2c80c4e0e9c95 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 22:48:36 +0000 Subject: cloud tests all passing again --- nova/tests/test_cloud.py | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 136082cc1..927edb861 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -121,7 +121,6 @@ class CloudTestCase(test.TestCase): public_ip=address) db.floating_ip_destroy(self.context, address) - @test.skip_test("Skipping this pending future merge") def test_allocate_address(self): address = "10.10.10.10" allocate = self.cloud.allocate_address @@ -161,13 +160,10 @@ class CloudTestCase(test.TestCase): # ApiError: Floating ip is in use. Disassociate it before releasing. self.assertRaises(exception.ApiError, release, self.context, address) - @test.skip_test("Skipping this pending future merge") def test_associate_disassociate_address(self): """Verifies associate runs cleanly without raising an exception""" address = "10.10.10.10" - db.floating_ip_create(self.context, - {'address': address, - 'host': self.network.host}) + db.floating_ip_create(self.context, {'address': address}) self.cloud.allocate_address(self.context) # TODO(jkoelker) Probably need to query for instance_type_id and # make sure we get a valid one @@ -175,11 +171,14 @@ class CloudTestCase(test.TestCase): 'instance_type_id': 1}) networks = db.network_get_all(self.context) for network in networks: - self.network.set_network_host(self.context, network['id']) + db.network_update(self.context, network['id'], + {'host': self.network.host}) project_id = self.context.project_id type_id = inst['instance_type_id'] ips = self.network.allocate_for_instance(self.context, instance_id=inst['id'], + host=inst['host'], + vpn=None, instance_type_id=type_id, project_id=project_id) # TODO(jkoelker) Make this mas bueno @@ -405,8 +404,6 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, service1['id']) db.service_destroy(self.context, service2['id']) - # NOTE(jkoelker): this test relies on fixed_ip being in instances - @test.skip_test("EC2 stuff needs fixed_ip in instance_ref") def test_describe_snapshots(self): """Makes sure describe_snapshots works and filters results.""" vol = db.volume_create(self.context, {}) @@ -1041,12 +1038,6 @@ class CloudTestCase(test.TestCase): self.cloud.delete_key_pair(self.context, 'test') def test_run_instances(self): - # stub out the rpc call - def stub_cast(*args, **kwargs): - pass - - self.stubs.Set(rpc, 'cast', stub_cast) - kwargs = {'image_id': FLAGS.default_image, 'instance_type': FLAGS.default_instance_type, 'max_count': 1} @@ -1056,7 +1047,7 @@ class CloudTestCase(test.TestCase): self.assertEqual(instance['imageId'], 'ami-00000001') self.assertEqual(instance['displayName'], 'Server 1') self.assertEqual(instance['instanceId'], 'i-00000001') - self.assertEqual(instance['instanceState']['name'], 'scheduling') + self.assertEqual(instance['instanceState']['name'], 'running') self.assertEqual(instance['instanceType'], 'm1.small') def test_run_instances_image_state_none(self): @@ -1128,16 +1119,15 @@ class CloudTestCase(test.TestCase): self.assertEqual('c00l 1m4g3', inst['display_name']) db.instance_destroy(self.context, inst['id']) - # NOTE(jkoelker): This test relies on mac_address in instance - @test.skip_test("EC2 stuff needs mac_address in instance_ref") def test_update_of_instance_wont_update_private_fields(self): inst = db.instance_create(self.context, {}) + host = inst['host'] ec2_id = ec2utils.id_to_ec2_id(inst['id']) self.cloud.update_instance(self.context, ec2_id, display_name='c00l 1m4g3', - mac_address='DE:AD:BE:EF') + host='otherhost') inst = db.instance_get(self.context, inst['id']) - self.assertEqual(None, inst['mac_address']) + self.assertEqual(host, inst['host']) db.instance_destroy(self.context, inst['id']) def test_update_of_volume_display_fields(self): @@ -1193,7 +1183,6 @@ class CloudTestCase(test.TestCase): elevated = self.context.elevated(read_deleted=True) self._wait_for_state(elevated, instance_id, is_deleted) - @test.skip_test("skipping, test is hanging with multinic for rpc reasons") def test_stop_start_instance(self): """Makes sure stop/start instance works""" # enforce periodic tasks run in short time to avoid wait for 60s. @@ -1251,7 +1240,6 @@ class CloudTestCase(test.TestCase): self.assertEqual(vol['status'], "available") self.assertEqual(vol['attach_status'], "detached") - @test.skip_test("skipping, test is hanging with multinic for rpc reasons") def test_stop_start_with_volume(self): """Make sure run instance with block device mapping works""" @@ -1320,7 +1308,6 @@ class CloudTestCase(test.TestCase): self._restart_compute_service() - @test.skip_test("skipping, test is hanging with multinic for rpc reasons") def test_stop_with_attached_volume(self): """Make sure attach info is reflected to block device mapping""" # enforce periodic tasks run in short time to avoid wait for 60s. @@ -1396,7 +1383,6 @@ class CloudTestCase(test.TestCase): greenthread.sleep(0.3) return result['snapshotId'] - @test.skip_test("skipping, test is hanging with multinic for rpc reasons") def test_run_with_snapshot(self): """Makes sure run/stop/start instance with snapshot works.""" vol = self._volume_create() -- cgit From 534b8c3c5b2f6eb3d4c3545c3d5dc2d15061cf6e Mon Sep 17 00:00:00 2001 From: Zed Shaw Date: Tue, 26 Jul 2011 16:29:50 -0700 Subject: Implements a simplified messaging abstraction with the least amount of impact to the code base. --- nova/rpc.py | 598 ++---------------------------------------- nova/rpc_backends/__init__.py | 0 nova/rpc_backends/amqp.py | 591 +++++++++++++++++++++++++++++++++++++++++ nova/rpc_backends/common.py | 23 ++ nova/service.py | 28 +- nova/test.py | 16 -- nova/tests/test_adminapi.py | 2 +- nova/tests/test_cloud.py | 68 +---- nova/tests/test_rpc.py | 61 +---- nova/tests/test_rpc_amqp.py | 68 +++++ nova/tests/test_service.py | 170 ------------ nova/tests/test_test.py | 13 +- nova/utils.py | 11 + 13 files changed, 760 insertions(+), 889 deletions(-) create mode 100644 nova/rpc_backends/__init__.py create mode 100644 nova/rpc_backends/amqp.py create mode 100644 nova/rpc_backends/common.py create mode 100644 nova/tests/test_rpc_amqp.py diff --git a/nova/rpc.py b/nova/rpc.py index e2771ca88..8b0c6df67 100644 --- a/nova/rpc.py +++ b/nova/rpc.py @@ -16,597 +16,51 @@ # License for the specific language governing permissions and limitations # under the License. -"""AMQP-based RPC. -Queues have consumers and publishers. - -No fan-out support yet. - -""" - -import json -import sys -import time -import traceback -import types -import uuid - -from carrot import connection as carrot_connection -from carrot import messaging -from eventlet import greenpool -from eventlet import pools -from eventlet import queue -import greenlet - -from nova import context -from nova import exception -from nova import fakerabbit +from nova.utils import load_module +from nova.rpc_backends.common import RemoteError, LOG from nova import flags -from nova import log as logging -from nova import utils - - -LOG = logging.getLogger('nova.rpc') - FLAGS = flags.FLAGS -flags.DEFINE_integer('rpc_thread_pool_size', 1024, - 'Size of RPC thread pool') -flags.DEFINE_integer('rpc_conn_pool_size', 30, - 'Size of RPC connection pool') - - -class Connection(carrot_connection.BrokerConnection): - """Connection instance object.""" - - @classmethod - def instance(cls, new=True): - """Returns the instance.""" - if new or not hasattr(cls, '_instance'): - params = dict(hostname=FLAGS.rabbit_host, - port=FLAGS.rabbit_port, - ssl=FLAGS.rabbit_use_ssl, - userid=FLAGS.rabbit_userid, - password=FLAGS.rabbit_password, - virtual_host=FLAGS.rabbit_virtual_host) - - if FLAGS.fake_rabbit: - params['backend_cls'] = fakerabbit.Backend - - # NOTE(vish): magic is fun! - # pylint: disable=W0142 - if new: - return cls(**params) - else: - cls._instance = cls(**params) - return cls._instance - - @classmethod - def recreate(cls): - """Recreates the connection instance. - - This is necessary to recover from some network errors/disconnects. - - """ - try: - del cls._instance - except AttributeError, e: - # The _instance stuff is for testing purposes. Usually we don't use - # it. So don't freak out if it doesn't exist. - pass - return cls.instance() - - -class Pool(pools.Pool): - """Class that implements a Pool of Connections.""" - - # TODO(comstud): Timeout connections not used in a while - def create(self): - LOG.debug('Creating new connection') - return Connection.instance(new=True) - -# Create a ConnectionPool to use for RPC calls. We'll order the -# pool as a stack (LIFO), so that we can potentially loop through and -# timeout old unused connections at some point -ConnectionPool = Pool( - max_size=FLAGS.rpc_conn_pool_size, - order_as_stack=True) - - -class Consumer(messaging.Consumer): - """Consumer base class. - - Contains methods for connecting the fetch method to async loops. - - """ - - def __init__(self, *args, **kwargs): - for i in xrange(FLAGS.rabbit_max_retries): - if i > 0: - time.sleep(FLAGS.rabbit_retry_interval) - try: - super(Consumer, self).__init__(*args, **kwargs) - self.failed_connection = False - break - except Exception as e: # Catching all because carrot sucks - fl_host = FLAGS.rabbit_host - fl_port = FLAGS.rabbit_port - fl_intv = FLAGS.rabbit_retry_interval - LOG.error(_('AMQP server on %(fl_host)s:%(fl_port)d is' - ' unreachable: %(e)s. Trying again in %(fl_intv)d' - ' seconds.') % locals()) - self.failed_connection = True - if self.failed_connection: - LOG.error(_('Unable to connect to AMQP server ' - 'after %d tries. Shutting down.'), - FLAGS.rabbit_max_retries) - sys.exit(1) - - def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False): - """Wraps the parent fetch with some logic for failed connection.""" - # TODO(vish): the logic for failed connections and logging should be - # refactored into some sort of connection manager object - try: - if self.failed_connection: - # NOTE(vish): connection is defined in the parent class, we can - # recreate it as long as we create the backend too - # pylint: disable=W0201 - self.connection = Connection.recreate() - self.backend = self.connection.create_backend() - self.declare() - return super(Consumer, self).fetch(no_ack, - auto_ack, - enable_callbacks) - if self.failed_connection: - LOG.error(_('Reconnected to queue')) - self.failed_connection = False - # NOTE(vish): This is catching all errors because we really don't - # want exceptions to be logged 10 times a second if some - # persistent failure occurs. - except Exception, e: # pylint: disable=W0703 - if not self.failed_connection: - LOG.exception(_('Failed to fetch message from queue: %s' % e)) - self.failed_connection = True - - def attach_to_eventlet(self): - """Only needed for unit tests!""" - timer = utils.LoopingCall(self.fetch, enable_callbacks=True) - timer.start(0.1) - return timer - - -class AdapterConsumer(Consumer): - """Calls methods on a proxy object based on method and args.""" - - def __init__(self, connection=None, topic='broadcast', proxy=None): - LOG.debug(_('Initing the Adapter Consumer for %s') % topic) - self.proxy = proxy - self.pool = greenpool.GreenPool(FLAGS.rpc_thread_pool_size) - super(AdapterConsumer, self).__init__(connection=connection, - topic=topic) - self.register_callback(self.process_data) - - def process_data(self, message_data, message): - """Consumer callback to call a method on a proxy object. - - Parses the message for validity and fires off a thread to call the - proxy object method. - - Message data should be a dictionary with two keys: - method: string representing the method to call - args: dictionary of arg: value - - Example: {'method': 'echo', 'args': {'value': 42}} - - """ - LOG.debug(_('received %s') % message_data) - # This will be popped off in _unpack_context - msg_id = message_data.get('_msg_id', None) - ctxt = _unpack_context(message_data) - - method = message_data.get('method') - args = message_data.get('args', {}) - message.ack() - if not method: - # NOTE(vish): we may not want to ack here, but that means that bad - # messages stay in the queue indefinitely, so for now - # we just log the message and send an error string - # back to the caller - LOG.warn(_('no method for message: %s') % message_data) - if msg_id: - msg_reply(msg_id, - _('No method for message: %s') % message_data) - return - self.pool.spawn_n(self._process_data, msg_id, ctxt, method, args) - - @exception.wrap_exception() - def _process_data(self, msg_id, ctxt, method, args): - """Thread that maigcally looks for a method on the proxy - object and calls it. - """ - - node_func = getattr(self.proxy, str(method)) - node_args = dict((str(k), v) for k, v in args.iteritems()) - # NOTE(vish): magic is fun! - try: - rval = node_func(context=ctxt, **node_args) - if msg_id: - # Check if the result was a generator - if isinstance(rval, types.GeneratorType): - for x in rval: - msg_reply(msg_id, x, None) - else: - msg_reply(msg_id, rval, None) - - # This final None tells multicall that it is done. - msg_reply(msg_id, None, None) - elif isinstance(rval, types.GeneratorType): - # NOTE(vish): this iterates through the generator - list(rval) - except Exception as e: - logging.exception('Exception during message handling') - if msg_id: - msg_reply(msg_id, None, sys.exc_info()) - return - - -class TopicAdapterConsumer(AdapterConsumer): - """Consumes messages on a specific topic.""" - - exchange_type = 'topic' - - def __init__(self, connection=None, topic='broadcast', proxy=None): - self.queue = topic - self.routing_key = topic - self.exchange = FLAGS.control_exchange - self.durable = False - super(TopicAdapterConsumer, self).__init__(connection=connection, - topic=topic, proxy=proxy) - - -class FanoutAdapterConsumer(AdapterConsumer): - """Consumes messages from a fanout exchange.""" - - exchange_type = 'fanout' - - def __init__(self, connection=None, topic='broadcast', proxy=None): - self.exchange = '%s_fanout' % topic - self.routing_key = topic - unique = uuid.uuid4().hex - self.queue = '%s_fanout_%s' % (topic, unique) - self.durable = False - # Fanout creates unique queue names, so we should auto-remove - # them when done, so they're not left around on restart. - # Also, we're the only one that should be consuming. exclusive - # implies auto_delete, so we'll just set that.. - self.exclusive = True - LOG.info(_('Created "%(exchange)s" fanout exchange ' - 'with "%(key)s" routing key'), - dict(exchange=self.exchange, key=self.routing_key)) - super(FanoutAdapterConsumer, self).__init__(connection=connection, - topic=topic, proxy=proxy) - - -class ConsumerSet(object): - """Groups consumers to listen on together on a single connection.""" - - def __init__(self, connection, consumer_list): - self.consumer_list = set(consumer_list) - self.consumer_set = None - self.enabled = True - self.init(connection) - - def init(self, conn): - if not conn: - conn = Connection.instance(new=True) - if self.consumer_set: - self.consumer_set.close() - self.consumer_set = messaging.ConsumerSet(conn) - for consumer in self.consumer_list: - consumer.connection = conn - # consumer.backend is set for us - self.consumer_set.add_consumer(consumer) - - def reconnect(self): - self.init(None) - - def wait(self, limit=None): - running = True - while running: - it = self.consumer_set.iterconsume(limit=limit) - if not it: - break - while True: - try: - it.next() - except StopIteration: - return - except greenlet.GreenletExit: - running = False - break - except Exception as e: - LOG.exception(_("Exception while processing consumer")) - self.reconnect() - # Break to outer loop - break +flags.DEFINE_string('rpc_backend', + 'nova.rpc_backends.amqp', + "The messaging module to use, defaults to AMQP.") - def close(self): - self.consumer_set.close() +RPCIMPL = load_module(FLAGS.rpc_backend) -class Publisher(messaging.Publisher): - """Publisher base class.""" - pass +def create_connection(new=True): + return RPCIMPL.Connection.instance(new=True) -class TopicPublisher(Publisher): - """Publishes messages on a specific topic.""" +def create_consumer(conn, topic, proxy, fanout=False): + if fanout: + return RPCIMPL.FanoutAdapterConsumer( + connection=conn, + topic=topic, + proxy=proxy) + else: + return RPCIMPL.TopicAdapterConsumer( + connection=conn, + topic=topic, + proxy=proxy) - exchange_type = 'topic' - def __init__(self, connection=None, topic='broadcast'): - self.routing_key = topic - self.exchange = FLAGS.control_exchange - self.durable = False - super(TopicPublisher, self).__init__(connection=connection) - - -class FanoutPublisher(Publisher): - """Publishes messages to a fanout exchange.""" - - exchange_type = 'fanout' - - def __init__(self, topic, connection=None): - self.exchange = '%s_fanout' % topic - self.queue = '%s_fanout' % topic - self.durable = False - self.auto_delete = True - LOG.info(_('Creating "%(exchange)s" fanout exchange'), - dict(exchange=self.exchange)) - super(FanoutPublisher, self).__init__(connection=connection) - - -class DirectConsumer(Consumer): - """Consumes messages directly on a channel specified by msg_id.""" - - exchange_type = 'direct' - - def __init__(self, connection=None, msg_id=None): - self.queue = msg_id - self.routing_key = msg_id - self.exchange = msg_id - self.auto_delete = True - self.exclusive = True - super(DirectConsumer, self).__init__(connection=connection) - - -class DirectPublisher(Publisher): - """Publishes messages directly on a channel specified by msg_id.""" - - exchange_type = 'direct' - - def __init__(self, connection=None, msg_id=None): - self.routing_key = msg_id - self.exchange = msg_id - self.auto_delete = True - super(DirectPublisher, self).__init__(connection=connection) - - -def msg_reply(msg_id, reply=None, failure=None): - """Sends a reply or an error on the channel signified by msg_id. - - Failure should be a sys.exc_info() tuple. - - """ - if failure: - message = str(failure[1]) - tb = traceback.format_exception(*failure) - LOG.error(_("Returning exception %s to caller"), message) - LOG.error(tb) - failure = (failure[0].__name__, str(failure[1]), tb) - - with ConnectionPool.item() as conn: - publisher = DirectPublisher(connection=conn, msg_id=msg_id) - try: - publisher.send({'result': reply, 'failure': failure}) - except TypeError: - publisher.send( - {'result': dict((k, repr(v)) - for k, v in reply.__dict__.iteritems()), - 'failure': failure}) - - publisher.close() - - -class RemoteError(exception.Error): - """Signifies that a remote class has raised an exception. - - Containes a string representation of the type of the original exception, - the value of the original exception, and the traceback. These are - sent to the parent as a joined string so printing the exception - contains all of the relevent info. - - """ - - def __init__(self, exc_type, value, traceback): - self.exc_type = exc_type - self.value = value - self.traceback = traceback - super(RemoteError, self).__init__('%s %s\n%s' % (exc_type, - value, - traceback)) - - -def _unpack_context(msg): - """Unpack context from msg.""" - context_dict = {} - for key in list(msg.keys()): - # NOTE(vish): Some versions of python don't like unicode keys - # in kwargs. - key = str(key) - if key.startswith('_context_'): - value = msg.pop(key) - context_dict[key[9:]] = value - context_dict['msg_id'] = msg.pop('_msg_id', None) - LOG.debug(_('unpacked context: %s'), context_dict) - return RpcContext.from_dict(context_dict) - - -def _pack_context(msg, context): - """Pack context into msg. - - Values for message keys need to be less than 255 chars, so we pull - context out into a bunch of separate keys. If we want to support - more arguments in rabbit messages, we may want to do the same - for args at some point. - - """ - context_d = dict([('_context_%s' % key, value) - for (key, value) in context.to_dict().iteritems()]) - msg.update(context_d) - - -class RpcContext(context.RequestContext): - def __init__(self, *args, **kwargs): - msg_id = kwargs.pop('msg_id', None) - self.msg_id = msg_id - super(RpcContext, self).__init__(*args, **kwargs) - - def reply(self, *args, **kwargs): - msg_reply(self.msg_id, *args, **kwargs) - - -def multicall(context, topic, msg): - """Make a call that returns multiple times.""" - LOG.debug(_('Making asynchronous call on %s ...'), topic) - msg_id = uuid.uuid4().hex - msg.update({'_msg_id': msg_id}) - LOG.debug(_('MSG_ID is %s') % (msg_id)) - _pack_context(msg, context) - - con_conn = ConnectionPool.get() - consumer = DirectConsumer(connection=con_conn, msg_id=msg_id) - wait_msg = MulticallWaiter(consumer) - consumer.register_callback(wait_msg) - - publisher = TopicPublisher(connection=con_conn, topic=topic) - publisher.send(msg) - publisher.close() - - return wait_msg - - -class MulticallWaiter(object): - def __init__(self, consumer): - self._consumer = consumer - self._results = queue.Queue() - self._closed = False - - def close(self): - self._closed = True - self._consumer.close() - ConnectionPool.put(self._consumer.connection) - - def __call__(self, data, message): - """Acks message and sets result.""" - message.ack() - if data['failure']: - self._results.put(RemoteError(*data['failure'])) - else: - self._results.put(data['result']) - - def __iter__(self): - return self.wait() - - def wait(self): - while True: - rv = None - while rv is None and not self._closed: - try: - rv = self._consumer.fetch(enable_callbacks=True) - except Exception: - self.close() - raise - time.sleep(0.01) - - result = self._results.get() - if isinstance(result, Exception): - self.close() - raise result - if result == None: - self.close() - raise StopIteration - yield result +def create_consumer_set(conn, consumers): + return RPCIMPL.ConsumerSet(connection=conn, consumer_list=consumers) def call(context, topic, msg): - """Sends a message on a topic and wait for a response.""" - rv = multicall(context, topic, msg) - # NOTE(vish): return the last result from the multicall - rv = list(rv) - if not rv: - return - return rv[-1] + return RPCIMPL.call(context, topic, msg) def cast(context, topic, msg): - """Sends a message on a topic without waiting for a response.""" - LOG.debug(_('Making asynchronous cast on %s...'), topic) - _pack_context(msg, context) - with ConnectionPool.item() as conn: - publisher = TopicPublisher(connection=conn, topic=topic) - publisher.send(msg) - publisher.close() + return RPCIMPL.cast(context, topic, msg) def fanout_cast(context, topic, msg): - """Sends a message on a fanout exchange without waiting for a response.""" - LOG.debug(_('Making asynchronous fanout cast...')) - _pack_context(msg, context) - with ConnectionPool.item() as conn: - publisher = FanoutPublisher(topic, connection=conn) - publisher.send(msg) - publisher.close() - - -def generic_response(message_data, message): - """Logs a result and exits.""" - LOG.debug(_('response %s'), message_data) - message.ack() - sys.exit(0) + return RPCIMPL.fanout_cast(context, topic, msg) -def send_message(topic, message, wait=True): - """Sends a message for testing.""" - msg_id = uuid.uuid4().hex - message.update({'_msg_id': msg_id}) - LOG.debug(_('topic is %s'), topic) - LOG.debug(_('message %s'), message) - - if wait: - consumer = messaging.Consumer(connection=Connection.instance(), - queue=msg_id, - exchange=msg_id, - auto_delete=True, - exchange_type='direct', - routing_key=msg_id) - consumer.register_callback(generic_response) - - publisher = messaging.Publisher(connection=Connection.instance(), - exchange=FLAGS.control_exchange, - durable=False, - exchange_type='topic', - routing_key=topic) - publisher.send(message) - publisher.close() - - if wait: - consumer.wait() - consumer.close() - - -if __name__ == '__main__': - # You can send messages from the command line using - # topic and a json string representing a dictionary - # for the method - send_message(sys.argv[1], json.loads(sys.argv[2])) +def multicall(context, topic, msg): + return RPCIMPL.multicall(context, topic, msg) diff --git a/nova/rpc_backends/__init__.py b/nova/rpc_backends/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/nova/rpc_backends/amqp.py b/nova/rpc_backends/amqp.py new file mode 100644 index 000000000..efa178bd2 --- /dev/null +++ b/nova/rpc_backends/amqp.py @@ -0,0 +1,591 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""AMQP-based RPC. + +Queues have consumers and publishers. + +No fan-out support yet. + +""" + +import json +import sys +import time +import traceback +import types +import uuid + +from carrot import connection as carrot_connection +from carrot import messaging +from eventlet import greenpool +from eventlet import pools +from eventlet import queue +import greenlet + +from nova import context +from nova import exception +from nova import fakerabbit +from nova import flags +from nova import log as logging +from nova import utils +from nova.rpc_backends.common import RemoteError, LOG + + +FLAGS = flags.FLAGS +flags.DEFINE_integer('rpc_thread_pool_size', 1024, + 'Size of RPC thread pool') +flags.DEFINE_integer('rpc_conn_pool_size', 30, + 'Size of RPC connection pool') + + +class Connection(carrot_connection.BrokerConnection): + """Connection instance object.""" + + @classmethod + def instance(cls, new=True): + """Returns the instance.""" + if new or not hasattr(cls, '_instance'): + params = dict(hostname=FLAGS.rabbit_host, + port=FLAGS.rabbit_port, + ssl=FLAGS.rabbit_use_ssl, + userid=FLAGS.rabbit_userid, + password=FLAGS.rabbit_password, + virtual_host=FLAGS.rabbit_virtual_host) + + if FLAGS.fake_rabbit: + params['backend_cls'] = fakerabbit.Backend + + # NOTE(vish): magic is fun! + # pylint: disable=W0142 + if new: + return cls(**params) + else: + cls._instance = cls(**params) + return cls._instance + + @classmethod + def recreate(cls): + """Recreates the connection instance. + + This is necessary to recover from some network errors/disconnects. + + """ + try: + del cls._instance + except AttributeError, e: + # The _instance stuff is for testing purposes. Usually we don't use + # it. So don't freak out if it doesn't exist. + pass + return cls.instance() + + +class Pool(pools.Pool): + """Class that implements a Pool of Connections.""" + + # TODO(comstud): Timeout connections not used in a while + def create(self): + LOG.debug('Creating new connection') + return Connection.instance(new=True) + +# Create a ConnectionPool to use for RPC calls. We'll order the +# pool as a stack (LIFO), so that we can potentially loop through and +# timeout old unused connections at some point +ConnectionPool = Pool( + max_size=FLAGS.rpc_conn_pool_size, + order_as_stack=True) + + +class Consumer(messaging.Consumer): + """Consumer base class. + + Contains methods for connecting the fetch method to async loops. + + """ + + def __init__(self, *args, **kwargs): + for i in xrange(FLAGS.rabbit_max_retries): + if i > 0: + time.sleep(FLAGS.rabbit_retry_interval) + try: + super(Consumer, self).__init__(*args, **kwargs) + self.failed_connection = False + break + except Exception as e: # Catching all because carrot sucks + fl_host = FLAGS.rabbit_host + fl_port = FLAGS.rabbit_port + fl_intv = FLAGS.rabbit_retry_interval + LOG.error(_('AMQP server on %(fl_host)s:%(fl_port)d is' + ' unreachable: %(e)s. Trying again in %(fl_intv)d' + ' seconds.') % locals()) + self.failed_connection = True + if self.failed_connection: + LOG.error(_('Unable to connect to AMQP server ' + 'after %d tries. Shutting down.'), + FLAGS.rabbit_max_retries) + sys.exit(1) + + def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False): + """Wraps the parent fetch with some logic for failed connection.""" + # TODO(vish): the logic for failed connections and logging should be + # refactored into some sort of connection manager object + try: + if self.failed_connection: + # NOTE(vish): connection is defined in the parent class, we can + # recreate it as long as we create the backend too + # pylint: disable=W0201 + self.connection = Connection.recreate() + self.backend = self.connection.create_backend() + self.declare() + return super(Consumer, self).fetch(no_ack, + auto_ack, + enable_callbacks) + if self.failed_connection: + LOG.error(_('Reconnected to queue')) + self.failed_connection = False + # NOTE(vish): This is catching all errors because we really don't + # want exceptions to be logged 10 times a second if some + # persistent failure occurs. + except Exception, e: # pylint: disable=W0703 + if not self.failed_connection: + LOG.exception(_('Failed to fetch message from queue: %s' % e)) + self.failed_connection = True + + def attach_to_eventlet(self): + """Only needed for unit tests!""" + timer = utils.LoopingCall(self.fetch, enable_callbacks=True) + timer.start(0.1) + return timer + + +class AdapterConsumer(Consumer): + """Calls methods on a proxy object based on method and args.""" + + def __init__(self, connection=None, topic='broadcast', proxy=None): + LOG.debug(_('Initing the Adapter Consumer for %s') % topic) + self.proxy = proxy + self.pool = greenpool.GreenPool(FLAGS.rpc_thread_pool_size) + super(AdapterConsumer, self).__init__(connection=connection, + topic=topic) + self.register_callback(self.process_data) + + def process_data(self, message_data, message): + """Consumer callback to call a method on a proxy object. + + Parses the message for validity and fires off a thread to call the + proxy object method. + + Message data should be a dictionary with two keys: + method: string representing the method to call + args: dictionary of arg: value + + Example: {'method': 'echo', 'args': {'value': 42}} + + """ + LOG.debug(_('received %s') % message_data) + # This will be popped off in _unpack_context + msg_id = message_data.get('_msg_id', None) + ctxt = _unpack_context(message_data) + + method = message_data.get('method') + args = message_data.get('args', {}) + message.ack() + if not method: + # NOTE(vish): we may not want to ack here, but that means that bad + # messages stay in the queue indefinitely, so for now + # we just log the message and send an error string + # back to the caller + LOG.warn(_('no method for message: %s') % message_data) + if msg_id: + msg_reply(msg_id, + _('No method for message: %s') % message_data) + return + self.pool.spawn_n(self._process_data, msg_id, ctxt, method, args) + + @exception.wrap_exception() + def _process_data(self, msg_id, ctxt, method, args): + """Thread that maigcally looks for a method on the proxy + object and calls it. + """ + + node_func = getattr(self.proxy, str(method)) + node_args = dict((str(k), v) for k, v in args.iteritems()) + # NOTE(vish): magic is fun! + try: + rval = node_func(context=ctxt, **node_args) + if msg_id: + # Check if the result was a generator + if isinstance(rval, types.GeneratorType): + for x in rval: + msg_reply(msg_id, x, None) + else: + msg_reply(msg_id, rval, None) + + # This final None tells multicall that it is done. + msg_reply(msg_id, None, None) + elif isinstance(rval, types.GeneratorType): + # NOTE(vish): this iterates through the generator + list(rval) + except Exception as e: + logging.exception('Exception during message handling') + if msg_id: + msg_reply(msg_id, None, sys.exc_info()) + return + + +class TopicAdapterConsumer(AdapterConsumer): + """Consumes messages on a specific topic.""" + + exchange_type = 'topic' + + def __init__(self, connection=None, topic='broadcast', proxy=None): + self.queue = topic + self.routing_key = topic + self.exchange = FLAGS.control_exchange + self.durable = False + super(TopicAdapterConsumer, self).__init__(connection=connection, + topic=topic, proxy=proxy) + + +class FanoutAdapterConsumer(AdapterConsumer): + """Consumes messages from a fanout exchange.""" + + exchange_type = 'fanout' + + def __init__(self, connection=None, topic='broadcast', proxy=None): + self.exchange = '%s_fanout' % topic + self.routing_key = topic + unique = uuid.uuid4().hex + self.queue = '%s_fanout_%s' % (topic, unique) + self.durable = False + # Fanout creates unique queue names, so we should auto-remove + # them when done, so they're not left around on restart. + # Also, we're the only one that should be consuming. exclusive + # implies auto_delete, so we'll just set that.. + self.exclusive = True + LOG.info(_('Created "%(exchange)s" fanout exchange ' + 'with "%(key)s" routing key'), + dict(exchange=self.exchange, key=self.routing_key)) + super(FanoutAdapterConsumer, self).__init__(connection=connection, + topic=topic, proxy=proxy) + + +class ConsumerSet(object): + """Groups consumers to listen on together on a single connection.""" + + def __init__(self, connection, consumer_list): + self.consumer_list = set(consumer_list) + self.consumer_set = None + self.enabled = True + self.init(connection) + + def init(self, conn): + if not conn: + conn = Connection.instance(new=True) + if self.consumer_set: + self.consumer_set.close() + self.consumer_set = messaging.ConsumerSet(conn) + for consumer in self.consumer_list: + consumer.connection = conn + # consumer.backend is set for us + self.consumer_set.add_consumer(consumer) + + def reconnect(self): + self.init(None) + + def wait(self, limit=None): + running = True + while running: + it = self.consumer_set.iterconsume(limit=limit) + if not it: + break + while True: + try: + it.next() + except StopIteration: + return + except greenlet.GreenletExit: + running = False + break + except Exception as e: + LOG.exception(_("Exception while processing consumer")) + self.reconnect() + # Break to outer loop + break + + def close(self): + self.consumer_set.close() + + +class Publisher(messaging.Publisher): + """Publisher base class.""" + pass + + +class TopicPublisher(Publisher): + """Publishes messages on a specific topic.""" + + exchange_type = 'topic' + + def __init__(self, connection=None, topic='broadcast'): + self.routing_key = topic + self.exchange = FLAGS.control_exchange + self.durable = False + super(TopicPublisher, self).__init__(connection=connection) + + +class FanoutPublisher(Publisher): + """Publishes messages to a fanout exchange.""" + + exchange_type = 'fanout' + + def __init__(self, topic, connection=None): + self.exchange = '%s_fanout' % topic + self.queue = '%s_fanout' % topic + self.durable = False + self.auto_delete = True + LOG.info(_('Creating "%(exchange)s" fanout exchange'), + dict(exchange=self.exchange)) + super(FanoutPublisher, self).__init__(connection=connection) + + +class DirectConsumer(Consumer): + """Consumes messages directly on a channel specified by msg_id.""" + + exchange_type = 'direct' + + def __init__(self, connection=None, msg_id=None): + self.queue = msg_id + self.routing_key = msg_id + self.exchange = msg_id + self.auto_delete = True + self.exclusive = True + super(DirectConsumer, self).__init__(connection=connection) + + +class DirectPublisher(Publisher): + """Publishes messages directly on a channel specified by msg_id.""" + + exchange_type = 'direct' + + def __init__(self, connection=None, msg_id=None): + self.routing_key = msg_id + self.exchange = msg_id + self.auto_delete = True + super(DirectPublisher, self).__init__(connection=connection) + + +def msg_reply(msg_id, reply=None, failure=None): + """Sends a reply or an error on the channel signified by msg_id. + + Failure should be a sys.exc_info() tuple. + + """ + if failure: + message = str(failure[1]) + tb = traceback.format_exception(*failure) + LOG.error(_("Returning exception %s to caller"), message) + LOG.error(tb) + failure = (failure[0].__name__, str(failure[1]), tb) + + with ConnectionPool.item() as conn: + publisher = DirectPublisher(connection=conn, msg_id=msg_id) + try: + publisher.send({'result': reply, 'failure': failure}) + except TypeError: + publisher.send( + {'result': dict((k, repr(v)) + for k, v in reply.__dict__.iteritems()), + 'failure': failure}) + + publisher.close() + + +def _unpack_context(msg): + """Unpack context from msg.""" + context_dict = {} + for key in list(msg.keys()): + # NOTE(vish): Some versions of python don't like unicode keys + # in kwargs. + key = str(key) + if key.startswith('_context_'): + value = msg.pop(key) + context_dict[key[9:]] = value + context_dict['msg_id'] = msg.pop('_msg_id', None) + LOG.debug(_('unpacked context: %s'), context_dict) + return RpcContext.from_dict(context_dict) + + +def _pack_context(msg, context): + """Pack context into msg. + + Values for message keys need to be less than 255 chars, so we pull + context out into a bunch of separate keys. If we want to support + more arguments in rabbit messages, we may want to do the same + for args at some point. + + """ + context_d = dict([('_context_%s' % key, value) + for (key, value) in context.to_dict().iteritems()]) + msg.update(context_d) + + +class RpcContext(context.RequestContext): + def __init__(self, *args, **kwargs): + msg_id = kwargs.pop('msg_id', None) + self.msg_id = msg_id + super(RpcContext, self).__init__(*args, **kwargs) + + def reply(self, *args, **kwargs): + msg_reply(self.msg_id, *args, **kwargs) + + +def multicall(context, topic, msg): + """Make a call that returns multiple times.""" + LOG.debug(_('Making asynchronous call on %s ...'), topic) + msg_id = uuid.uuid4().hex + msg.update({'_msg_id': msg_id}) + LOG.debug(_('MSG_ID is %s') % (msg_id)) + _pack_context(msg, context) + + con_conn = ConnectionPool.get() + consumer = DirectConsumer(connection=con_conn, msg_id=msg_id) + wait_msg = MulticallWaiter(consumer) + consumer.register_callback(wait_msg) + + publisher = TopicPublisher(connection=con_conn, topic=topic) + publisher.send(msg) + publisher.close() + + return wait_msg + + +class MulticallWaiter(object): + def __init__(self, consumer): + self._consumer = consumer + self._results = queue.Queue() + self._closed = False + + def close(self): + self._closed = True + self._consumer.close() + ConnectionPool.put(self._consumer.connection) + + def __call__(self, data, message): + """Acks message and sets result.""" + message.ack() + if data['failure']: + self._results.put(RemoteError(*data['failure'])) + else: + self._results.put(data['result']) + + def __iter__(self): + return self.wait() + + def wait(self): + while True: + rv = None + while rv is None and not self._closed: + try: + rv = self._consumer.fetch(enable_callbacks=True) + except Exception: + self.close() + raise + time.sleep(0.01) + + result = self._results.get() + if isinstance(result, Exception): + self.close() + raise result + if result == None: + self.close() + raise StopIteration + yield result + + +def call(context, topic, msg): + """Sends a message on a topic and wait for a response.""" + rv = multicall(context, topic, msg) + # NOTE(vish): return the last result from the multicall + rv = list(rv) + if not rv: + return + return rv[-1] + + +def cast(context, topic, msg): + """Sends a message on a topic without waiting for a response.""" + LOG.debug(_('Making asynchronous cast on %s...'), topic) + _pack_context(msg, context) + with ConnectionPool.item() as conn: + publisher = TopicPublisher(connection=conn, topic=topic) + publisher.send(msg) + publisher.close() + + +def fanout_cast(context, topic, msg): + """Sends a message on a fanout exchange without waiting for a response.""" + LOG.debug(_('Making asynchronous fanout cast...')) + _pack_context(msg, context) + with ConnectionPool.item() as conn: + publisher = FanoutPublisher(topic, connection=conn) + publisher.send(msg) + publisher.close() + + +def generic_response(message_data, message): + """Logs a result and exits.""" + LOG.debug(_('response %s'), message_data) + message.ack() + sys.exit(0) + + +def send_message(topic, message, wait=True): + """Sends a message for testing.""" + msg_id = uuid.uuid4().hex + message.update({'_msg_id': msg_id}) + LOG.debug(_('topic is %s'), topic) + LOG.debug(_('message %s'), message) + + if wait: + consumer = messaging.Consumer(connection=Connection.instance(), + queue=msg_id, + exchange=msg_id, + auto_delete=True, + exchange_type='direct', + routing_key=msg_id) + consumer.register_callback(generic_response) + + publisher = messaging.Publisher(connection=Connection.instance(), + exchange=FLAGS.control_exchange, + durable=False, + exchange_type='topic', + routing_key=topic) + publisher.send(message) + publisher.close() + + if wait: + consumer.wait() + consumer.close() + + +if __name__ == '__main__': + # You can send messages from the command line using + # topic and a json string representing a dictionary + # for the method + send_message(sys.argv[1], json.loads(sys.argv[2])) diff --git a/nova/rpc_backends/common.py b/nova/rpc_backends/common.py new file mode 100644 index 000000000..1d3065a83 --- /dev/null +++ b/nova/rpc_backends/common.py @@ -0,0 +1,23 @@ +from nova import exception +from nova import log as logging + +LOG = logging.getLogger('nova.rpc') + + +class RemoteError(exception.Error): + """Signifies that a remote class has raised an exception. + + Containes a string representation of the type of the original exception, + the value of the original exception, and the traceback. These are + sent to the parent as a joined string so printing the exception + contains all of the relevent info. + + """ + + def __init__(self, exc_type, value, traceback): + self.exc_type = exc_type + self.value = value + self.traceback = traceback + super(RemoteError, self).__init__('%s %s\n%s' % (exc_type, + value, + traceback)) diff --git a/nova/service.py b/nova/service.py index 00e4f61e5..6e9eddc5a 100644 --- a/nova/service.py +++ b/nova/service.py @@ -149,26 +149,22 @@ class Service(object): if 'nova-compute' == self.binary: self.manager.update_available_resource(ctxt) - self.conn = rpc.Connection.instance(new=True) + self.conn = rpc.create_connection(new=True) logging.debug("Creating Consumer connection for Service %s" % self.topic) # Share this same connection for these Consumers - consumer_all = rpc.TopicAdapterConsumer( - connection=self.conn, - topic=self.topic, - proxy=self) - consumer_node = rpc.TopicAdapterConsumer( - connection=self.conn, - topic='%s.%s' % (self.topic, self.host), - proxy=self) - fanout = rpc.FanoutAdapterConsumer( - connection=self.conn, - topic=self.topic, - proxy=self) - consumer_set = rpc.ConsumerSet( - connection=self.conn, - consumer_list=[consumer_all, consumer_node, fanout]) + consumer_all = rpc.create_consumer(self.conn, self.topic, self, + fanout=False) + + node_topic = '%s.%s' % (self.topic, self.host) + consumer_node = rpc.create_consumer(self.conn, node_topic, self, + fanout=False) + + fanout = rpc.create_consumer(self.conn, self.topic, self, fanout=True) + + consumers = [consumer_all, consumer_node, fanout] + consumer_set = rpc.create_consumer_set(self.conn, consumers) # Wait forever, processing these consumers def _wait(): diff --git a/nova/test.py b/nova/test.py index 9790b0aa1..549aa6fcf 100644 --- a/nova/test.py +++ b/nova/test.py @@ -99,9 +99,7 @@ class TestCase(unittest.TestCase): self.flag_overrides = {} self.injected = [] self._services = [] - self._monkey_patch_attach() self._original_flags = FLAGS.FlagValuesDict() - rpc.ConnectionPool = rpc.Pool(max_size=FLAGS.rpc_conn_pool_size) def tearDown(self): """Runs after each test method to tear down test environment.""" @@ -126,9 +124,6 @@ class TestCase(unittest.TestCase): # Reset any overriden flags self.reset_flags() - # Reset our monkey-patches - rpc.Consumer.attach_to_eventlet = self.original_attach - # Stop any timers for x in self.injected: try: @@ -172,17 +167,6 @@ class TestCase(unittest.TestCase): self._services.append(svc) return svc - def _monkey_patch_attach(self): - self.original_attach = rpc.Consumer.attach_to_eventlet - - def _wrapped(inner_self): - rv = self.original_attach(inner_self) - self.injected.append(rv) - return rv - - _wrapped.func_name = self.original_attach.func_name - rpc.Consumer.attach_to_eventlet = _wrapped - # Useful assertions def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001): """Assert two dicts are equivalent. diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py index 877cf4ea1..6bbe15f53 100644 --- a/nova/tests/test_adminapi.py +++ b/nova/tests/test_adminapi.py @@ -39,7 +39,7 @@ class AdminApiTestCase(test.TestCase): super(AdminApiTestCase, self).setUp() self.flags(connection_type='fake') - self.conn = rpc.Connection.instance() + self.conn = rpc.create_connection() # set up our cloud self.api = admin.AdminController() diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 136082cc1..a1b296a96 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -50,7 +50,7 @@ class CloudTestCase(test.TestCase): self.flags(connection_type='fake', stub_network=True) - self.conn = rpc.Connection.instance() + self.conn = rpc.create_connection() # set up our cloud self.cloud = cloud.CloudController() @@ -269,63 +269,24 @@ class CloudTestCase(test.TestCase): delete = self.cloud.delete_security_group self.assertRaises(exception.ApiError, delete, self.context) - def test_authorize_security_group_ingress(self): + def test_authorize_revoke_security_group_ingress(self): kwargs = {'project_id': self.context.project_id, 'name': 'test'} sec = db.security_group_create(self.context, kwargs) authz = self.cloud.authorize_security_group_ingress kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'} - self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs)) - - def test_authorize_security_group_ingress_ip_permissions_ip_ranges(self): - kwargs = {'project_id': self.context.project_id, 'name': 'test'} - sec = db.security_group_create(self.context, kwargs) - authz = self.cloud.authorize_security_group_ingress - kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81, - 'ip_ranges': - {'1': {'cidr_ip': u'0.0.0.0/0'}, - '2': {'cidr_ip': u'10.10.10.10/32'}}, - 'ip_protocol': u'tcp'}]} - self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs)) - - def test_authorize_security_group_ingress_ip_permissions_groups(self): - kwargs = {'project_id': self.context.project_id, 'name': 'test'} - sec = db.security_group_create(self.context, kwargs) - authz = self.cloud.authorize_security_group_ingress - kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81, - 'ip_ranges':{'1': {'cidr_ip': u'0.0.0.0/0'}, - '2': {'cidr_ip': u'10.10.10.10/32'}}, - 'groups': {'1': {'user_id': u'someuser', - 'group_name': u'somegroup1'}, - '2': {'user_id': u'someuser', - 'group_name': u'othergroup2'}}, - 'ip_protocol': u'tcp'}]} - self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs)) - - def test_revoke_security_group_ingress(self): - kwargs = {'project_id': self.context.project_id, 'name': 'test'} - sec = db.security_group_create(self.context, kwargs) - authz = self.cloud.authorize_security_group_ingress - kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'} - authz(self.context, group_id=sec['id'], **kwargs) + authz(self.context, group_name=sec['name'], **kwargs) revoke = self.cloud.revoke_security_group_ingress self.assertTrue(revoke(self.context, group_name=sec['name'], **kwargs)) - def test_revoke_security_group_ingress_by_id(self): - kwargs = {'project_id': self.context.project_id, 'name': 'test'} - sec = db.security_group_create(self.context, kwargs) - authz = self.cloud.authorize_security_group_ingress - kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'} - authz(self.context, group_id=sec['id'], **kwargs) - revoke = self.cloud.revoke_security_group_ingress - self.assertTrue(revoke(self.context, group_id=sec['id'], **kwargs)) - - def test_authorize_security_group_ingress_by_id(self): + def test_authorize_revoke_security_group_ingress_by_id(self): sec = db.security_group_create(self.context, {'project_id': self.context.project_id, 'name': 'test'}) authz = self.cloud.authorize_security_group_ingress kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'} - self.assertTrue(authz(self.context, group_id=sec['id'], **kwargs)) + authz(self.context, group_id=sec['id'], **kwargs) + revoke = self.cloud.revoke_security_group_ingress + self.assertTrue(revoke(self.context, group_id=sec['id'], **kwargs)) def test_authorize_security_group_ingress_missing_protocol_params(self): sec = db.security_group_create(self.context, @@ -947,21 +908,6 @@ class CloudTestCase(test.TestCase): self._wait_for_running(ec2_instance_id) return ec2_instance_id - def test_rescue_unrescue_instance(self): - instance_id = self._run_instance( - image_id='ami-1', - instance_type=FLAGS.default_instance_type, - max_count=1) - self.cloud.rescue_instance(context=self.context, - instance_id=instance_id) - # NOTE(vish): This currently does no validation, it simply makes sure - # that the code path doesn't throw an exception. - self.cloud.unrescue_instance(context=self.context, - instance_id=instance_id) - # TODO(soren): We need this until we can stop polling in the rpc code - # for unit tests. - self.cloud.terminate_instances(self.context, [instance_id]) - def test_console_output(self): instance_id = self._run_instance( image_id='ami-1', diff --git a/nova/tests/test_rpc.py b/nova/tests/test_rpc.py index ffd748efe..2d2436175 100644 --- a/nova/tests/test_rpc.py +++ b/nova/tests/test_rpc.py @@ -33,11 +33,12 @@ LOG = logging.getLogger('nova.tests.rpc') class RpcTestCase(test.TestCase): def setUp(self): super(RpcTestCase, self).setUp() - self.conn = rpc.Connection.instance(True) + self.conn = rpc.create_connection(True) self.receiver = TestReceiver() - self.consumer = rpc.TopicAdapterConsumer(connection=self.conn, - topic='test', - proxy=self.receiver) + self.consumer = rpc.create_consumer(self.conn, + 'test', + self.receiver, + False) self.consumer.attach_to_eventlet() self.context = context.get_admin_context() @@ -129,6 +130,8 @@ class RpcTestCase(test.TestCase): """Calls echo in the passed queue""" LOG.debug(_("Nested received %(queue)s, %(value)s") % locals()) + # TODO: so, it will replay the context and use the same REQID? + # that's bizarre. ret = rpc.call(context, queue, {"method": "echo", @@ -137,10 +140,11 @@ class RpcTestCase(test.TestCase): return value nested = Nested() - conn = rpc.Connection.instance(True) - consumer = rpc.TopicAdapterConsumer(connection=conn, - topic='nested', - proxy=nested) + conn = rpc.create_connection(True) + consumer = rpc.create_consumer(conn, + 'nested', + nested, + False) consumer.attach_to_eventlet() value = 42 result = rpc.call(self.context, @@ -149,47 +153,6 @@ class RpcTestCase(test.TestCase): "value": value}}) self.assertEqual(value, result) - def test_connectionpool_single(self): - """Test that ConnectionPool recycles a single connection.""" - conn1 = rpc.ConnectionPool.get() - rpc.ConnectionPool.put(conn1) - conn2 = rpc.ConnectionPool.get() - rpc.ConnectionPool.put(conn2) - self.assertEqual(conn1, conn2) - - def test_connectionpool_double(self): - """Test that ConnectionPool returns and reuses separate connections. - - When called consecutively we should get separate connections and upon - returning them those connections should be reused for future calls - before generating a new connection. - - """ - conn1 = rpc.ConnectionPool.get() - conn2 = rpc.ConnectionPool.get() - - self.assertNotEqual(conn1, conn2) - rpc.ConnectionPool.put(conn1) - rpc.ConnectionPool.put(conn2) - - conn3 = rpc.ConnectionPool.get() - conn4 = rpc.ConnectionPool.get() - self.assertEqual(conn1, conn3) - self.assertEqual(conn2, conn4) - - def test_connectionpool_limit(self): - """Test connection pool limit and connection uniqueness.""" - max_size = FLAGS.rpc_conn_pool_size - conns = [] - - for i in xrange(max_size): - conns.append(rpc.ConnectionPool.get()) - - self.assertFalse(rpc.ConnectionPool.free_items) - self.assertEqual(rpc.ConnectionPool.current_size, - rpc.ConnectionPool.max_size) - self.assertEqual(len(set(conns)), max_size) - class TestReceiver(object): """Simple Proxy class so the consumer has methods to call. diff --git a/nova/tests/test_rpc_amqp.py b/nova/tests/test_rpc_amqp.py new file mode 100644 index 000000000..e3df2393a --- /dev/null +++ b/nova/tests/test_rpc_amqp.py @@ -0,0 +1,68 @@ +from nova import context +from nova import flags +from nova import log as logging +from nova import rpc +from nova.rpc_backends import amqp +from nova import test + + +FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.tests.rpc') + + +class RpcAMQPTestCase(test.TestCase): + def setUp(self): + super(RpcAMQPTestCase, self).setUp() + self.conn = rpc.create_connection(True) + self.receiver = TestReceiver() + self.consumer = rpc.create_consumer(self.conn, + 'test', + self.receiver, + False) + self.consumer.attach_to_eventlet() + self.context = context.get_admin_context() + + def test_connectionpool_single(self): + """Test that ConnectionPool recycles a single connection.""" + conn1 = amqp.ConnectionPool.get() + amqp.ConnectionPool.put(conn1) + conn2 = amqp.ConnectionPool.get() + amqp.ConnectionPool.put(conn2) + self.assertEqual(conn1, conn2) + + +class TestReceiver(object): + """Simple Proxy class so the consumer has methods to call. + + Uses static methods because we aren't actually storing any state. + + """ + + @staticmethod + def echo(context, value): + """Simply returns whatever value is sent in.""" + LOG.debug(_("Received %s"), value) + return value + + @staticmethod + def context(context, value): + """Returns dictionary version of context.""" + LOG.debug(_("Received %s"), context) + return context.to_dict() + + @staticmethod + def echo_three_times(context, value): + context.reply(value) + context.reply(value + 1) + context.reply(value + 2) + + @staticmethod + def echo_three_times_yield(context, value): + yield value + yield value + 1 + yield value + 2 + + @staticmethod + def fail(context, value): + """Raises an exception with the value sent in.""" + raise Exception(value) diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py index f45f76b73..bbf47b50f 100644 --- a/nova/tests/test_service.py +++ b/nova/tests/test_service.py @@ -109,103 +109,8 @@ class ServiceTestCase(test.TestCase): # the looping calls are created in StartService. app = service.Service.create(host=host, binary=binary, topic=topic) - self.mox.StubOutWithMock(service.rpc.Connection, 'instance') - service.rpc.Connection.instance(new=mox.IgnoreArg()) - - self.mox.StubOutWithMock(rpc, - 'TopicAdapterConsumer', - use_mock_anything=True) - self.mox.StubOutWithMock(rpc, - 'FanoutAdapterConsumer', - use_mock_anything=True) - - self.mox.StubOutWithMock(rpc, - 'ConsumerSet', - use_mock_anything=True) - - rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(), - topic=topic, - proxy=mox.IsA(service.Service)).AndReturn( - rpc.TopicAdapterConsumer) - - rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(), - topic='%s.%s' % (topic, host), - proxy=mox.IsA(service.Service)).AndReturn( - rpc.TopicAdapterConsumer) - - rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(), - topic=topic, - proxy=mox.IsA(service.Service)).AndReturn( - rpc.FanoutAdapterConsumer) - - def wait_func(self, limit=None): - return None - - mock_cset = self.mox.CreateMock(rpc.ConsumerSet, - {'wait': wait_func}) - rpc.ConsumerSet(connection=mox.IgnoreArg(), - consumer_list=mox.IsA(list)).AndReturn(mock_cset) - wait_func(mox.IgnoreArg()) - - service_create = {'host': host, - 'binary': binary, - 'topic': topic, - 'report_count': 0, - 'availability_zone': 'nova'} - service_ref = {'host': host, - 'binary': binary, - 'report_count': 0, - 'id': 1} - - service.db.service_get_by_args(mox.IgnoreArg(), - host, - binary).AndRaise(exception.NotFound()) - service.db.service_create(mox.IgnoreArg(), - service_create).AndReturn(service_ref) - self.mox.ReplayAll() - - app.start() - app.stop() self.assert_(app) - # We're testing sort of weird behavior in how report_state decides - # whether it is disconnected, it looks for a variable on itself called - # 'model_disconnected' and report_state doesn't really do much so this - # these are mostly just for coverage - def test_report_state_no_service(self): - host = 'foo' - binary = 'bar' - topic = 'test' - service_create = {'host': host, - 'binary': binary, - 'topic': topic, - 'report_count': 0, - 'availability_zone': 'nova'} - service_ref = {'host': host, - 'binary': binary, - 'topic': topic, - 'report_count': 0, - 'availability_zone': 'nova', - 'id': 1} - - service.db.service_get_by_args(mox.IgnoreArg(), - host, - binary).AndRaise(exception.NotFound()) - service.db.service_create(mox.IgnoreArg(), - service_create).AndReturn(service_ref) - service.db.service_get(mox.IgnoreArg(), - service_ref['id']).AndReturn(service_ref) - service.db.service_update(mox.IgnoreArg(), service_ref['id'], - mox.ContainsKeyValue('report_count', 1)) - - self.mox.ReplayAll() - serv = service.Service(host, - binary, - topic, - 'nova.tests.test_service.FakeManager') - serv.start() - serv.report_state() - def test_report_state_newly_disconnected(self): host = 'foo' binary = 'bar' @@ -276,81 +181,6 @@ class ServiceTestCase(test.TestCase): self.assert_(not serv.model_disconnected) - def test_compute_can_update_available_resource(self): - """Confirm compute updates their record of compute-service table.""" - host = 'foo' - binary = 'nova-compute' - topic = 'compute' - - # Any mocks are not working without UnsetStubs() here. - self.mox.UnsetStubs() - ctxt = context.get_admin_context() - service_ref = db.service_create(ctxt, {'host': host, - 'binary': binary, - 'topic': topic}) - serv = service.Service(host, - binary, - topic, - 'nova.compute.manager.ComputeManager') - - # This testcase want to test calling update_available_resource. - # No need to call periodic call, then below variable must be set 0. - serv.report_interval = 0 - serv.periodic_interval = 0 - - # Creating mocks - self.mox.StubOutWithMock(service.rpc.Connection, 'instance') - service.rpc.Connection.instance(new=mox.IgnoreArg()) - - self.mox.StubOutWithMock(rpc, - 'TopicAdapterConsumer', - use_mock_anything=True) - self.mox.StubOutWithMock(rpc, - 'FanoutAdapterConsumer', - use_mock_anything=True) - - self.mox.StubOutWithMock(rpc, - 'ConsumerSet', - use_mock_anything=True) - - rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(), - topic=topic, - proxy=mox.IsA(service.Service)).AndReturn( - rpc.TopicAdapterConsumer) - - rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(), - topic='%s.%s' % (topic, host), - proxy=mox.IsA(service.Service)).AndReturn( - rpc.TopicAdapterConsumer) - - rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(), - topic=topic, - proxy=mox.IsA(service.Service)).AndReturn( - rpc.FanoutAdapterConsumer) - - def wait_func(self, limit=None): - return None - - mock_cset = self.mox.CreateMock(rpc.ConsumerSet, - {'wait': wait_func}) - rpc.ConsumerSet(connection=mox.IgnoreArg(), - consumer_list=mox.IsA(list)).AndReturn(mock_cset) - wait_func(mox.IgnoreArg()) - - self.mox.StubOutWithMock(serv.manager.driver, - 'update_available_resource') - serv.manager.driver.update_available_resource(mox.IgnoreArg(), host) - - # Just doing start()-stop(), not confirm new db record is created, - # because update_available_resource() works only in - # libvirt environment. This testcase confirms - # update_available_resource() is called. Otherwise, mox complains. - self.mox.ReplayAll() - serv.start() - serv.stop() - - db.service_destroy(ctxt, service_ref['id']) - class TestWSGIService(test.TestCase): diff --git a/nova/tests/test_test.py b/nova/tests/test_test.py index 35c838065..64f11fa45 100644 --- a/nova/tests/test_test.py +++ b/nova/tests/test_test.py @@ -33,8 +33,13 @@ class IsolationTestCase(test.TestCase): self.start_service('compute') def test_rpc_consumer_isolation(self): - connection = rpc.Connection.instance(new=True) - consumer = rpc.TopicAdapterConsumer(connection, topic='compute') - consumer.register_callback( - lambda x, y: self.fail('I should never be called')) + class NeverCalled(object): + + def __getattribute__(*args): + assert False, "I should never get called." + + connection = rpc.create_connection(new=True) + proxy = NeverCalled() + consumer = rpc.create_consumer(connection, 'compute', + proxy, fanout=False) consumer.attach_to_eventlet() diff --git a/nova/utils.py b/nova/utils.py index 8784a227d..ad31f88bd 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -809,3 +809,14 @@ class Bootstrapper(object): for key in FLAGS: value = FLAGS.get(key, None) logging.audit(_("%(key)s : %(value)s" % locals())) + + +def load_module(name): + mod = __import__(name) + + components = name.split('.') + + for comp in components[1:]: + mod = getattr(mod, comp) + + return mod -- cgit From eaf8385dfc1bffff3d9a3af39f46deaec7cbb9c3 Mon Sep 17 00:00:00 2001 From: Zed Shaw Date: Tue, 26 Jul 2011 16:43:04 -0700 Subject: Add myself to authors. --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index 1a07946bd..e82070002 100644 --- a/Authors +++ b/Authors @@ -105,3 +105,4 @@ Yoshiaki Tamura Youcef Laribi Yuriy Taraday Zhixue Wu +Zed Shaw -- cgit From 54de340991c6c2dbd50d6efa4054102eef6df3a0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 27 Jul 2011 00:40:50 +0000 Subject: fix the skipped tests in vmwareapi xenapi and quota --- nova/tests/test_quota.py | 3 +- nova/tests/test_vmwareapi.py | 107 ++++++++++++++++------------------- nova/tests/test_xenapi.py | 56 +++++++++--------- nova/tests/vmwareapi/db_fakes.py | 10 ++-- nova/tests/vmwareapi/stubs.py | 7 +++ nova/virt/vmwareapi/fake.py | 10 ++++ nova/virt/vmwareapi/network_utils.py | 1 + nova/virt/vmwareapi/vm_util.py | 3 +- nova/virt/vmwareapi/vmops.py | 7 ++- nova/virt/xenapi/vmops.py | 2 +- 10 files changed, 110 insertions(+), 96 deletions(-) diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index 69d2deafe..a35caadf8 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -269,11 +269,10 @@ class QuotaTestCase(test.TestCase): for volume_id in volume_ids: db.volume_destroy(self.context, volume_id) - @test.skip_test def test_too_many_addresses(self): address = '192.168.0.100' db.floating_ip_create(context.get_admin_context(), - {'address': address, 'host': FLAGS.host, + {'address': address, 'project_id': self.project.id}) self.assertRaises(quota.QuotaError, self.network.allocate_floating_ip, diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py index cbf7801cf..7313508a6 100644 --- a/nova/tests/test_vmwareapi.py +++ b/nova/tests/test_vmwareapi.py @@ -19,9 +19,6 @@ Test suite for VMWareAPI. """ -import stubout - -from nova import context from nova import db from nova import flags from nova import test @@ -41,51 +38,66 @@ FLAGS = flags.FLAGS class VMWareAPIVMTestCase(test.TestCase): """Unit tests for Vmware API connection calls.""" - # NOTE(jkoelker): This is leaking stubs into the db module. - # Commenting out until updated for multi-nic. - #def setUp(self): - # super(VMWareAPIVMTestCase, self).setUp() - # self.flags(vmwareapi_host_ip='test_url', - # vmwareapi_host_username='test_username', - # vmwareapi_host_password='test_pass') - # self.manager = manager.AuthManager() - # self.user = self.manager.create_user('fake', 'fake', 'fake', - # admin=True) - # self.project = self.manager.create_project('fake', 'fake', 'fake') - # self.network = utils.import_object(FLAGS.network_manager) - # self.stubs = stubout.StubOutForTesting() - # vmwareapi_fake.reset() - # db_fakes.stub_out_db_instance_api(self.stubs) - # stubs.set_stubs(self.stubs) - # glance_stubs.stubout_glance_client(self.stubs, - # glance_stubs.FakeGlance) - # self.conn = vmwareapi_conn.get_connection(False) - - #def tearDown(self): - # super(VMWareAPIVMTestCase, self).tearDown() - # vmwareapi_fake.cleanup() - # self.manager.delete_project(self.project) - # self.manager.delete_user(self.user) - # self.stubs.UnsetAll() + def setUp(self): + super(VMWareAPIVMTestCase, self).setUp() + self.flags(vmwareapi_host_ip='test_url', + vmwareapi_host_username='test_username', + vmwareapi_host_password='test_pass') + self.manager = manager.AuthManager() + self.user = self.manager.create_user('fake', 'fake', 'fake', + admin=True) + self.project = self.manager.create_project('fake', 'fake', 'fake') + self.network = utils.import_object(FLAGS.network_manager) + vmwareapi_fake.reset() + db_fakes.stub_out_db_instance_api(self.stubs) + stubs.set_stubs(self.stubs) + glance_stubs.stubout_glance_client(self.stubs) + self.conn = vmwareapi_conn.get_connection(False) + # NOTE(vish): none of the network plugging code is actually + # being tested + self.network_info = [({'bridge': 'fa0', + 'id': 0, + 'vlan': None, + 'bridge_interface': None, + 'injected': True}, + {'broadcast': '192.168.0.255', + 'dns': ['192.168.0.1'], + 'gateway': '192.168.0.1', + 'gateway6': 'dead:beef::1', + 'ip6s': [{'enabled': '1', + 'ip': 'dead:beef::dcad:beff:feef:0', + 'netmask': '64'}], + 'ips': [{'enabled': '1', + 'ip': '192.168.0.100', + 'netmask': '255.255.255.0'}], + 'label': 'fake', + 'mac': 'DE:AD:BE:EF:00:00', + 'rxtx_cap': 3})] + + def tearDown(self): + super(VMWareAPIVMTestCase, self).tearDown() + vmwareapi_fake.cleanup() + self.manager.delete_project(self.project) + self.manager.delete_user(self.user) def _create_instance_in_the_db(self): values = {'name': 1, 'id': 1, 'project_id': self.project.id, 'user_id': self.user.id, - 'image_id': "1", + 'image_ref': "1", 'kernel_id': "1", 'ramdisk_id': "1", + 'mac_address': "de:ad:be:ef:be:ef", 'instance_type': 'm1.large', - 'mac_address': 'aa:bb:cc:dd:ee:ff', } - self.instance = db.instance_create(values) + self.instance = db.instance_create(None, values) def _create_vm(self): """Create and spawn the VM.""" self._create_instance_in_the_db() self.type_data = db.instance_type_get_by_name(None, 'm1.large') - self.conn.spawn(self.instance) + self.conn.spawn(self.instance, self.network_info) self._check_vm_record() def _check_vm_record(self): @@ -129,24 +141,20 @@ class VMWareAPIVMTestCase(test.TestCase): self.assertEquals(info["mem"], mem_kib) self.assertEquals(info["num_cpu"], self.type_data['vcpus']) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_list_instances(self): instances = self.conn.list_instances() self.assertEquals(len(instances), 0) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_list_instances_1(self): self._create_vm() instances = self.conn.list_instances() self.assertEquals(len(instances), 1) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_spawn(self): self._create_vm() info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_snapshot(self): self._create_vm() info = self.conn.get_info(1) @@ -155,27 +163,23 @@ class VMWareAPIVMTestCase(test.TestCase): info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_snapshot_non_existent(self): self._create_instance_in_the_db() self.assertRaises(Exception, self.conn.snapshot, self.instance, "Test-Snapshot") - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_reboot(self): self._create_vm() info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - self.conn.reboot(self.instance) + self.conn.reboot(self.instance, self.network_info) info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_reboot_non_existent(self): self._create_instance_in_the_db() self.assertRaises(Exception, self.conn.reboot, self.instance) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_reboot_not_poweredon(self): self._create_vm() info = self.conn.get_info(1) @@ -185,7 +189,6 @@ class VMWareAPIVMTestCase(test.TestCase): self._check_vm_info(info, power_state.PAUSED) self.assertRaises(Exception, self.conn.reboot, self.instance) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_suspend(self): self._create_vm() info = self.conn.get_info(1) @@ -194,13 +197,11 @@ class VMWareAPIVMTestCase(test.TestCase): info = self.conn.get_info(1) self._check_vm_info(info, power_state.PAUSED) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_suspend_non_existent(self): self._create_instance_in_the_db() self.assertRaises(Exception, self.conn.suspend, self.instance, self.dummy_callback_handler) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_resume(self): self._create_vm() info = self.conn.get_info(1) @@ -212,13 +213,11 @@ class VMWareAPIVMTestCase(test.TestCase): info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_resume_non_existent(self): self._create_instance_in_the_db() self.assertRaises(Exception, self.conn.resume, self.instance, self.dummy_callback_handler) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_resume_not_suspended(self): self._create_vm() info = self.conn.get_info(1) @@ -226,49 +225,41 @@ class VMWareAPIVMTestCase(test.TestCase): self.assertRaises(Exception, self.conn.resume, self.instance, self.dummy_callback_handler) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_get_info(self): self._create_vm() info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_destroy(self): self._create_vm() info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) instances = self.conn.list_instances() self.assertEquals(len(instances), 1) - self.conn.destroy(self.instance) + self.conn.destroy(self.instance, self.network_info) instances = self.conn.list_instances() self.assertEquals(len(instances), 0) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_destroy_non_existent(self): self._create_instance_in_the_db() - self.assertEquals(self.conn.destroy(self.instance), None) + self.assertEquals(self.conn.destroy(self.instance, self.network_info), + None) - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_pause(self): pass - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_unpause(self): pass - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_diagnostics(self): pass - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_get_console_output(self): pass - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def test_get_ajax_console(self): pass - @test.skip_test("DB stubbing not removed, needs updating for multi-nic") def dummy_callback_handler(self, ret): """ Dummy callback function to be passed to suspend, resume, etc., calls. diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 199a8bc52..4338eb7a0 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -396,18 +396,22 @@ class XenAPIVMTestCase(test.TestCase): def _test_spawn(self, image_ref, kernel_id, ramdisk_id, instance_type_id="3", os_type="linux", architecture="x86-64", instance_id=1, - check_injection=False): + check_injection=False, + create_record=True): stubs.stubout_loopingcall_start(self.stubs) - values = {'id': instance_id, - 'project_id': self.project.id, - 'user_id': self.user.id, - 'image_ref': image_ref, - 'kernel_id': kernel_id, - 'ramdisk_id': ramdisk_id, - 'instance_type_id': instance_type_id, - 'os_type': os_type, - 'architecture': architecture} - instance = db.instance_create(self.context, values) + if create_record: + values = {'id': instance_id, + 'project_id': self.project.id, + 'user_id': self.user.id, + 'image_ref': image_ref, + 'kernel_id': kernel_id, + 'ramdisk_id': ramdisk_id, + 'instance_type_id': instance_type_id, + 'os_type': os_type, + 'architecture': architecture} + instance = db.instance_create(self.context, values) + else: + instance = db.instance_get(self.context, instance_id) network_info = [({'bridge': 'fa0', 'id': 0, 'injected': True}, {'broadcast': '192.168.0.255', 'dns': ['192.168.0.1'], @@ -599,41 +603,38 @@ class XenAPIVMTestCase(test.TestCase): # guest agent is detected self.assertFalse(self._tee_executed) - @test.skip_test("Never gets an address, not sure why") def test_spawn_vlanmanager(self): self.flags(xenapi_image_service='glance', network_manager='nova.network.manager.VlanManager', - network_driver='nova.network.xenapi_net', vlan_interface='fake0') def dummy(*args, **kwargs): pass - self.stubs.Set(VMOps, 'create_vifs', dummy) + self.stubs.Set(vmops.VMOps, 'create_vifs', dummy) # Reset network table xenapi_fake.reset_table('network') # Instance id = 2 will use vlan network (see db/fakes.py) ctxt = self.context.elevated() - instance_ref = self._create_instance(2) - network_bk = self.network - # Ensure we use xenapi_net driver - self.network = utils.import_object(FLAGS.network_manager) + instance = self._create_instance(2, False) networks = self.network.db.network_get_all(ctxt) for network in networks: - self.network.set_network_host(ctxt, network['id']) - - self.network.allocate_for_instance(ctxt, instance_id=instance_ref.id, - instance_type_id=1, project_id=self.project.id) - self.network.setup_compute_network(ctxt, instance_ref.id) + self.network.set_network_host(ctxt, network) + + self.network.allocate_for_instance(ctxt, + instance_id=2, + host=FLAGS.host, + vpn=None, + instance_type_id=1, + project_id=self.project.id) self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE, glance_stubs.FakeGlance.IMAGE_KERNEL, glance_stubs.FakeGlance.IMAGE_RAMDISK, - instance_id=instance_ref.id, + instance_id=2, create_record=False) # TODO(salvatore-orlando): a complete test here would require # a check for making sure the bridge for the VM's VIF is # consistent with bridge specified in nova db - self.network = network_bk def test_spawn_with_network_qos(self): self._create_instance() @@ -663,7 +664,7 @@ class XenAPIVMTestCase(test.TestCase): self.vm = None self.stubs.UnsetAll() - def _create_instance(self, instance_id=1): + def _create_instance(self, instance_id=1, spawn=True): """Creates and spawns a test instance.""" stubs.stubout_loopingcall_start(self.stubs) values = { @@ -691,7 +692,8 @@ class XenAPIVMTestCase(test.TestCase): 'label': 'fake', 'mac': 'DE:AD:BE:EF:00:00', 'rxtx_cap': 3})] - self.conn.spawn(instance, network_info) + if spawn: + self.conn.spawn(instance, network_info) return instance diff --git a/nova/tests/vmwareapi/db_fakes.py b/nova/tests/vmwareapi/db_fakes.py index d4eb87daf..afd672c7a 100644 --- a/nova/tests/vmwareapi/db_fakes.py +++ b/nova/tests/vmwareapi/db_fakes.py @@ -70,8 +70,8 @@ def stub_out_db_instance_api(stubs): 'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), 'instance_type': values['instance_type'], 'memory_mb': type_data['memory_mb'], - 'mac_address': values['mac_address'], 'vcpus': type_data['vcpus'], + 'mac_addresses': [{'address': values['mac_address']}], 'local_gb': type_data['local_gb'], } return FakeModel(base_options) @@ -83,6 +83,8 @@ def stub_out_db_instance_api(stubs): 'bridge': 'vmnet0', 'netmask': '255.255.255.0', 'gateway': '10.10.10.1', + 'broadcast': '10.10.10.255', + 'dns1': 'fake', 'vlan': 100} return FakeModel(fields) @@ -90,7 +92,7 @@ def stub_out_db_instance_api(stubs): """Stubs out the db.instance_action_create method.""" pass - def fake_instance_get_fixed_address(context, instance_id): + def fake_instance_get_fixed_addresses(context, instance_id): """Stubs out the db.instance_get_fixed_address method.""" return '10.10.10.10' @@ -103,7 +105,7 @@ def stub_out_db_instance_api(stubs): stubs.Set(db, 'instance_create', fake_instance_create) stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) stubs.Set(db, 'instance_action_create', fake_instance_action_create) - stubs.Set(db, 'instance_get_fixed_address', - fake_instance_get_fixed_address) + stubs.Set(db, 'instance_get_fixed_addresses', + fake_instance_get_fixed_addresses) stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) diff --git a/nova/tests/vmwareapi/stubs.py b/nova/tests/vmwareapi/stubs.py index a648efb16..0ed5e9b68 100644 --- a/nova/tests/vmwareapi/stubs.py +++ b/nova/tests/vmwareapi/stubs.py @@ -22,6 +22,8 @@ Stubouts for the test suite from nova.virt import vmwareapi_conn from nova.virt.vmwareapi import fake from nova.virt.vmwareapi import vmware_images +from nova.virt.vmwareapi import vmops +from nova.virt.vmwareapi import network_utils def fake_get_vim_object(arg): @@ -36,10 +38,15 @@ def fake_is_vim_object(arg, module): def set_stubs(stubs): """Set the stubs.""" + stubs.Set(vmops.VMWareVMOps, 'plug_vifs', fake.fake_plug_vifs) + stubs.Set(network_utils, 'get_network_with_the_name', + fake.fake_get_network) stubs.Set(vmware_images, 'fetch_image', fake.fake_fetch_image) stubs.Set(vmware_images, 'get_vmdk_size_and_properties', fake.fake_get_vmdk_size_and_properties) stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image) + stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", + fake_get_vim_object) stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", fake_get_vim_object) stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object", diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 7370684bd..4c62d18bb 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -402,6 +402,16 @@ def _remove_file(file_path): lst_files.remove(file) +def fake_plug_vifs(*args, **kwargs): + """Fakes plugging vifs.""" + pass + + +def fake_get_network(*args, **kwargs): + """Fake get network.""" + return [{'type': 'fake'}] + + def fake_fetch_image(image, instance, **kwargs): """Fakes fetch image call. Just adds a reference to the db for the file.""" ds_name = kwargs.get("datastore_name") diff --git a/nova/virt/vmwareapi/network_utils.py b/nova/virt/vmwareapi/network_utils.py index 08e3bf0b1..ec3b93fe7 100644 --- a/nova/virt/vmwareapi/network_utils.py +++ b/nova/virt/vmwareapi/network_utils.py @@ -46,6 +46,7 @@ def get_network_with_the_name(session, network_name="vmnet0"): "get_properties_for_a_collection_of_objects", "Network", vm_networks, ["summary.name"]) network_obj = {} + LOG.warn(vm_networks) for network in vm_networks: # Get network properties if network._type == 'DistributedVirtualPortgroup': diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py index 55578dd3c..82b5f7214 100644 --- a/nova/virt/vmwareapi/vm_util.py +++ b/nova/virt/vmwareapi/vm_util.py @@ -110,7 +110,8 @@ def create_network_spec(client_factory, network_name, mac_address, # ephemeral. Invalid configuration if set to static and the NIC does # not come up on boot if set to dynamic. backing = None - if (network_ref['type'] == "DistributedVirtualPortgroup"): + if (network_ref and + network_ref['type'] == "DistributedVirtualPortgroup"): backing_name = \ 'ns0:VirtualEthernetCardDistributedVirtualPortBackingInfo' backing = \ diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 7e7d2dac3..1ee8fa1c0 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -734,13 +734,14 @@ class VMWareVMOps(object): net_mask = network["netmask"] gateway = network["gateway"] broadcast = network["broadcast"] - dns = network["dns"] + # TODO(vish): add support for dns2 + dns = network["dns1"] addresses = db.instance_get_fixed_addresses(admin_context, instance['id']) ip_addr = addresses[0] if addresses else None - machine_id_chanfge_spec = \ + machine_id_change_spec = \ vm_util.get_machine_id_change_spec(client_factory, mac_address, ip_addr, net_mask, gateway, broadcast, dns) @@ -750,7 +751,7 @@ class VMWareVMOps(object): 'ip_addr': ip_addr})) reconfig_task = self._session._call_method(self._session._get_vim(), "ReconfigVM_Task", vm_ref, - spec=machine_id_chanfge_spec) + spec=machine_id_change_spec) self._session._wait_for_task(instance.id, reconfig_task) LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id " "with ip - %(ip_addr)s") % diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0473abb97..6154cb9e7 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1092,7 +1092,7 @@ class VMOps(object): LOG.debug(_('Created VIF %(vif_ref)s for VM %(vm_ref)s,' ' network %(network_ref)s.') % locals()) - def plug_vifs(instance, network_info): + def plug_vifs(self, instance, network_info): """Set up VIF networking on the host.""" for (network, mapping) in network_info: self.vif_driver.plug(self._session, instance, network, mapping) -- cgit From fc87e8acff4ef3b0e048f66c2cddfb6880f1fd60 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 27 Jul 2011 01:01:43 +0000 Subject: fix the first round of missing data --- nova/tests/test_libvirt.py | 26 ++++++-------------------- nova/virt/libvirt/netutils.py | 1 + 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index ad0931a89..e01da1c79 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -280,7 +280,6 @@ class LibvirtConnTestCase(test.TestCase): return db.service_create(context.get_admin_context(), service_ref) - @test.skip_test("Please review this test to ensure intent") def test_preparing_xml_info(self): conn = connection.LibvirtConnection(True) instance_ref = db.instance_create(self.context, self.test_instance) @@ -296,27 +295,23 @@ class LibvirtConnTestCase(test.TestCase): _create_network_info(2)) self.assertTrue(len(result['nics']) == 2) - @test.skip_test("skipping libvirt tests depends on get_network_info shim") def test_xml_and_uri_no_ramdisk_no_kernel(self): instance_data = dict(self.test_instance) self._check_xml_and_uri(instance_data, expect_kernel=False, expect_ramdisk=False) - @test.skip_test("skipping libvirt tests depends on get_network_info shim") def test_xml_and_uri_no_ramdisk(self): instance_data = dict(self.test_instance) instance_data['kernel_id'] = 'aki-deadbeef' self._check_xml_and_uri(instance_data, expect_kernel=True, expect_ramdisk=False) - @test.skip_test("skipping libvirt tests depends on get_network_info shim") def test_xml_and_uri_no_kernel(self): instance_data = dict(self.test_instance) instance_data['ramdisk_id'] = 'ari-deadbeef' self._check_xml_and_uri(instance_data, expect_kernel=False, expect_ramdisk=False) - @test.skip_test("skipping libvirt tests depends on get_network_info shim") def test_xml_and_uri(self): instance_data = dict(self.test_instance) instance_data['ramdisk_id'] = 'ari-deadbeef' @@ -324,7 +319,6 @@ class LibvirtConnTestCase(test.TestCase): self._check_xml_and_uri(instance_data, expect_kernel=True, expect_ramdisk=True) - @test.skip_test("skipping libvirt tests depends on get_network_info shim") def test_xml_and_uri_rescue(self): instance_data = dict(self.test_instance) instance_data['ramdisk_id'] = 'ari-deadbeef' @@ -332,7 +326,6 @@ class LibvirtConnTestCase(test.TestCase): self._check_xml_and_uri(instance_data, expect_kernel=True, expect_ramdisk=True, rescue=True) - @test.skip_test("skipping libvirt tests depends on get_network_info shim") def test_lxc_container_and_uri(self): instance_data = dict(self.test_instance) self._check_xml_and_container(instance_data) @@ -743,7 +736,6 @@ class LibvirtConnTestCase(test.TestCase): db.volume_destroy(self.context, volume_ref['id']) db.instance_destroy(self.context, instance_ref['id']) - @test.skip_test("test needs rewrite: instance no longer has mac_address") def test_spawn_with_network_info(self): # Skip if non-libvirt environment if not self.lazy_load_library_exists(): @@ -895,7 +887,6 @@ class IptablesFirewallTestCase(test.TestCase): 'project_id': 'fake', 'instance_type_id': 1}) - @test.skip_test("skipping libvirt tests depends on get_network_info shim") def test_static_filters(self): instance_ref = self._create_instance_ref() ip = '10.11.12.13' @@ -1047,7 +1038,6 @@ class IptablesFirewallTestCase(test.TestCase): self.assertEquals(ipv6_network_rules, ipv6_rules_per_network * networks_count) - @test.skip_test("skipping libvirt tests") def test_do_refresh_security_group_rules(self): instance_ref = self._create_instance_ref() self.mox.StubOutWithMock(self.fw, @@ -1058,7 +1048,6 @@ class IptablesFirewallTestCase(test.TestCase): self.mox.ReplayAll() self.fw.do_refresh_security_group_rules("fake") - @test.skip_test("skip libvirt test project_get_network no longer exists") def test_unfilter_instance_undefines_nwfilter(self): # Skip if non-libvirt environment if not self.lazy_load_library_exists(): @@ -1092,7 +1081,6 @@ class IptablesFirewallTestCase(test.TestCase): db.instance_destroy(admin_ctxt, instance_ref['id']) - @test.skip_test("skip libvirt test project_get_network no longer exists") def test_provider_firewall_rules(self): # setup basic instance data instance_ref = self._create_instance_ref() @@ -1259,7 +1247,6 @@ class NWFilterTestCase(test.TestCase): inst.update(params) return db.instance_type_create(context, inst)['id'] - @test.skip_test('Skipping this test') def test_creates_base_rule_first(self): # These come pre-defined by libvirt self.defined_filters = ['no-mac-spoofing', @@ -1293,13 +1280,13 @@ class NWFilterTestCase(test.TestCase): ip = '10.11.12.13' - #network_ref = db.project_get_networks(self.context, 'fake')[0] - #fixed_ip = {'address': ip, 'network_id': network_ref['id']} + network_ref = db.project_get_networks(self.context, 'fake')[0] + fixed_ip = {'address': ip, 'network_id': network_ref['id']} - #admin_ctxt = context.get_admin_context() - #db.fixed_ip_create(admin_ctxt, fixed_ip) - #db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, - # 'instance_id': inst_id}) + admin_ctxt = context.get_admin_context() + db.fixed_ip_create(admin_ctxt, fixed_ip) + db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, + 'instance_id': inst_id}) self._setup_networking(instance_ref['id'], ip=ip) @@ -1336,7 +1323,6 @@ class NWFilterTestCase(test.TestCase): "fake") self.assertEquals(len(result), 3) - @test.skip_test("skip libvirt test project_get_network no longer exists") def test_unfilter_instance_undefines_nwfilters(self): admin_ctxt = context.get_admin_context() diff --git a/nova/virt/libvirt/netutils.py b/nova/virt/libvirt/netutils.py index 041eacb2d..019f4ce2b 100644 --- a/nova/virt/libvirt/netutils.py +++ b/nova/virt/libvirt/netutils.py @@ -89,6 +89,7 @@ def get_network_info(instance): 'label': network['label'], 'gateway': network['gateway'], 'broadcast': network['broadcast'], + 'dhcp_server': network['gateway'], 'mac': vif['address'], 'rxtx_cap': flavor['rxtx_cap'], 'dns': [], -- cgit From 6a757bbbbda208a7e141e56e2334c5a501645adb Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 22:55:58 -0700 Subject: fix more tests --- nova/tests/test_libvirt.py | 11 +++++++---- nova/virt/libvirt/netutils.py | 10 +++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index e01da1c79..377c92aca 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1065,11 +1065,13 @@ class IptablesFirewallTestCase(test.TestCase): instance = db.instance_get(self.context, inst_id) ip = '10.11.12.13' - network_ref = db.project_get_network(self.context, 'fake') + network_ref = db.project_get_networks(self.context, 'fake')[0] fixed_ip = {'address': ip, 'network_id': network_ref['id']} db.fixed_ip_create(admin_ctxt, fixed_ip) db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, 'instance_id': inst_id}) + + _setup_networking(inst_id, ip) self.fw.setup_basic_filtering(instance) self.fw.prepare_instance_filter(instance) self.fw.apply_instance_filter(instance) @@ -1086,7 +1088,7 @@ class IptablesFirewallTestCase(test.TestCase): instance_ref = self._create_instance_ref() nw_info = _create_network_info(1) ip = '10.11.12.13' - network_ref = db.project_get_network(self.context, 'fake') + network_ref = db.project_get_networks(self.context, 'fake')[0] admin_ctxt = context.get_admin_context() fixed_ip = {'address': ip, 'network_id': network_ref['id']} db.fixed_ip_create(admin_ctxt, fixed_ip) @@ -1288,7 +1290,7 @@ class NWFilterTestCase(test.TestCase): db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, 'instance_id': inst_id}) - self._setup_networking(instance_ref['id'], ip=ip) + _setup_networking(instance_ref['id'], ip=ip) def _ensure_all_called(): instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'], @@ -1341,7 +1343,7 @@ class NWFilterTestCase(test.TestCase): instance = db.instance_get(self.context, inst_id) ip = '10.11.12.13' - network_ref = db.project_get_network(self.context, 'fake') + network_ref = db.project_get_networks(self.context, 'fake')[0] fixed_ip = {'address': ip, 'network_id': network_ref['id']} db.fixed_ip_create(admin_ctxt, fixed_ip) db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, @@ -1350,6 +1352,7 @@ class NWFilterTestCase(test.TestCase): self.fw.prepare_instance_filter(instance) self.fw.apply_instance_filter(instance) original_filter_count = len(fakefilter.filters) + raise Exception(original_filter_count) self.fw.unfilter_instance(instance) # should undefine 2 filters: instance and instance-secgroup diff --git a/nova/virt/libvirt/netutils.py b/nova/virt/libvirt/netutils.py index 019f4ce2b..5f57387c6 100644 --- a/nova/virt/libvirt/netutils.py +++ b/nova/virt/libvirt/netutils.py @@ -25,6 +25,7 @@ import netaddr from nova import context from nova import db +from nova import exception from nova import flags from nova import ipv6 from nova import utils @@ -55,10 +56,13 @@ def get_network_info(instance): # we should cache network_info admin_context = context.get_admin_context() - fixed_ips = db.fixed_ip_get_by_instance(admin_context, instance['id']) + + try: + fixed_ips = db.fixed_ip_get_by_instance(admin_context, instance['id']) + except exception.FixedIpNotFoundForInstance: + fixed_ips = [] + vifs = db.virtual_interface_get_by_instance(admin_context, instance['id']) - networks = db.network_get_all_by_instance(admin_context, - instance['id']) flavor = db.instance_type_get(admin_context, instance['instance_type_id']) network_info = [] -- cgit From d64a54098bcecbb4c1001e99c1bf4c29b326265a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 23:13:03 -0700 Subject: cleanup network create --- nova/tests/test_libvirt.py | 95 +++++++++------------------------------------- 1 file changed, 18 insertions(+), 77 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 377c92aca..d12bd34f7 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -88,7 +88,7 @@ def _setup_networking(instance_id, ip='1.2.3.4'): 'virtual_interface_id': vif_ref['id']} db.fixed_ip_create(ctxt, fixed_ip) db.fixed_ip_update(ctxt, ip, {'allocated': True, - 'instance_id': instance_id}) + 'instance_id': instance_id}) class CacheConcurrencyTestCase(test.TestCase): @@ -177,13 +177,13 @@ class LibvirtConnTestCase(test.TestCase): self.context = context.get_admin_context() FLAGS.instances_path = '' self.call_libvirt_dependant_setup = False + self.test_ip = '10.11.12.13' def tearDown(self): self.manager.delete_project(self.project) self.manager.delete_user(self.user) super(LibvirtConnTestCase, self).tearDown() - test_ip = '10.11.12.13' test_instance = {'memory_kb': '1024000', 'basepath': '/some/path', 'bridge_name': 'br100', @@ -425,24 +425,7 @@ class LibvirtConnTestCase(test.TestCase): user_context = context.RequestContext(project=self.project, user=self.user) instance_ref = db.instance_create(user_context, instance) - # Re-get the instance so it's bound to an actual session - instance_ref = db.instance_get(user_context, instance_ref['id']) - network_ref = db.project_get_networks(context.get_admin_context(), - self.project.id)[0] - - vif = {'address': '56:12:12:12:12:12', - 'network_id': network_ref['id'], - 'instance_id': instance_ref['id']} - vif_ref = db.virtual_interface_create(self.context, vif) - fixed_ip = {'address': self.test_ip, - 'network_id': network_ref['id'], - 'virtual_interface_id': vif_ref['id']} - - ctxt = context.get_admin_context() - fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip) - db.fixed_ip_update(ctxt, self.test_ip, - {'allocated': True, - 'instance_id': instance_ref['id']}) + _setup_networking(instance_ref['id'], self.test_ip) self.flags(libvirt_type='lxc') conn = connection.LibvirtConnection(True) @@ -474,7 +457,7 @@ class LibvirtConnTestCase(test.TestCase): network_ref = db.project_get_networks(context.get_admin_context(), self.project.id)[0] - _setup_networking(instance_ref['id'], ip=self.test_ip) + _setup_networking(instance_ref['id'], self.test_ip) type_uri_map = {'qemu': ('qemu:///system', [(lambda t: t.find('.').get('type'), 'qemu'), @@ -822,6 +805,7 @@ class IptablesFirewallTestCase(test.TestCase): """setup_basic_rules in nwfilter calls this.""" pass self.fake_libvirt_connection = FakeLibvirtConnection() + self.test_ip = '10.11.12.13' self.fw = firewall.IptablesFirewallDriver( get_connection=lambda: self.fake_libvirt_connection) @@ -889,24 +873,9 @@ class IptablesFirewallTestCase(test.TestCase): def test_static_filters(self): instance_ref = self._create_instance_ref() - ip = '10.11.12.13' - - network_ref = db.project_get_networks(self.context, - 'fake', - associate=True)[0] - vif = {'address': '56:12:12:12:12:12', - 'network_id': network_ref['id'], - 'instance_id': instance_ref['id']} - vif_ref = db.virtual_interface_create(self.context, vif) - - fixed_ip = {'address': ip, - 'network_id': network_ref['id'], - 'virtual_interface_id': vif_ref['id']} - admin_ctxt = context.get_admin_context() - db.fixed_ip_create(admin_ctxt, fixed_ip) - db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, - 'instance_id': instance_ref['id']}) + _setup_networking(instance_ref['id'], self.test_ip) + admin_ctxt = context.get_admin_context() secgroup = db.security_group_create(admin_ctxt, {'user_id': 'fake', 'project_id': 'fake', @@ -1061,22 +1030,13 @@ class IptablesFirewallTestCase(test.TestCase): self.fw.nwfilter._conn.nwfilterLookupByName =\ fakefilter.nwfilterLookupByName instance_ref = self._create_instance_ref() - inst_id = instance_ref['id'] - instance = db.instance_get(self.context, inst_id) - - ip = '10.11.12.13' - network_ref = db.project_get_networks(self.context, 'fake')[0] - fixed_ip = {'address': ip, 'network_id': network_ref['id']} - db.fixed_ip_create(admin_ctxt, fixed_ip) - db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, - 'instance_id': inst_id}) - _setup_networking(inst_id, ip) - self.fw.setup_basic_filtering(instance) - self.fw.prepare_instance_filter(instance) - self.fw.apply_instance_filter(instance) + _setup_networking(instance_ref['id'], self.test_ip) + self.fw.setup_basic_filtering(instance_ref) + self.fw.prepare_instance_filter(instance_ref) + self.fw.apply_instance_filter(instance_ref) original_filter_count = len(fakefilter.filters) - self.fw.unfilter_instance(instance) + self.fw.unfilter_instance(instance_ref) # should undefine just the instance filter self.assertEqual(original_filter_count - len(fakefilter.filters), 1) @@ -1087,13 +1047,7 @@ class IptablesFirewallTestCase(test.TestCase): # setup basic instance data instance_ref = self._create_instance_ref() nw_info = _create_network_info(1) - ip = '10.11.12.13' - network_ref = db.project_get_networks(self.context, 'fake')[0] - admin_ctxt = context.get_admin_context() - fixed_ip = {'address': ip, 'network_id': network_ref['id']} - db.fixed_ip_create(admin_ctxt, fixed_ip) - db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, - 'instance_id': instance_ref['id']}) + _setup_networking(instance_ref['id'], self.test_ip) # FRAGILE: peeks at how the firewall names chains chain_name = 'inst-%s' % instance_ref['id'] @@ -1105,6 +1059,7 @@ class IptablesFirewallTestCase(test.TestCase): if rule.chain == 'provider'] self.assertEqual(0, len(rules)) + admin_ctxt = context.get_admin_context() # add a rule and send the update message, check for 1 rule provider_fw0 = db.provider_fw_rule_create(admin_ctxt, {'protocol': 'tcp', @@ -1163,6 +1118,7 @@ class NWFilterTestCase(test.TestCase): self.fake_libvirt_connection = Mock() + self.test_ip = '10.11.12.13' self.fw = firewall.NWFilterFirewall( lambda: self.fake_libvirt_connection) @@ -1280,17 +1236,7 @@ class NWFilterTestCase(test.TestCase): instance_ref = self._create_instance() inst_id = instance_ref['id'] - ip = '10.11.12.13' - - network_ref = db.project_get_networks(self.context, 'fake')[0] - fixed_ip = {'address': ip, 'network_id': network_ref['id']} - - admin_ctxt = context.get_admin_context() - db.fixed_ip_create(admin_ctxt, fixed_ip) - db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, - 'instance_id': inst_id}) - - _setup_networking(instance_ref['id'], ip=ip) + _setup_networking(instance_ref['id'], self.test_ip) def _ensure_all_called(): instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'], @@ -1315,7 +1261,7 @@ class NWFilterTestCase(test.TestCase): self.fw.apply_instance_filter(instance) _ensure_all_called() self.teardown_security_group() - db.instance_destroy(admin_ctxt, instance_ref['id']) + db.instance_destroy(context.get_admin_context, instance_ref['id']) def test_create_network_filters(self): instance_ref = self._create_instance() @@ -1342,12 +1288,7 @@ class NWFilterTestCase(test.TestCase): instance = db.instance_get(self.context, inst_id) - ip = '10.11.12.13' - network_ref = db.project_get_networks(self.context, 'fake')[0] - fixed_ip = {'address': ip, 'network_id': network_ref['id']} - db.fixed_ip_create(admin_ctxt, fixed_ip) - db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, - 'instance_id': inst_id}) + _setup_networking(instance_ref['id'], self.test_ip) self.fw.setup_basic_filtering(instance) self.fw.prepare_instance_filter(instance) self.fw.apply_instance_filter(instance) -- cgit From f1f191c381a43af1f8dc87246e7c4973cf50f78e Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 23:22:50 -0700 Subject: couple more fixes --- nova/tests/test_libvirt.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index d12bd34f7..084fb5ca7 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -737,17 +737,7 @@ class LibvirtConnTestCase(test.TestCase): conn.firewall_driver.setattr('setup_basic_filtering', fake_none) conn.firewall_driver.setattr('prepare_instance_filter', fake_none) - network = db.project_get_networks(context.get_admin_context(), - self.project.id)[0] - ip_dict = {'ip': self.test_ip, - 'netmask': network['netmask'], - 'enabled': '1'} - mapping = {'label': network['label'], - 'gateway': network['gateway'], - 'mac': instance['mac_address'], - 'dns': [network['dns']], - 'ips': [ip_dict]} - network_info = [(network, mapping)] + network_info = _create_network_info() try: conn.spawn(instance, network_info) @@ -1293,7 +1283,6 @@ class NWFilterTestCase(test.TestCase): self.fw.prepare_instance_filter(instance) self.fw.apply_instance_filter(instance) original_filter_count = len(fakefilter.filters) - raise Exception(original_filter_count) self.fw.unfilter_instance(instance) # should undefine 2 filters: instance and instance-secgroup -- cgit From 8c40dba0785dadf479e232c26023def4c5a38761 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 23:33:53 -0700 Subject: fake plug for vif driver --- nova/tests/test_libvirt.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 084fb5ca7..65c06a06c 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -232,6 +232,16 @@ class LibvirtConnTestCase(test.TestCase): def setattr(self, key, val): self.__setattr__(key, val) + def plug(self, instance, network, mapping): + return { + 'id': 'fake', + 'bridge_name': 'fake', + 'mac_address': 'fake', + 'ip_address': 'fake', + 'dhcp_server': 'fake', + 'extra_params': 'fake' + } + # Creating mocks fake = FakeLibvirtConnection() fakeip = FakeIptablesFirewallDriver -- cgit From 32e17ebeac99510dd155ce5560192ee6de9d83ab Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 23:41:15 -0700 Subject: fix the last of them --- nova/tests/test_libvirt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 65c06a06c..452e608d4 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1240,7 +1240,7 @@ class NWFilterTestCase(test.TestCase): def _ensure_all_called(): instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'], - '00A0C914C829') + '561212121212') secgroup_filter = 'nova-secgroup-%s' % self.security_group['id'] for required in [secgroup_filter, 'allow-dhcp-server', 'no-arp-spoofing', 'no-ip-spoofing', @@ -1261,7 +1261,7 @@ class NWFilterTestCase(test.TestCase): self.fw.apply_instance_filter(instance) _ensure_all_called() self.teardown_security_group() - db.instance_destroy(context.get_admin_context, instance_ref['id']) + db.instance_destroy(context.get_admin_context(), instance_ref['id']) def test_create_network_filters(self): instance_ref = self._create_instance() -- cgit From 30eea9c5f15775c96046091c874ee34bf366d566 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 26 Jul 2011 23:50:49 -0700 Subject: fix typo in attach_volume --- nova/virt/libvirt/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 96f9c41f9..cc51449c1 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -351,7 +351,7 @@ class LibvirtConnection(driver.ComputeDriver): virt_dom = self._lookup_by_name(instance_name) mount_device = mountpoint.rpartition("/")[2] (type, protocol, name) = \ - self._get_volume_device_info(vol['device_path']) + self._get_volume_device_info(device_path) if type == 'block': xml = """ -- cgit From 9008b1f291ae38a4de9b5af5087b1815b3562e3f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 27 Jul 2011 09:19:45 -0700 Subject: add invalid device test and make sure NovaExceptions don't get wrapped --- nova/exception.py | 3 ++- nova/tests/test_libvirt.py | 9 +++++++++ nova/virt/libvirt/connection.py | 4 ---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/nova/exception.py b/nova/exception.py index 38e705417..ea046b712 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -116,7 +116,8 @@ def wrap_exception(notifier=None, publisher_id=None, event_type=None, notifier.notify(publisher_id, temp_type, temp_level, payload) - if not isinstance(e, Error): + if (not isinstance(e, Error) and + not isinstance(e, NovaException)): #exc_type, exc_value, exc_traceback = sys.exc_info() LOG.exception(_('Uncaught exception')) #logging.error(traceback.extract_stack(exc_traceback)) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index ad0931a89..9a42556c2 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -412,6 +412,15 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['name'], snapshot_name) + def test_attach_invalid_device(self): + self.create_fake_libvirt_mock() + connection.LibvirtConnection._conn.lookupByName = self.fake_lookup + self.mox.ReplayAll() + conn = connection.LibvirtConnection(False) + self.assertRaises(exception.InvalidDevicePath, + conn.attach_volume, + "fake", "bad/device/path", "/dev/fake") + def test_multi_nic(self): instance_data = dict(self.test_instance) network_info = _create_network_info(2) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index cc51449c1..c27e92feb 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -364,9 +364,6 @@ class LibvirtConnection(driver.ComputeDriver): """ % (protocol, name, mount_device) - else: - raise exception.InvalidDevicePath(path=device_path) - virt_dom.attachDevice(xml) def _get_disk_xml(self, xml, device): @@ -955,7 +952,6 @@ class LibvirtConnection(driver.ComputeDriver): return True return False - @exception.wrap_exception def _get_volume_device_info(self, device_path): if device_path.startswith('/dev/'): return ('block', None, None) -- cgit From a0b536064620e4d18ab00c1154ec3b597ab16a67 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 27 Jul 2011 11:44:14 -0500 Subject: updated nova-manage create network. better help, handling of required args, and exceptions. Also updated FLAG flat_network_bridge to default to None --- bin/nova-manage | 89 ++++++++++++++++++++++++++---------------------- nova/exception.py | 4 +++ nova/network/manager.py | 8 ++--- nova/tests/fake_flags.py | 1 + nova/virt/libvirt/vif.py | 2 +- 5 files changed, 59 insertions(+), 45 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index b63bd326f..c1a5d8c5f 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -631,21 +631,24 @@ class NetworkCommands(object): """Class for managing networks.""" @args('--label', dest="label", metavar='