From 5ef94944514c3f81e31cc60d3d63b903859dca45 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Thu, 18 Aug 2011 22:15:13 -0700 Subject: add user_id and description. without user_id, there is no way for a tenant to tell which user created the server. description should be added for ec2 parity. --- nova/api/openstack/create_instance_helper.py | 3 ++- nova/api/openstack/views/servers.py | 2 ++ nova/tests/api/openstack/test_servers.py | 22 +++++++++++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 978741682..96f817d38 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -154,7 +154,8 @@ class CreateInstanceHelper(object): kernel_id=kernel_id, ramdisk_id=ramdisk_id, display_name=name, - display_description=name, + display_description=\ + server_dict.get('description', ''), key_name=key_name, key_data=key_data, metadata=server_dict.get('metadata', {}), diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index edc328129..c5f1e6021 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -64,6 +64,8 @@ class ViewBuilder(object): inst_dict = { 'id': inst['id'], 'name': inst['display_name'], + 'user_id': inst['user_id'], + 'description': inst['display_description'], 'status': common.status_from_power_state(inst.get('state'))} ctxt = nova.context.get_admin_context() diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 437620854..7ca58b24d 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -194,7 +194,7 @@ def stub_instance(id, user_id='fake', project_id='fake', private_address=None, "terminated_at": utils.utcnow(), "availability_zone": "", "display_name": server_name, - "display_description": "", + "display_description": "fakedescription", "locked": False, "metadata": metadata, "uuid": uuid, @@ -329,10 +329,12 @@ class ServersTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, "name": "server1", + "description": "fakedescription", "status": "BUILD", "hostId": '', "image": { @@ -491,10 +493,12 @@ class ServersTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, "name": "server1", + "description": "fakedescription", "status": "ACTIVE", "hostId": '', "image": { @@ -582,10 +586,12 @@ class ServersTest(test.TestCase): "server": { "id": 1, "uuid": FAKE_UUID, + "user_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, "name": "server1", + "description": "fakedescription", "status": "ACTIVE", "hostId": '', "image": { @@ -1380,6 +1386,8 @@ class ServersTest(test.TestCase): 'uuid': FAKE_UUID, 'instance_type': dict(inst_type), 'image_ref': image_ref, + 'display_description': 'fakedescription', + 'user_id': 'fake', "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), } @@ -2719,6 +2727,8 @@ class TestServerInstanceCreation(test.TestCase): else: self.injected_files = None return [{'id': '1234', 'display_name': 'fakeinstance', + 'user_id': 'fake', + 'display_description': 'fakedescription', 'uuid': FAKE_UUID}] def set_admin_password(self, *args, **kwargs): @@ -3010,7 +3020,7 @@ class ServersViewBuilderV11Test(test.TestCase): "created_at": created_at, "updated_at": updated_at, "admin_pass": "", - "user_id": "", + "user_id": "fake", "project_id": "", "image_ref": "5", "kernel_id": "", @@ -3036,7 +3046,7 @@ class ServersViewBuilderV11Test(test.TestCase): "terminated_at": utils.utcnow(), "availability_zone": "", "display_name": "test_server", - "display_description": "", + "display_description": "fakedescription", "locked": False, "metadata": [], #"address": , @@ -3088,10 +3098,12 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, "name": "test_server", + "description": "fakedescription", "status": "BUILD", "hostId": '', "image": { @@ -3139,10 +3151,12 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, "name": "test_server", + "description": "fakedescription", "status": "ACTIVE", "hostId": '', "image": { @@ -3194,10 +3208,12 @@ class ServersViewBuilderV11Test(test.TestCase): "server": { "id": 1, "uuid": self.instance['uuid'], + "user_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, "name": "test_server", + "description": "fakedescription", "status": "BUILD", "hostId": '', "image": { -- cgit From 34ef09beb3bf00fd9eb16b8517c520af24641e8c Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Fri, 19 Aug 2011 10:10:51 -0700 Subject: add tenant_id to api. without tenant_id, admins can't tell which servers belong to which tenants when retrieving lists --- nova/api/openstack/servers.py | 5 +++++ nova/api/openstack/views/servers.py | 1 + nova/tests/api/openstack/test_servers.py | 29 ++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 41e63ec3c..57ed5f45e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -107,6 +107,11 @@ class Controller(object): LOG.error(reason) raise exception.InvalidInput(reason=reason) + # translate tenant_id filter to internal project_id + if 'tenant_id' in search_opts: + search_opts['project_id'] = search_opts['tenant_id'] + del search_opts['tenant_id'] + # By default, compute's get_all() will return deleted instances. # If an admin hasn't specified a 'deleted' search option, we need # to filter out deleted instances by setting the filter ourselves. diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index c5f1e6021..37f48b3b2 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -65,6 +65,7 @@ class ViewBuilder(object): 'id': inst['id'], 'name': inst['display_name'], 'user_id': inst['user_id'], + 'tenant_id': inst['project_id'], 'description': inst['display_description'], 'status': common.status_from_power_state(inst.get('state'))} diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 7ca58b24d..480d6a370 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -330,6 +330,7 @@ class ServersTest(test.TestCase): "id": 1, "uuid": FAKE_UUID, "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, @@ -494,6 +495,7 @@ class ServersTest(test.TestCase): "id": 1, "uuid": FAKE_UUID, "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, @@ -587,6 +589,7 @@ class ServersTest(test.TestCase): "id": 1, "uuid": FAKE_UUID, "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, @@ -1152,6 +1155,25 @@ class ServersTest(test.TestCase): self.assertEqual(len(servers), 1) self.assertEqual(servers[0]['id'], 100) + def test_tenant_id_filter_converts_to_project_id_for_admin(self): + def fake_get_all(compute_self, context, search_opts=None): + self.assertNotEqual(search_opts, None) + self.assertEqual(search_opts['project_id'], 'faketenant') + self.assertFalse(search_opts.get('tenant_id')) + return [stub_instance(100)] + + self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + self.flags(allow_admin_api=True) + + req = webob.Request.blank('/v1.1/servers?tenant_id=faketenant') + # Use admin context + context = nova.context.RequestContext('testuser', 'testproject', + is_admin=True) + res = req.get_response(fakes.wsgi_app(fake_auth_context=context)) + res_dict = json.loads(res.body) + # Failure in fake_get_all returns non 200 status code + self.assertEqual(res.status_int, 200) + def test_get_servers_allows_flavor_v1_1(self): def fake_get_all(compute_self, context, search_opts=None): self.assertNotEqual(search_opts, None) @@ -1388,6 +1410,7 @@ class ServersTest(test.TestCase): 'image_ref': image_ref, 'display_description': 'fakedescription', 'user_id': 'fake', + 'project_id': 'fake', "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), } @@ -2728,6 +2751,7 @@ class TestServerInstanceCreation(test.TestCase): self.injected_files = None return [{'id': '1234', 'display_name': 'fakeinstance', 'user_id': 'fake', + 'project_id': 'fake', 'display_description': 'fakedescription', 'uuid': FAKE_UUID}] @@ -3021,7 +3045,7 @@ class ServersViewBuilderV11Test(test.TestCase): "updated_at": updated_at, "admin_pass": "", "user_id": "fake", - "project_id": "", + "project_id": "fake", "image_ref": "5", "kernel_id": "", "ramdisk_id": "", @@ -3099,6 +3123,7 @@ class ServersViewBuilderV11Test(test.TestCase): "id": 1, "uuid": self.instance['uuid'], "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, @@ -3152,6 +3177,7 @@ class ServersViewBuilderV11Test(test.TestCase): "id": 1, "uuid": self.instance['uuid'], "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 100, @@ -3209,6 +3235,7 @@ class ServersViewBuilderV11Test(test.TestCase): "id": 1, "uuid": self.instance['uuid'], "user_id": "fake", + "tenant_id": "fake", "updated": "2010-11-11T11:00:00Z", "created": "2010-10-10T12:00:00Z", "progress": 0, -- cgit From f8ec1a5b9002f4a4cda5d7156c6006aac0035c33 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 16:12:33 -0500 Subject: initial committ --- nova/tests/test_compute.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 4f5d36f14..925ac733b 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -35,6 +35,7 @@ from nova import rpc from nova import test from nova import utils from nova.notifier import test_notifier +from nova.tests import fake_network_info LOG = logging.getLogger('nova.tests.compute') FLAGS = flags.FLAGS @@ -133,6 +134,9 @@ class ComputeTestCase(test.TestCase): def test_create_instance_defaults_display_name(self): """Verify that an instance cannot be created without a display_name.""" + import pretty_print as pp + pp(fake_get_instance_nw_info(self.stubs, 1, 2)) + self.assertEqual(True, False) cases = [dict(), dict(display_name=None)] for instance in cases: ref = self.compute_api.create(self.context, -- cgit From 773f64af47e38f10b20399fcfaa43cc52eb1d2e6 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 16:41:02 -0500 Subject: added fake network info --- nova/tests/fake_network_info.py | 107 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 nova/tests/fake_network_info.py (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py new file mode 100644 index 000000000..f5bc0368c --- /dev/null +++ b/nova/tests/fake_network_info.py @@ -0,0 +1,107 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Rackspace +# 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. + +from nova import db +from nova import test +from nova.network import manager as network_manager + + +HOST = "testhost" + + +class FakeModel(dict): + """Represent a model from the db""" + def __init__(self, *args, **kwargs): + self.update(kwargs) + + def __getattr__(self, name): + return self[name] + + +def fake_network(n): + return {'id': n, + 'label': 'test%d' % n, + 'injected': False, + 'multi_host': False, + 'cidr': '192.168.%d.0/24' % n, + 'cidr_v6': '2001:db8:0:%x::/64' % n, + 'gateway_v6': '2001:db8:0:%x::1' % n, + 'netmask_v6': '64', + 'netmask': '255.255.255.0', + 'bridge': 'fa%d' % n, + 'bridge_interface': 'fake_br%d' % n, + 'gateway': '192.168.%d.1' % n, + 'broadcast': '192.168.%d.255' % n, + 'dns1': '192.168.%d.3' % n, + 'dns2': '192.168.%d.4' % n, + 'vlan': None, + 'host': None, + 'project_id': 'fake_project', + 'vpn_public_address': '192.168.%d.2' % n} + + +def fixed_ips(num_networks, num_ips): + for network in xrange(num_networks): + for ip in xrange(num_ips): + yield {'id': network * ip, + 'network_id': network, + 'address': '192.168.%d.100' % network, + 'instance_id': 0, + 'allocated': False, + # and since network_id and vif_id happen to be equivalent + 'virtual_interface_id': network, + 'floating_ips': [FakeModel(**floating_ip)]} + + +flavor = {'id': 0, + 'rxtx_cap': 3} + + +floating_ip = {'id': 0, + 'address': '10.10.10.10', + 'fixed_ip_id': 0, + 'project_id': None, + 'auto_assigned': False} + + +def vifs(n): + for x in xrange(n): + yield {'id': x, + 'address': 'DE:AD:BE:EF:00:%2x' % x, + 'uuid': '00000000-0000-0000-0000-00000000000000%2d' % x, + 'network_id': x, + 'network': FakeModel(**fake_network(x)), + 'instance_id': 0} + + +def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): + network = network_manager.FlatManager(host=HOST) + + def fixed_ips_fake(*args, **kwargs): + return fixed_ips() + + def virtual_interfaces_fake(*args, **kwargs): + return [vif for vif in vifs(n)] + + def instance_type_fake(*args, **kwargs): + return flavor + + stubs.Set(db, 'fixed_ip_get_by_instance', fixed_ips_fake) + stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interface_fake) + stubs.Set(db, 'instance_type_get', instance_type_fake) + + nw_info = self.network.get_instance_nw_info(None, 0, 0, None) -- cgit From 608a6c3e719fffe9af6f8f7cc6d18824d0c28c36 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 16:42:52 -0500 Subject: typo --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index f5bc0368c..991599987 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -101,7 +101,7 @@ def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): return flavor stubs.Set(db, 'fixed_ip_get_by_instance', fixed_ips_fake) - stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interface_fake) + stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interfaces_fake) stubs.Set(db, 'instance_type_get', instance_type_fake) nw_info = self.network.get_instance_nw_info(None, 0, 0, None) -- cgit From c4d73b638dee9dc4c1b2883affc0c00cdfc5ecb6 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 16:44:23 -0500 Subject: typo --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 991599987..61b35a9d2 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -104,4 +104,4 @@ def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interfaces_fake) stubs.Set(db, 'instance_type_get', instance_type_fake) - nw_info = self.network.get_instance_nw_info(None, 0, 0, None) + nw_info = network_manager.get_instance_nw_info(None, 0, 0, None) -- cgit From ca83ca57646ba76908e9b5e600208fe9afde78a4 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 16:47:10 -0500 Subject: typo --- nova/tests/fake_network_info.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 61b35a9d2..1fb3584dd 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -18,6 +18,7 @@ from nova import db from nova import test from nova.network import manager as network_manager +from network_ HOST = "testhost" @@ -104,4 +105,4 @@ def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interfaces_fake) stubs.Set(db, 'instance_type_get', instance_type_fake) - nw_info = network_manager.get_instance_nw_info(None, 0, 0, None) + nw_info = network.get_instance_nw_info(None, 0, 0, None) -- cgit From 23e163199e3b3208d7ba16049b7a93cbd59a8eaf Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 16:48:01 -0500 Subject: typo --- nova/tests/fake_network_info.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 1fb3584dd..87f6b9c56 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -18,7 +18,6 @@ from nova import db from nova import test from nova.network import manager as network_manager -from network_ HOST = "testhost" -- cgit From 7e6451b722b1e77ccab702482d5d5ad516056825 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 16:58:39 -0500 Subject: typo --- nova/tests/fake_network_info.py | 1 + nova/tests/test_compute.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 87f6b9c56..3360675d6 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -90,6 +90,7 @@ def vifs(n): def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): network = network_manager.FlatManager(host=HOST) + network.db = db def fixed_ips_fake(*args, **kwargs): return fixed_ips() diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 925ac733b..dd65d81cc 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -134,8 +134,7 @@ class ComputeTestCase(test.TestCase): def test_create_instance_defaults_display_name(self): """Verify that an instance cannot be created without a display_name.""" - import pretty_print as pp - pp(fake_get_instance_nw_info(self.stubs, 1, 2)) + print fake_network_info.fake_get_instance_nw_info(self.stubs, 1, 2) self.assertEqual(True, False) cases = [dict(), dict(display_name=None)] for instance in cases: -- cgit From 429d28df87e887ef297453f3dc186c1a99ba0a7a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 17:00:13 -0500 Subject: typo --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 3360675d6..d920ab1a2 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -93,7 +93,7 @@ def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): network.db = db def fixed_ips_fake(*args, **kwargs): - return fixed_ips() + return fixed_ips(n, ips_per_vif) def virtual_interfaces_fake(*args, **kwargs): return [vif for vif in vifs(n)] -- cgit From 6ab9559b5f1f398cdaac4eca7bfcfcda859d8bc8 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 17:02:21 -0500 Subject: fixed formatting string --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index d920ab1a2..e0ff71c2c 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -81,7 +81,7 @@ floating_ip = {'id': 0, def vifs(n): for x in xrange(n): yield {'id': x, - 'address': 'DE:AD:BE:EF:00:%2x' % x, + 'address': 'DE:AD:BE:EF:00:%02x' % x, 'uuid': '00000000-0000-0000-0000-00000000000000%2d' % x, 'network_id': x, 'network': FakeModel(**fake_network(x)), -- cgit From 95851baea252d801cbb704e869f78162ea995ceb Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 17:05:29 -0500 Subject: added return --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index e0ff71c2c..53785635c 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -105,4 +105,4 @@ def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interfaces_fake) stubs.Set(db, 'instance_type_get', instance_type_fake) - nw_info = network.get_instance_nw_info(None, 0, 0, None) + return network.get_instance_nw_info(None, 0, 0, None) -- cgit From 44af602dbc6f02de44e2b844a8d53a87680b7a94 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 17:18:57 -0500 Subject: who cares --- nova/tests/test_compute.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index dd65d81cc..fcb86e322 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -134,7 +134,7 @@ class ComputeTestCase(test.TestCase): def test_create_instance_defaults_display_name(self): """Verify that an instance cannot be created without a display_name.""" - print fake_network_info.fake_get_instance_nw_info(self.stubs, 1, 2) + print fake_network_info.fake_get_instance_nw_info(self.stubs, 2, 1) self.assertEqual(True, False) cases = [dict(), dict(display_name=None)] for instance in cases: -- cgit From 32e5341af311faf9838bd5d039b2153549726a71 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 17:36:52 -0500 Subject: updated a maths --- nova/tests/fake_network_info.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 53785635c..e73fe0442 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -57,7 +57,7 @@ def fake_network(n): def fixed_ips(num_networks, num_ips): for network in xrange(num_networks): for ip in xrange(num_ips): - yield {'id': network * ip, + yield {'id': network * num_ips + ip, 'network_id': network, 'address': '192.168.%d.100' % network, 'instance_id': 0, @@ -82,7 +82,7 @@ def vifs(n): for x in xrange(n): yield {'id': x, 'address': 'DE:AD:BE:EF:00:%02x' % x, - 'uuid': '00000000-0000-0000-0000-00000000000000%2d' % x, + 'uuid': '00000000-0000-0000-0000-00000000000000%02d' % x, 'network_id': x, 'network': FakeModel(**fake_network(x)), 'instance_id': 0} @@ -93,7 +93,7 @@ def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): network.db = db def fixed_ips_fake(*args, **kwargs): - return fixed_ips(n, ips_per_vif) + return list(fixed_ips(n, ips_per_vif)) def virtual_interfaces_fake(*args, **kwargs): return [vif for vif in vifs(n)] -- cgit From 154f1fbcc7098aca210514ce9f458fb755b4b50b Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 17:51:14 -0500 Subject: updated a maths --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index e73fe0442..0c33898cb 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -59,7 +59,7 @@ def fixed_ips(num_networks, num_ips): for ip in xrange(num_ips): yield {'id': network * num_ips + ip, 'network_id': network, - 'address': '192.168.%d.100' % network, + 'address': '192.168.%d.1%02d' % (network, ip), 'instance_id': 0, 'allocated': False, # and since network_id and vif_id happen to be equivalent -- cgit From 4d975772a6a488a99bec616f0118dd1ce74e9403 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 19 Aug 2011 18:04:08 -0500 Subject: finished fake network info, removed testing shims --- nova/tests/test_compute.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index fcb86e322..4f5d36f14 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -35,7 +35,6 @@ from nova import rpc from nova import test from nova import utils from nova.notifier import test_notifier -from nova.tests import fake_network_info LOG = logging.getLogger('nova.tests.compute') FLAGS = flags.FLAGS @@ -134,8 +133,6 @@ class ComputeTestCase(test.TestCase): def test_create_instance_defaults_display_name(self): """Verify that an instance cannot be created without a display_name.""" - print fake_network_info.fake_get_instance_nw_info(self.stubs, 2, 1) - self.assertEqual(True, False) cases = [dict(), dict(display_name=None)] for instance in cases: ref = self.compute_api.create(self.context, -- cgit From d2ea838bfad96bae172458ac6389a9f98111a7df Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Mon, 22 Aug 2011 17:46:47 -0500 Subject: update test_network test_get_instance_nw_info() --- nova/network/manager.py | 12 +++---- nova/tests/fake_network_info.py | 21 ++++++------ nova/tests/test_network.py | 71 +++++++++++++++++------------------------ 3 files changed, 48 insertions(+), 56 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 921c27e45..dcd5aad9a 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -468,17 +468,17 @@ class NetworkManager(manager.SchedulerDependentManager): # TODO(tr3buchet) eventually "enabled" should be determined def ip_dict(ip): return { - "ip": ip, - "netmask": network["netmask"], - "enabled": "1"} + 'ip': ip, + 'netmask': network['netmask'], + 'enabled': '1'} def ip6_dict(): return { - "ip": ipv6.to_global(network['cidr_v6'], + 'ip': ipv6.to_global(network['cidr_v6'], vif['address'], network['project_id']), - "netmask": network['netmask_v6'], - "enabled": "1"} + 'netmask': network['netmask_v6'], + 'enabled': '1'} network_dict = { 'bridge': network['bridge'], 'id': network['id'], diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 0c33898cb..072585195 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -42,8 +42,8 @@ def fake_network(n): 'gateway_v6': '2001:db8:0:%x::1' % n, 'netmask_v6': '64', 'netmask': '255.255.255.0', - 'bridge': 'fa%d' % n, - 'bridge_interface': 'fake_br%d' % n, + 'bridge': 'fake_br%d' % n, + 'bridge_interface': 'fake_eth%d' % n, 'gateway': '192.168.%d.1' % n, 'broadcast': '192.168.%d.255' % n, 'dns1': '192.168.%d.3' % n, @@ -54,26 +54,29 @@ def fake_network(n): 'vpn_public_address': '192.168.%d.2' % n} -def fixed_ips(num_networks, num_ips): +def fixed_ips(num_networks, num_ips, num_floating_ips=0): for network in xrange(num_networks): for ip in xrange(num_ips): - yield {'id': network * num_ips + ip, + id = network * num_ips + ip + f_ips = [floating_ips(id).next() for i in xrange(num_floating_ips)] + yield {'id': id, 'network_id': network, 'address': '192.168.%d.1%02d' % (network, ip), 'instance_id': 0, 'allocated': False, # and since network_id and vif_id happen to be equivalent 'virtual_interface_id': network, - 'floating_ips': [FakeModel(**floating_ip)]} + 'floating_ips': [FakeModel(**ip) for ip in f_ips]} flavor = {'id': 0, 'rxtx_cap': 3} - -floating_ip = {'id': 0, - 'address': '10.10.10.10', - 'fixed_ip_id': 0, +def floating_ips(fixed_ip_id): + for i in xrange(154): + yield {'id': 0, + 'address': '10.10.10.%d' % (i + 100), + 'fixed_ip_id': fixed_ip_id, 'project_id': None, 'auto_assigned': False} diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index e5c80b6f6..7822fbc70 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -14,15 +14,14 @@ # 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 mox from nova import db from nova import exception from nova import log as logging from nova import test from nova.network import manager as network_manager - - -import mox +from nova.tests import fake_network_info LOG = logging.getLogger('nova.tests.network') @@ -128,60 +127,50 @@ class FlatNetworkTestCase(test.TestCase): self.network.db = db def test_get_instance_nw_info(self): - self.mox.StubOutWithMock(db, 'fixed_ip_get_by_instance') - self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') - self.mox.StubOutWithMock(db, 'instance_type_get') - - db.fixed_ip_get_by_instance(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips) - db.virtual_interface_get_by_instance(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(vifs) - db.instance_type_get(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(flavor) - self.mox.ReplayAll() - - nw_info = self.network.get_instance_nw_info(None, 0, 0, None) + fake_get_instance_nw_info = fake_network_info.fake_get_instance_nw_info - self.assertTrue(nw_info) + nw_info = fake_get_instance_nw_info(self.stubs, 0, 2) + self.assertFalse(nw_info) - for i, nw in enumerate(nw_info): - i8 = i + 8 - check = {'bridge': 'fa%s' % i, + for i, (nw, info) in enumerate(nw_info): + check = {'bridge': 'fake_br%d' % i, 'cidr': '192.168.%s.0/24' % i, - 'cidr_v6': '2001:db%s::/64' % i8, + 'cidr_v6': '2001:db8:0:%x::/64' % i, 'id': i, 'multi_host': False, - 'injected': 'DONTCARE', - 'bridge_interface': 'fake_fa%s' % i, + 'injected': False, + 'bridge_interface': 'fake_eth%d' % i, 'vlan': None} - self.assertDictMatch(nw[0], check) + self.assertDictMatch(nw, check) - check = {'broadcast': '192.168.%s.255' % i, - 'dhcp_server': '192.168.%s.1' % i, - 'dns': 'DONTCARE', - 'gateway': '192.168.%s.1' % i, - 'gateway6': '2001:db%s::1' % i8, + check = {'broadcast': '192.168.%d.255' % i, + 'dhcp_server': '192.168.%d.1' % i, + 'dns': ['192.168.%d.3' % n, '192.168.%d.4' % n] + 'gateway': '192.168.%d.1' % i, + 'gateway6': '2001:db8:0:%x::1' % i, 'ip6s': 'DONTCARE', 'ips': 'DONTCARE', - 'label': 'test%s' % i, - 'mac': 'DE:AD:BE:EF:00:0%s' % i, - 'vif_uuid': ('00000000-0000-0000-0000-000000000000000%s' % - i), - 'rxtx_cap': 'DONTCARE', + 'label': 'test%d' % i, + 'mac': 'DE:AD:BE:EF:00:%02x' % i, + 'vif_uuid': + '00000000-0000-0000-0000-00000000000000%02d' % i, + 'rxtx_cap': 3, 'should_create_vlan': False, 'should_create_bridge': False} - self.assertDictMatch(nw[1], check) + self.assertDictMatch(info, check) check = [{'enabled': 'DONTCARE', - 'ip': '2001:db%s::dcad:beff:feef:%s' % (i8, i), + 'ip': '2001:db8::dcad:beff:feef:%s' % i, 'netmask': '64'}] - self.assertDictListMatch(nw[1]['ip6s'], check) + self.assertDictListMatch(info['ip6s'], check) - check = [{'enabled': '1', - 'ip': '192.168.%s.100' % i, - 'netmask': '255.255.255.0'}] - self.assertDictListMatch(nw[1]['ips'], check) + num_fixed_ips = len(info['ips']) + check = [{'enabled': 'DONTCARE', + 'ip': '192.168.%d.1%02d' % (i, ip_num), + 'netmask': '255.255.255.0'} + for ip_num in xrange(num_fixed_ips)] + self.assertDictListMatch(info['ips'], check) class VlanNetworkTestCase(test.TestCase): -- cgit From 158a953f98f8d4ee365cbc4936754fe7a8c89082 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Mon, 22 Aug 2011 17:52:54 -0500 Subject: syntax --- nova/tests/test_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 7822fbc70..91105ece3 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -146,7 +146,7 @@ class FlatNetworkTestCase(test.TestCase): check = {'broadcast': '192.168.%d.255' % i, 'dhcp_server': '192.168.%d.1' % i, - 'dns': ['192.168.%d.3' % n, '192.168.%d.4' % n] + 'dns': ['192.168.%d.3' % n, '192.168.%d.4' % n], 'gateway': '192.168.%d.1' % i, 'gateway6': '2001:db8:0:%x::1' % i, 'ip6s': 'DONTCARE', -- cgit From 49ef06ba21115a64c2efbb6fa81e0e6ee3f9095d Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Mon, 22 Aug 2011 16:21:29 -0700 Subject: xml deserialization, and test fixes --- nova/api/openstack/servers.py | 9 ++++- nova/tests/api/openstack/test_servers.py | 66 +++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 57ed5f45e..7faeb7278 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -183,6 +183,10 @@ class Controller(object): self.helper._validate_server_name(name) update_dict['display_name'] = name.strip() + if 'description' in body['server']: + description = body['server']['description'] + update_dict['display_description'] = description.strip() + try: self.compute_api.update(ctxt, id, **update_dict) except exception.NotFound: @@ -836,9 +840,12 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): def _add_server_attributes(self, node, server): node.setAttribute('id', str(server['id'])) + node.setAttribute('userId', str(server['user_id'])) + node.setAttribute('tenantId', str(server['tenant_id'])) node.setAttribute('uuid', str(server['uuid'])) node.setAttribute('hostId', str(server['hostId'])) node.setAttribute('name', server['name']) + node.setAttribute('description', server['description']) node.setAttribute('created', str(server['created'])) node.setAttribute('updated', str(server['updated'])) node.setAttribute('status', server['status']) @@ -945,7 +952,7 @@ def create_resource(version='1.0'): "attributes": { "server": ["id", "imageId", "name", "flavorId", "hostId", "status", "progress", "adminPass", "flavorRef", - "imageRef"], + "imageRef", "userId", "tenantId", "description"], "link": ["rel", "type", "href"], }, "dict_collections": { diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 480d6a370..2f849f07a 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -145,7 +145,8 @@ def instance_addresses(context, instance_id): 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, image_ref="10", - flavor_id="1", interfaces=None, name=None): + flavor_id="1", interfaces=None, name=None, + description='fakedescription'): metadata = [] metadata.append(InstanceMetadata(key='seq', value=id)) @@ -194,7 +195,7 @@ def stub_instance(id, user_id='fake', project_id='fake', private_address=None, "terminated_at": utils.utcnow(), "availability_zone": "", "display_name": server_name, - "display_description": "fakedescription", + "display_description": description, "locked": False, "metadata": metadata, "uuid": uuid, @@ -427,9 +428,12 @@ class ServersTest(test.TestCase): expected = minidom.parseString(""" Date: Mon, 22 Aug 2011 17:02:54 -0700 Subject: fix pep8 issue --- nova/api/openstack/create_instance_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 96f817d38..b0cdd87ea 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -154,8 +154,8 @@ class CreateInstanceHelper(object): kernel_id=kernel_id, ramdisk_id=ramdisk_id, display_name=name, - display_description=\ - server_dict.get('description', ''), + display_description=server_dict.\ + get('description', ''), key_name=key_name, key_data=key_data, metadata=server_dict.get('metadata', {}), -- cgit From 632526f0cf7a5be3a26c3ae14683b75bfb6afbfd Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Mon, 22 Aug 2011 22:18:43 -0700 Subject: pulling all qmanager changes into a branch based on trunk, as they were previously stacked on top of melange --- nova/db/api.py | 5 + nova/db/sqlalchemy/api.py | 20 +- .../versions/041_add_network_priority.py | 45 +++ nova/db/sqlalchemy/models.py | 1 + nova/network/manager.py | 37 ++- nova/network/quantum/__init__.py | 16 ++ nova/network/quantum/client.py | 306 +++++++++++++++++++++ nova/network/quantum/fake.py | 213 ++++++++++++++ nova/network/quantum/manager.py | 232 ++++++++++++++++ nova/network/quantum/melange_connection.py | 133 +++++++++ nova/network/quantum/melange_ipam_lib.py | 135 +++++++++ nova/network/quantum/nova_ipam_lib.py | 152 ++++++++++ nova/network/quantum/quantum_connection.py | 97 +++++++ nova/tests/test_quantum.py | 261 ++++++++++++++++++ 14 files changed, 1637 insertions(+), 16 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py create mode 100644 nova/network/quantum/__init__.py create mode 100644 nova/network/quantum/client.py create mode 100644 nova/network/quantum/fake.py create mode 100644 nova/network/quantum/manager.py create mode 100644 nova/network/quantum/melange_connection.py create mode 100644 nova/network/quantum/melange_ipam_lib.py create mode 100644 nova/network/quantum/nova_ipam_lib.py create mode 100644 nova/network/quantum/quantum_connection.py create mode 100644 nova/tests/test_quantum.py (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index 2d854f24c..9ff3a1c74 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -419,6 +419,11 @@ def virtual_interface_get_by_address(context, address): return IMPL.virtual_interface_get_by_address(context, address) +def virtual_interface_get_by_uuid(context, vif_uuid): + """Gets a virtual interface from the table filtering on vif uuid.""" + return IMPL.virtual_interface_get_by_uuid(context, vif_uuid) + + def virtual_interface_get_by_fixed_ip(context, fixed_ip_id): """Gets the virtual interface fixed_ip is associated with.""" return IMPL.virtual_interface_get_by_fixed_ip(context, fixed_ip_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 04b5405f6..d96b951a1 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -688,10 +688,8 @@ def fixed_ip_associate(context, address, instance_id, network_id=None): def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): session = get_session() with session.begin(): - network_or_none = or_(models.FixedIp.network_id == network_id, - models.FixedIp.network_id == None) fixed_ip_ref = session.query(models.FixedIp).\ - filter(network_or_none).\ + filter_by(network_id=network_id).\ filter_by(reserved=False).\ filter_by(deleted=False).\ filter_by(instance=None).\ @@ -928,6 +926,22 @@ def virtual_interface_get_by_address(context, address): return vif_ref +@require_context +def virtual_interface_get_by_uuid(context, vif_uuid): + """Gets a virtual interface from the table. + + :param vif_uuid: = the uuid of the interface you're looking to get + """ + session = get_session() + vif_ref = session.query(models.VirtualInterface).\ + filter_by(uuid=vif_uuid).\ + options(joinedload('network')).\ + options(joinedload('instance')).\ + options(joinedload('fixed_ips')).\ + first() + return vif_ref + + @require_context def virtual_interface_get_by_fixed_ip(context, fixed_ip_id): """Gets the virtual interface fixed_ip is associated with. diff --git a/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py new file mode 100644 index 000000000..e619b1fcd --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py @@ -0,0 +1,45 @@ +# Copyright 2011 OpenStack LLC. +# 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 datetime + +from sqlalchemy import * +from migrate import * + +from nova import log as logging +from nova import utils + +meta = MetaData() + +# Add priority column to networks table +priority = Column('priority', Integer()) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + + # grab tables and (column for dropping later) + networks = Table('networks', meta, autoload=True) + + try: + networks.create_column(priority) + except Exception: + logging.error(_("priority column not added to networks table")) + raise + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + networks.drop_column(priority) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 19dc3302e..11b147802 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -560,6 +560,7 @@ class Network(BASE, NovaBase): dhcp_start = Column(String(255)) project_id = Column(String(255)) + priority = Column(Integer) host = Column(String(255)) # , ForeignKey('hosts.id')) uuid = Column(String(36)) diff --git a/nova/network/manager.py b/nova/network/manager.py index aa2a3700c..b778377a0 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -443,7 +443,7 @@ class NetworkManager(manager.SchedulerDependentManager): try: fixed_ips = kwargs.get('fixed_ips') or \ self.db.fixed_ip_get_by_instance(context, instance_id) - except exceptions.FixedIpNotFoundForInstance: + except exception.FixedIpNotFoundForInstance: fixed_ips = [] LOG.debug(_("network deallocation for instance |%s|"), instance_id, context=context) @@ -541,21 +541,23 @@ class NetworkManager(manager.SchedulerDependentManager): def _allocate_mac_addresses(self, context, instance_id, networks): """Generates mac addresses and creates vif rows in db for them.""" for network in networks: - vif = {'address': self.generate_mac_address(), + self.add_virtual_interface(context, instance_id, network['id']) + + def add_virtual_interface(self, context, instance_id, network_id): + vif = {'address': self.generate_mac_address(), 'instance_id': instance_id, - 'network_id': network['id'], + 'network_id': network_id, 'uuid': str(utils.gen_uuid())} - # try FLAG times to create a vif record with a unique mac_address - for i in range(FLAGS.create_unique_mac_address_attempts): - try: - self.db.virtual_interface_create(context, vif) - break - except exception.VirtualInterfaceCreateException: - vif['address'] = self.generate_mac_address() - else: - self.db.virtual_interface_delete_by_instance(context, + # try FLAG times to create a vif record with a unique mac_address + for i in range(FLAGS.create_unique_mac_address_attempts): + try: + return self.db.virtual_interface_create(context, vif) + except exception.VirtualInterfaceCreateException: + vif['address'] = self.generate_mac_address() + else: + self.db.virtual_interface_delete_by_instance(context, instance_id) - raise exception.VirtualInterfaceMacAddressException() + raise exception.VirtualInterfaceMacAddressException() def generate_mac_address(self): """Generate an Ethernet MAC address.""" @@ -784,6 +786,15 @@ class NetworkManager(manager.SchedulerDependentManager): self._create_fixed_ips(context, network['id']) return networks + def delete_network(self, context, fixed_range, require_disassociated=True): + + network = db.network_get_by_cidr(context, fixed_range) + + if require_disassociated and network.project_id is not None: + raise ValueError(_('Network must be disassociated from project %s' + ' before delete' % network.project_id)) + self.db.network_delete_safe(context, network.id) + @property def _bottom_reserved_ips(self): # pylint: disable=R0201 """Number of reserved ips at the bottom of the range.""" diff --git a/nova/network/quantum/__init__.py b/nova/network/quantum/__init__.py new file mode 100644 index 000000000..f7fbfb511 --- /dev/null +++ b/nova/network/quantum/__init__.py @@ -0,0 +1,16 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira Networks +# 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. diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py new file mode 100644 index 000000000..a0c7dc6d8 --- /dev/null +++ b/nova/network/quantum/client.py @@ -0,0 +1,306 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Citrix Systems +# 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. +# @author: Tyler Smith, Cisco Systems + +import httplib +import json +import socket +import urllib + + +# this is a simple json-only serializer to use until +# we can just grab the standard serializer +# from the quantum library +class Serializer: + + def serialize(self, data, content_type): + try: + return json.dumps(data) + except TypeError: + pass + return json.dumps(to_primitive(data)) + + def deserialize(self, data, content_type): + return json.loads(data) + + +class api_call(object): + """A Decorator to add support for format and tenant overriding""" + def __init__(self, f): + self.f = f + + def __get__(self, instance, owner): + def with_params(*args, **kwargs): + # Temporarily set format and tenant for this request + (format, tenant) = (instance.format, instance.tenant) + + if 'format' in kwargs: + instance.format = kwargs['format'] + if 'tenant' in kwargs: + instance.tenant = kwargs['tenant'] + + ret = self.f(instance, *args) + (instance.format, instance.tenant) = (format, tenant) + return ret + return with_params + + +class Client(object): + + """A base client class - derived from Glance.BaseClient""" + + action_prefix = '/v0.1/tenants/{tenant_id}' + + """Action query strings""" + networks_path = "/networks" + network_path = "/networks/%s" + ports_path = "/networks/%s/ports" + port_path = "/networks/%s/ports/%s" + attachment_path = "/networks/%s/ports/%s/attachment" + + def __init__(self, host="127.0.0.1", port=9696, use_ssl=False, tenant=None, + format="xml", testingStub=None, key_file=None, cert_file=None, + logger=None): + """ + Creates a new client to some service. + + :param host: The host where service resides + :param port: The port where service resides + :param use_ssl: True to use SSL, False to use HTTP + :param tenant: The tenant ID to make requests with + :param format: The format to query the server with + :param testingStub: A class that stubs basic server methods for tests + :param key_file: The SSL key file to use if use_ssl is true + :param cert_file: The SSL cert file to use if use_ssl is true + """ + self.host = host + self.port = port + self.use_ssl = use_ssl + self.tenant = tenant + self.format = format + self.connection = None + self.testingStub = testingStub + self.key_file = key_file + self.cert_file = cert_file + self.logger = logger + + def get_connection_type(self): + """ + Returns the proper connection type + """ + if self.testingStub: + return self.testingStub + if self.use_ssl: + return httplib.HTTPSConnection + else: + return httplib.HTTPConnection + + def do_request(self, method, action, body=None, + headers=None, params=None): + """ + Connects to the server and issues a request. + Returns the result data, or raises an appropriate exception if + HTTP status code is not 2xx + + :param method: HTTP method ("GET", "POST", "PUT", etc...) + :param body: string of data to send, or None (default) + :param headers: mapping of key/value pairs to add as headers + :param params: dictionary of key/value pairs to add to append + to action + + """ + + # Ensure we have a tenant id + if not self.tenant: + raise Exception("Tenant ID not set") + + # Add format and tenant_id + action += ".%s" % self.format + action = Client.action_prefix + action + action = action.replace('{tenant_id}', self.tenant) + + if type(params) is dict: + action += '?' + urllib.urlencode(params) + + try: + connection_type = self.get_connection_type() + headers = headers or {"Content-Type": + "application/%s" % self.format} + + # Open connection and send request, handling SSL certs + certs = {'key_file': self.key_file, 'cert_file': self.cert_file} + certs = dict((x, certs[x]) for x in certs if certs[x] != None) + + if self.use_ssl and len(certs): + c = connection_type(self.host, self.port, **certs) + else: + c = connection_type(self.host, self.port) + + if self.logger: + self.logger.debug("Quantum Client Request:\n" \ + + method + " " + action + "\n") + if body: + self.logger.debug(body) + + c.request(method, action, body, headers) + res = c.getresponse() + status_code = self.get_status_code(res) + data = res.read() + + if self.logger: + self.logger.debug("Quantum Client Reply (code = %s) :\n %s" \ + % (str(status_code), data)) + + if status_code in (httplib.OK, + httplib.CREATED, + httplib.ACCEPTED, + httplib.NO_CONTENT): + return self.deserialize(data, status_code) + else: + raise Exception("Server returned error: %s" % res.read()) + + except (socket.error, IOError), e: + raise Exception("Unable to connect to " + "server. Got error: %s" % e) + + def get_status_code(self, response): + """ + Returns the integer status code from the response, which + can be either a Webob.Response (used in testing) or httplib.Response + """ + if hasattr(response, 'status_int'): + return response.status_int + else: + return response.status + + def serialize(self, data): + if data is None: + return None + elif type(data) is dict: + return Serializer().serialize(data, self.content_type()) + else: + raise Exception("unable to deserialize object of type = '%s'" \ + % type(data)) + + def deserialize(self, data, status_code): + if status_code == 202: + return data + return Serializer().deserialize(data, self.content_type()) + + def content_type(self, format=None): + if not format: + format = self.format + return "application/%s" % (format) + + @api_call + def list_networks(self): + """ + Fetches a list of all networks for a tenant + """ + return self.do_request("GET", self.networks_path) + + @api_call + def show_network_details(self, network): + """ + Fetches the details of a certain network + """ + return self.do_request("GET", self.network_path % (network)) + + @api_call + def create_network(self, body=None): + """ + Creates a new network + """ + body = self.serialize(body) + return self.do_request("POST", self.networks_path, body=body) + + @api_call + def update_network(self, network, body=None): + """ + Updates a network + """ + body = self.serialize(body) + return self.do_request("PUT", self.network_path % (network), body=body) + + @api_call + def delete_network(self, network): + """ + Deletes the specified network + """ + return self.do_request("DELETE", self.network_path % (network)) + + @api_call + def list_ports(self, network): + """ + Fetches a list of ports on a given network + """ + return self.do_request("GET", self.ports_path % (network)) + + @api_call + def show_port_details(self, network, port): + """ + Fetches the details of a certain port + """ + return self.do_request("GET", self.port_path % (network, port)) + + @api_call + def create_port(self, network, body=None): + """ + Creates a new port on a given network + """ + body = self.serialize(body) + return self.do_request("POST", self.ports_path % (network), body=body) + + @api_call + def delete_port(self, network, port): + """ + Deletes the specified port from a network + """ + return self.do_request("DELETE", self.port_path % (network, port)) + + @api_call + def set_port_state(self, network, port, body=None): + """ + Sets the state of the specified port + """ + body = self.serialize(body) + return self.do_request("PUT", + self.port_path % (network, port), body=body) + + @api_call + def show_port_attachment(self, network, port): + """ + Fetches the attachment-id associated with the specified port + """ + return self.do_request("GET", self.attachment_path % (network, port)) + + @api_call + def attach_resource(self, network, port, body=None): + """ + Sets the attachment-id of the specified port + """ + body = self.serialize(body) + return self.do_request("PUT", + self.attachment_path % (network, port), body=body) + + @api_call + def detach_resource(self, network, port): + """ + Removes the attachment-id of the specified port + """ + return self.do_request("DELETE", + self.attachment_path % (network, port)) diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py new file mode 100644 index 000000000..ff2b1e9d5 --- /dev/null +++ b/nova/network/quantum/fake.py @@ -0,0 +1,213 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira Networks, Inc +# 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. + +from nova import exception +from nova import ipv6 +from nova import log as logging +from nova import utils +import math +from netaddr import IPNetwork + + +LOG = logging.getLogger("network.quantum.fake") + + +# this class can be used for unit functional/testing on nova, +# as it does not actually make remote calls to the Quantum service +class FakeQuantumClientConnection: + + def __init__(self): + self.nets = {} + + def get_networks_for_tenant(self, tenant_id): + net_ids = [] + for net_id, n in self.nets.items(): + if n['tenant-id'] == tenant_id: + net_ids.append(net_id) + return net_ids + + def create_network(self, tenant_id, network_name): + + uuid = str(utils.gen_uuid()) + self.nets[uuid] = {'net-name': network_name, + 'tenant-id': tenant_id, + 'ports': {}} + return uuid + + def delete_network(self, tenant_id, net_id): + if self.nets[net_id]['tenant-id'] == tenant_id: + del self.nets[net_id] + + def network_exists(self, tenant_id, net_id): + try: + return self.nets[net_id]['tenant-id'] == tenant_id + except: + return False + + def _confirm_not_attached(self, interface_id): + for n in self.nets.values(): + for p in n['ports'].values(): + if p['attachment-id'] == interface_id: + raise Exception("interface '%s' is already attached" %\ + interface_id) + + def create_and_attach_port(self, tenant_id, net_id, interface_id): + if not self.network_exists(tenant_id, net_id): + raise Exception("network %s does not exist for tenant %s" %\ + (net_id, tenant_id)) + + self._confirm_not_attached(interface_id) + uuid = str(utils.gen_uuid()) + self.nets[net_id]['ports'][uuid] = \ + {"port-state": "ACTIVE", + "attachment-id": interface_id} + + def detach_and_delete_port(self, tenant_id, net_id, port_id): + if not self.network_exists(tenant_id, net_id): + raise Exception("network %s does not exist for tenant %s" %\ + (net_id, tenant_id)) + del self.nets[net_id]['ports'][port_id] + + def get_port_by_attachment(self, tenant_id, attachment_id): + for net_id, n in self.nets.items(): + if n['tenant-id'] == tenant_id: + for port_id, p in n['ports'].items(): + if p['attachment-id'] == attachment_id: + return (net_id, port_id) + + return (None, None) + + +def get_ipam_lib(net_man): + return FakeQuantumIPAMLib() + + +class FakeQuantumIPAMLib(): + + def __init__(self): + self.subnets = {} + + def create_subnet(self, context, label, tenant_id, quantum_net_id, + cidr=None, gateway_v6=None, cidr_v6=None, + dns1=None, dns2=None): + if int(cidr.split("/")[1]) != 24: + raise Exception("fake ipam_lib only supports /24s") + v4_ips = [] + net_start = cidr[0:cidr.rfind(".") + 1] + subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) + for i in xrange(2, subnet_size - 1): + v4_ips.append({"ip": net_start + str(i), + "allocated": False, + "virtual_interface_id": None, + "instance_id": None}) + self.subnets[quantum_net_id] = {\ + "label": label, + "gateway": net_start + "1", + "netmask": "255.255.255.0", + "broadcast": net_start + "255", + "cidr": cidr, + "gateway_v6": gateway_v6, + "cidr_v6": cidr_v6, + "dns1": dns1, + "dns2": dns2, + "project_id": tenant_id, + "v4_ips": v4_ips} + + def get_network_id_by_cidr(self, context, cidr, project_id): + for net_id, s in self.subnets.items(): + if s['cidr'] == cidr or s['cidr_v6'] == cidr: + return net_id + return None + + def delete_subnets_by_net_id(self, context, net_id, project_id): + self.verify_subnet_exists(context, project_id, net_id) + del self.subnets[net_id] + + def get_project_and_global_net_ids(self, context, project_id): + net_ids = [] + for nid, s in self.subnets.items(): + if s['project_id'] == project_id or \ + s['project_id'] == None: + net_ids.append((nid, s['project_id'])) + return net_ids + + def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): + subnet = self.subnets[quantum_net_id] + for i in xrange(0, len(subnet['v4_ips'])): + ip = subnet['v4_ips'][i] + if not ip['allocated']: + subnet['v4_ips'][i] = {'ip': ip['ip'], + 'allocated': True, + 'virtual_interface_id': vif_rec['uuid'], + 'instance_id': vif_rec['instance_id']} + return + raise Exception("Unable to find available IP for net '%s'" %\ + quantum_net_id) + + def get_subnets_by_net_id(self, context, tenant_id, net_id): + self.verify_subnet_exists(context, tenant_id, net_id) + + subnet_data = self.subnets[net_id] + subnet_data_v4 = { + 'network_id': net_id, + 'cidr': subnet_data['cidr'], + 'gateway': subnet_data['gateway'], + 'broadcast': subnet_data['broadcast'], + 'netmask': subnet_data['netmask'], + 'dns1': subnet_data['dns1'], + 'dns2': subnet_data['dns2']} + subnet_data_v6 = { + 'network_id': net_id, + 'cidr': subnet_data['cidr_v6'], + 'gateway': subnet_data['gateway_v6'], + 'broadcast': None, + 'netmask': None, + 'dns1': None, + 'dns2': None} + return (subnet_data_v4, subnet_data_v6) + + def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): + self.verify_subnet_exists(context, project_id, net_id) + + subnet_data = self.subnets[net_id] + if subnet_data['cidr_v6']: + ip = ipv6.to_global(subnet_data['cidr_v6'], + "ca:fe:de:ad:be:ef", + project_id) + return [ip] + return [] + + def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): + self.verify_subnet_exists(context, project_id, net_id) + + subnet_data = self.subnets[net_id] + for ip in subnet_data['v4_ips']: + if ip['virtual_interface_id'] == vif_id: + return [ip['ip']] + return [] + + def verify_subnet_exists(self, context, tenant_id, quantum_net_id): + if quantum_net_id not in self.subnets: + raise exception.NetworkNotFound(network_id=quantum_net_id) + + def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref): + s = self.subnets[net_id] + for ip in s['v4_ips']: + if ip['virtual_interface_id'] == vif_ref['id']: + ip['allocated'] = False + ip['instance_id'] = None + ip['virtual_interface_id'] = None diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py new file mode 100644 index 000000000..f712a93c4 --- /dev/null +++ b/nova/network/quantum/manager.py @@ -0,0 +1,232 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira Networks, Inc +# 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. + +from nova import db +from nova import exception +from nova import flags +from nova import log as logging +from nova import manager +from nova import utils +from nova.network import manager +from nova.network.quantum import quantum_connection +from nova.network.quantum import fake + +LOG = logging.getLogger("quantum_manager") + +FLAGS = flags.FLAGS + +flags.DEFINE_string('quantum_ipam_lib', + 'nova.network.quantum.nova_ipam_lib', + "Indicates underlying IP address management library") + + +class QuantumManager(manager.FlatManager): + + def __init__(self, ipam_lib=None, *args, **kwargs): + + if FLAGS.fake_network: + self.q_conn = fake.FakeQuantumClientConnection() + else: + self.q_conn = quantum_connection.QuantumClientConnection() + + if not ipam_lib: + ipam_lib = FLAGS.quantum_ipam_lib + self.ipam = utils.import_object(ipam_lib).get_ipam_lib(self) + + super(QuantumManager, self).__init__(*args, **kwargs) + + def create_networks(self, context, label, cidr, multi_host, num_networks, + network_size, cidr_v6, gateway_v6, bridge, + bridge_interface, dns1=None, dns2=None, **kwargs): + if num_networks != 1: + raise Exception("QuantumManager requires that only one" + " network is created per call") + q_tenant_id = kwargs["project_id"] or \ + FLAGS.quantum_default_tenant_id + quantum_net_id = bridge + if quantum_net_id: + if not q_conn.network_exists(q_tenant_id, quantum_net_id): + raise Exception("Unable to find existing quantum " \ + " network for tenant '%s' with net-id '%s'" % \ + (q_tenant_id, quantum_net_id)) + else: + # otherwise, create network from default quantum pool + quantum_net_id = self.q_conn.create_network(q_tenant_id, label) + + ipam_tenant_id = kwargs.get("project_id", None) + self.ipam.create_subnet(context, label, ipam_tenant_id, quantum_net_id, + cidr, gateway_v6, cidr_v6, dns1, dns2) + + def delete_network(self, context, fixed_range): + project_id = context.project_id + quantum_net_id = self.ipam.get_network_id_by_cidr( + context, fixed_range, project_id) + self.ipam.delete_subnets_by_net_id(context, quantum_net_id, + project_id) + try: + q_tenant_id = project_id or FLAGS.quantum_default_tenant_id + self.q_conn.delete_network(q_tenant_id, quantum_net_id) + except Exception, e: + raise Exception("Unable to delete Quantum Network with " + "fixed_range = %s (ports still in use?)." % fixed_range) + + def allocate_for_instance(self, context, **kwargs): + instance_id = kwargs.pop('instance_id') + instance_type_id = kwargs['instance_type_id'] + host = kwargs.pop('host') + project_id = kwargs.pop('project_id') + LOG.debug(_("network allocations for instance %s"), instance_id) + + # if using the create-server-networks extension, 'requested_networks' + # will be defined, otherwise, just grab relevant nets from IPAM + requested_networks = kwargs.get('requested_networks') + + if requested_networks: + net_proj_pairs = [(net_id, project_id) \ + for (net_id, _i) in requested_networks] + else: + net_proj_pairs = self.ipam.get_project_and_global_net_ids(context, + project_id) + + # Create a port via quantum and attach the vif + for (net_id, project_id) in net_proj_pairs: + vif_rec = manager.FlatManager.add_virtual_interface(self, + context, instance, None) + + q_tenant_id = project_id or FLAGS.quantum_default_tenant_id + self.q_conn.create_and_attach_port(q_tenant_id, net_id, + vif_rec['uuid']) + self.ipam.allocate_fixed_ip(context, project_id, net_id, vif_rec) + + return self.get_instance_nw_info(context, instance_id, + instance_type_id, host) + + def get_instance_nw_info(self, context, instance_id, + instance_type_id, host): + network_info = [] + project_id = context.project_id + + admin_context = context.elevated() + vifs = db.virtual_interface_get_by_instance(admin_context, + instance_id) + for vif in vifs: + q_tenant_id = project_id + ipam_tenant_id = project_id + net_id, port_id = self.q_conn.get_port_by_attachment(q_tenant_id, + vif['uuid']) + if not net_id: + q_tenant_id = FLAGS.quantum_default_tenant_id + ipam_tenant_id = None + net_id, port_id = self.q_conn.get_port_by_attachment( + q_tenant_id, vif['uuid']) + if not net_id: + raise Exception(_("No network for for virtual interface %s") %\ + vif['uuid']) + (v4_subnet, v6_subnet) = self.ipam.get_subnets_by_net_id(context, + ipam_tenant_id, net_id) + v4_ips = self.ipam.get_v4_ips_by_interface(context, + net_id, vif['uuid'], + project_id=ipam_tenant_id) + v6_ips = self.ipam.get_v6_ips_by_interface(context, + net_id, vif['uuid'], + project_id=ipam_tenant_id) + + quantum_net_id = v4_subnet['network_id'] or v6_subnet['network_id'] + + def ip_dict(ip, subnet): + return { + "ip": ip, + "netmask": subnet["netmask"], + "enabled": "1"} + + network_dict = { + 'cidr': v4_subnet['cidr'], + 'injected': True, + 'multi_host': False} + + info = { + 'gateway': v4_subnet['gateway'], + 'dhcp_server': v4_subnet['gateway'], + 'broadcast': v4_subnet['broadcast'], + 'mac': vif['address'], + 'vif_uuid': vif['uuid'], + 'dns': [], + 'ips': [ip_dict(ip, v4_subnet) for ip in v4_ips]} + + if v6_subnet['cidr']: + network_dict['cidr_v6'] = v6_subnet['cidr'] + info['ip6s'] = [ip_dict(ip, v6_subnet) for ip in v6_ips] + # TODO(tr3buchet): handle ip6 routes here as well + if v6_subnet['gateway']: + info['gateway6'] = v6_subnet['gateway'] + + dns_dict = {} + for s in [v4_subnet, v6_subnet]: + for k in ['dns1', 'dns2']: + if s[k]: + dns_dict[s[k]] = None + info['dns'] = [d for d in dns_dict.keys()] + + network_info.append((network_dict, info)) + return network_info + + def deallocate_for_instance(self, context, **kwargs): + instance_id = kwargs.get('instance_id') + project_id = kwargs.pop('project_id', None) + + admin_context = context.elevated() + vifs = db.virtual_interface_get_by_instance(admin_context, + instance_id) + for vif_ref in vifs: + interface_id = vif_ref['uuid'] + q_tenant_id = project_id + ipam_tenant_id = project_id + (net_id, port_id) = self.q_conn.get_port_by_attachment(q_tenant_id, + interface_id) + if not net_id: + q_tenant_id = FLAGS.quantum_default_tenant_id + ipam_tenant_id = None + (net_id, port_id) = self.q_conn.get_port_by_attachment( + q_tenant_id, interface_id) + if not net_id: + LOG.error("Unable to find port with attachment: %s" % \ + (interface_id)) + continue + self.q_conn.detach_and_delete_port(q_tenant_id, + net_id, port_id) + + self.ipam.deallocate_ips_by_vif(context, ipam_tenant_id, + net_id, vif_ref) + + self.net_manager.db.virtual_interface_delete_by_instance(admin_context, + instance_id) + self._do_trigger_security_group_members_refresh_for_instance( + instance_id) + + # validates that this tenant has quantum networks with the associated + # UUIDs. This is called by the 'os-create-server-ext' API extension + # code so that we can return an API error code to the caller if they + # request an invalid network. + def validate_networks(self, context, networks): + if networks is None: + return + + project_id = context.project_id + for (net_id, _i) in networks: + self.ipam.verify_subnet_exists(context, project_id, net_id) + if not self.q_conn.network_exists(project_id, net_id): + raise exception.NetworkNotFound(network_id=net_id) diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py new file mode 100644 index 000000000..b3955138d --- /dev/null +++ b/nova/network/quantum/melange_connection.py @@ -0,0 +1,133 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# 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 httplib +import socket +import urllib +import json +from nova import flags + + +FLAGS = flags.FLAGS + +flags.DEFINE_string('melange_host', + '127.0.0.1', + 'HOST for connecting to melange') + +flags.DEFINE_string('melange_port', + '9898', + 'PORT for connecting to melange') + +json_content_type = {'Content-type': "application/json"} + + +class MelangeConnection(object): + + def __init__(self, host=None, port=None, use_ssl=False): + if host is None: + host = FLAGS.melange_host + if port is None: + port = int(FLAGS.melange_port) + self.host = host + self.port = port + self.use_ssl = use_ssl + + def get(self, path, params={}, headers={}): + return self.do_request("GET", path, params=params, headers=headers) + + def post(self, path, body=None, headers={}): + return self.do_request("POST", path, body=body, headers=headers) + + def delete(self, path, headers={}): + return self.do_request("DELETE", path, headers=headers) + + def _get_connection(self): + if self.use_ssl: + return httplib.HTTPSConnection(self.host, self.port) + else: + return httplib.HTTPConnection(self.host, self.port) + + def do_request(self, method, path, body=None, headers={}, params={}): + + url = path + '.json?' + urllib.urlencode(params) + + try: + connection = self._get_connection() + connection.request(method, url, body, headers) + response = connection.getresponse() + response_str = response.read() + if response.status < 400: + return response_str + raise Exception("Server returned error: %s", response_str) + except (socket.error, IOError), e: + raise Exception("Unable to connect to " + "server. Got error: %s" % e) + + def allocate_ip(self, network_id, vif_id, + project_id=None, mac_address=None): + tenant_scope = "/tenants/%s" % project_id if project_id else "" + request_body = (json.dumps(dict(network=dict(mac_address=mac_address, + tenant_id=project_id))) + if mac_address else None) + url = ("/ipam%(tenant_scope)s/networks/%(network_id)s/" + "interfaces/%(vif_id)s/ip_allocations" % locals()) + response = self.post(url, body=request_body, + headers=json_content_type) + return json.loads(response)['ip_addresses'] + + def create_block(self, network_id, cidr, + project_id=None, dns1=None, dns2=None): + tenant_scope = "/tenants/%s" % project_id if project_id else "" + + url = "/ipam%(tenant_scope)s/ip_blocks" % locals() + + req_params = dict(ip_block=dict(cidr=cidr, network_id=network_id, + type='private', dns1=dns1, dns2=dns2)) + self.post(url, body=json.dumps(req_params), + headers=json_content_type) + + def delete_block(self, block_id, project_id=None): + tenant_scope = "/tenants/%s" % project_id if project_id else "" + + url = "/ipam%(tenant_scope)s/ip_blocks/%(block_id)s" % locals() + + self.delete(url, headers=json_content_type) + + def get_blocks(self, project_id=None): + tenant_scope = "/tenants/%s" % project_id if project_id else "" + + url = "/ipam%(tenant_scope)s/ip_blocks" % locals() + + response = self.get(url, headers=json_content_type) + return json.loads(response) + + def get_allocated_ips(self, network_id, vif_id, project_id=None): + tenant_scope = "/tenants/%s" % project_id if project_id else "" + + url = ("/ipam%(tenant_scope)s/networks/%(network_id)s/" + "interfaces/%(vif_id)s/ip_allocations" % locals()) + + response = self.get(url, headers=json_content_type) + return json.loads(response)['ip_addresses'] + + def deallocate_ips(self, network_id, vif_id, project_id=None): + tenant_scope = "/tenants/%s" % project_id if project_id else "" + + url = ("/ipam%(tenant_scope)s/networks/%(network_id)s/" + "interfaces/%(vif_id)s/ip_allocations" % locals()) + + self.delete(url, headers=json_content_type) diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py new file mode 100644 index 000000000..46038c349 --- /dev/null +++ b/nova/network/quantum/melange_ipam_lib.py @@ -0,0 +1,135 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira Networks, Inc +# 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. + +from netaddr import IPNetwork + +from nova import flags +from nova import log as logging +from nova.network.quantum import melange_connection + +LOG = logging.getLogger("quantum_melange_ipam") + +FLAGS = flags.FLAGS + + +def get_ipam_lib(net_man): + return QuantumMelangeIPAMLib() + + +class QuantumMelangeIPAMLib: + + def __init__(self): + self.m_conn = melange_connection.MelangeConnection() + + def create_subnet(self, context, label, project_id, + quantum_net_id, cidr=None, + gateway_v6=None, cidr_v6=None, + dns1=None, dns2=None): + tenant_id = project_id or FLAGS.quantum_default_tenant_id + if cidr: + self.m_conn.create_block(quantum_net_id, cidr, + project_id=tenant_id, + dns1=dns1, dns2=dns2) + if cidr_v6: + self.m_conn.create_block(quantum_net_id, cidr_v6, + project_id=tenant_id, + dns1=dns1, dns2=dns2) + + def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref): + tenant_id = project_id or FLAGS.quantum_default_tenant_id + self.m_conn.allocate_ip(quantum_net_id, + vif_ref['uuid'], project_id=tenant_id, + mac_address=vif_ref['address']) + + def get_network_id_by_cidr(self, context, cidr, project_id): + tenant_id = project_id or FLAGS.quantum_default_tenant_id + all_blocks = self.m_conn.get_blocks(tenant_id) + for b in all_blocks['ip_blocks']: + if b['cidr'] == cidr: + return b['network_id'] + + def delete_subnets_by_net_id(self, context, net_id, project_id): + tenant_id = project_id or FLAGS.quantum_default_tenant_id + all_blocks = self.m_conn.get_blocks(tenant_id) + for b in all_blocks['ip_blocks']: + if b['network_id'] == net_id: + self.m_conn.delete_block(b['id'], tenant_id) + + # get all networks with this project_id, as well as all networks + # where the project-id is not set (these are shared networks) + def get_project_and_global_net_ids(self, context, project_id): + id_proj_map = {} + if not project_id: + raise Exception("get_project_and_global_net_ids must be called" \ + " with a non-null project_id") + tenant_id = project_id + all_tenant_blocks = self.m_conn.get_blocks(tenant_id) + for b in all_tenant_blocks['ip_blocks']: + id_proj_map[b['network_id']] = tenant_id + tenant_id = FLAGS.quantum_default_tenant_id + all_provider_blocks = self.m_conn.get_blocks(tenant_id) + for b in all_provider_blocks['ip_blocks']: + id_proj_map[b['network_id']] = tenant_id + return id_proj_map.items() + + # FIXME: there must be a more efficient way to do this, + # talk to the melange folks + def get_subnets_by_net_id(self, context, project_id, net_id): + subnet_v4 = None + subnet_v6 = None + tenant_id = project_id or FLAGS.quantum_default_tenant_id + all_blocks = self.m_conn.get_blocks(tenant_id) + for b in all_blocks['ip_blocks']: + if b['network_id'] == net_id: + subnet = {'network_id': b['network_id'], + 'cidr': b['cidr'], + 'gateway': b['gateway'], + 'broadcast': b['broadcast'], + 'netmask': b['netmask'], + 'dns1': b['dns1'], + 'dns2': b['dns2']} + + if IPNetwork(b['cidr']).version == 6: + subnet_v6 = subnet + else: + subnet_v4 = subnet + return (subnet_v4, subnet_v6) + + def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): + return self.get_ips_by_interface(context, net_id, vif_id, + project_id, 4) + + def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): + return self.get_ips_by_interface(context, net_id, vif_id, + project_id, 6) + + def get_ips_by_interface(self, context, net_id, vif_id, project_id, + ip_version): + tenant_id = project_id or FLAGS.quantum_default_tenant_id + ip_list = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id) + return [ip['address'] for ip in ip_list \ + if IPNetwork(ip['address']).version == ip_version] + + def verify_subnet_exists(self, context, project_id, quantum_net_id): + tenant_id = project_id or FLAGS.quantum_default_tenant_id + v4_subnet, v6_subnet = self.get_subnets_by_net_id(context, tenant_id, + quantum_net_id) + return v4_subnet is not None + + def deallocate_ips_by_vif(self, context, project_id, net_id, vif_ref): + tenant_id = project_id or FLAGS.quantum_default_tenant_id + self.m_conn.deallocate_ips(net_id, vif_ref['uuid'], tenant_id) diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py new file mode 100644 index 000000000..0fc74fa49 --- /dev/null +++ b/nova/network/quantum/nova_ipam_lib.py @@ -0,0 +1,152 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira Networks, Inc +# 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 math + +#from nova import context +from nova import db +from nova import exception +from nova import flags +from nova import ipv6 +from nova import log as logging +from nova import utils +from nova.network import manager +from nova.network.quantum import melange_connection as melange + +LOG = logging.getLogger("quantum_nova_ipam_lib") + +FLAGS = flags.FLAGS + + +def get_ipam_lib(net_man): + return QuantumNovaIPAMLib(net_man) + + +class QuantumNovaIPAMLib: + + def __init__(self, net_manager): + self.net_manager = net_manager + + def create_subnet(self, context, label, tenant_id, + quantum_net_id, cidr=None, + gateway_v6=None, cidr_v6=None, + dns1=None, dns2=None): + print "creating subnet %s" % cidr + admin_context = context.elevated() + subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) + manager.FlatManager.create_networks(self.net_manager, + admin_context, label, cidr, + False, 1, subnet_size, cidr_v6, + gateway_v6, quantum_net_id, None, dns1, dns2) + + # now grab the network and update project_id + network = db.network_get_by_bridge(admin_context, quantum_net_id) + net = {"project_id": tenant_id} + db.network_update(admin_context, network['id'], net) + + def get_network_id_by_cidr(self, context, cidr, project_id): + admin_context = context.elevated() + network = db.network_get_by_cidr(admin_context, cidr) + if not network: + raise Exception("No network with fixed_range = %s" \ + % fixed_range) + return network['bridge'] + + def delete_subnets_by_net_id(self, context, net_id, project_id): + admin_context = context.elevated() + network = db.network_get_by_bridge(admin_context, net_id) + if not network: + raise Exception("No network with net_id = %s" % net_id) + manager.FlatManager.delete_network(self.net_manager, + admin_context, network['cidr'], + require_disassociated=False) + + def get_project_and_global_net_ids(self, context, project_id): + + # get all networks with this project_id, as well as all networks + # where the project-id is not set (these are shared networks) + admin_context = context.elevated() + networks = db.project_get_networks(admin_context, project_id, False) + networks.extend(db.project_get_networks(admin_context, None, False)) + return [(n['bridge'], n['project_id']) for n in networks] + + def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): + admin_context = context.elevated() + network = db.network_get_by_bridge(admin_context, quantum_net_id) + if network['cidr']: + address = db.fixed_ip_associate_pool(admin_context, + network['id'], + vif_rec['instance_id']) + values = {'allocated': True, + 'virtual_interface_id': vif_rec['id']} + db.fixed_ip_update(admin_context, address, values) + + def get_subnets_by_net_id(self, context, tenant_id, net_id): + n = db.network_get_by_bridge(context.elevated(), net_id) + subnet_data_v4 = { + 'network_id': n['bridge'], + 'cidr': n['cidr'], + 'gateway': n['gateway'], + 'broadcast': n['broadcast'], + 'netmask': n['netmask'], + 'dns1': n['dns1'], + 'dns2': n['dns2'] + } + subnet_data_v6 = { + 'network_id': n['bridge'], + 'cidr': n['cidr_v6'], + 'gateway': n['gateway_v6'], + 'broadcast': None, + 'netmask': None, + 'dns1': None, + 'dns2': None + } + return (subnet_data_v4, subnet_data_v6) + + def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): + vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) + fixed_ips = db.fixed_ip_get_by_virtual_interface(context, + vif_rec['id']) + return [f['address'] for f in fixed_ips] + + def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): + admin_context = context.elevated() + network = db.network_get_by_bridge(admin_context, net_id) + vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) + if network['cidr_v6']: + ip = ipv6.to_global(network['cidr_v6'], + vif_rec['address'], + project_id) + return [ip] + return [] + + def verify_subnet_exists(self, context, tenant_id, quantum_net_id): + admin_context = context.elevated() + network = db.network_get_by_bridge(admin_context, quantum_net_id) + + def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref): + try: + admin_context = context.elevated() + fixed_ips = db.fixed_ip_get_by_virtual_interface(admin_context, + vif_ref['id']) + for f in fixed_ips: + db.fixed_ip_update(admin_context, f['address'], + {'allocated': False, + 'virtual_interface_id': None}) + except exception.FixedIpNotFoundForInstance: + LOG.error(_('Failed to deallocate fixed IP for vif %s' % \ + vif_ref['id'])) diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py new file mode 100644 index 000000000..3aa017bcd --- /dev/null +++ b/nova/network/quantum/quantum_connection.py @@ -0,0 +1,97 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira Networks +# 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. + +from nova import flags +from nova import log as logging +from nova import utils + +from nova.network.quantum.client import Client + +LOG = logging.getLogger("nova.network.quantum") +FLAGS = flags.FLAGS + +flags.DEFINE_string('quantum_connection_host', + '127.0.0.1', + 'HOST for connecting to quantum') + +flags.DEFINE_string('quantum_connection_port', + '9696', + 'PORT for connecting to quantum') + +flags.DEFINE_string('quantum_default_tenant_id', + "default", + 'Default tenant id when creating quantum networks') + + +class QuantumClientConnection: + + def __init__(self): + self.client = Client(FLAGS.quantum_connection_host, + FLAGS.quantum_connection_port, + format="json", + logger=LOG) + + def create_network(self, tenant_id, network_name): + data = {'network': {'net-name': network_name}} + resdict = self.client.create_network(data, tenant=tenant_id) + return resdict["networks"]["network"]["id"] + + def delete_network(self, tenant_id, net_id): + self.client.delete_network(net_id, tenant=tenant_id) + + def network_exists(self, tenant_id, net_id): + try: + self.client.show_network_details(net_id, tenant=tenant_id) + except: + # FIXME: client lib should expose more granular exceptions + # so we can confirm we're getting a 404 and not some other error + return False + return True + + def create_and_attach_port(self, tenant_id, net_id, interface_id): + LOG.debug("Connecting interface %s to net %s for %s" % \ + (interface_id, net_id, tenant_id)) + port_data = {'port': {'port-state': 'ACTIVE'}} + resdict = self.client.create_port(net_id, port_data, tenant=tenant_id) + port_id = resdict["ports"]["port"]["id"] + + attach_data = {'port': {'attachment-id': interface_id}} + self.client.attach_resource(net_id, port_id, attach_data, + tenant=tenant_id) + + def detach_and_delete_port(self, tenant_id, net_id, port_id): + LOG.debug("Deleteing port %s on net %s for %s" % \ + (port_id, net_id, tenant_id)) + + self.client.detach_resource(net_id, port_id, tenant=tenant_id) + self.client.delete_port(net_id, port_id, tenant=tenant_id) + + # FIXME: this will be inefficient until API implements querying + def get_port_by_attachment(self, tenant_id, attachment_id): + + net_list_resdict = self.client.list_networks(tenant=tenant_id) + for n in net_list_resdict["networks"]: + net_id = n['id'] + port_list_resdict = self.client.list_ports(net_id, + tenant=tenant_id) + for p in port_list_resdict["ports"]: + port_id = p["id"] + port_get_resdict = self.client.show_port_attachment(net_id, + port_id, tenant=tenant_id) + if attachment_id == port_get_resdict["attachment"]: + return (net_id, port_id) + return (None, None) diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py new file mode 100644 index 000000000..378beb6ed --- /dev/null +++ b/nova/tests/test_quantum.py @@ -0,0 +1,261 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Nicira, Inc. +# 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. + +from nova import context +from nova import db +from nova import exception +from nova import log as logging +from nova import test +from nova.network.quantum import manager as quantum_manager + +LOG = logging.getLogger('nova.tests.quantum_network') + +networks = [{'label': 'project1-net1', + 'injected': False, + 'multi_host': False, + 'cidr': '192.168.0.0/24', + 'cidr_v6': '2001:1db8::/64', + 'gateway_v6': '2001:1db8::1', + 'netmask_v6': '64', + 'netmask': '255.255.255.0', + 'bridge': None, + 'bridge_interface': None, + 'gateway': '192.168.0.1', + 'broadcast': '192.168.0.255', + 'dns1': '192.168.0.1', + 'dns2': '192.168.0.2', + 'vlan': None, + 'host': None, + 'vpn_public_address': None, + 'project_id': 'fake_project1'}, + {'label': 'project2-net1', + 'injected': False, + 'multi_host': False, + 'cidr': '192.168.1.0/24', + 'cidr_v6': '2001:1db9::/64', + 'gateway_v6': '2001:1db9::1', + 'netmask_v6': '64', + 'netmask': '255.255.255.0', + 'bridge': None, + 'bridge_interface': None, + 'gateway': '192.168.1.1', + 'broadcast': '192.168.1.255', + 'dns1': '192.168.0.1', + 'dns2': '192.168.0.2', + 'vlan': None, + 'host': None, + 'project_id': 'fake_project2', + 'vpn_public_address': '192.168.1.2'}, + {'label': "public", + 'injected': False, + 'multi_host': False, + 'cidr': '10.0.0.0/24', + 'cidr_v6': '2001:1dba::/64', + 'gateway_v6': '2001:1dba::1', + 'netmask_v6': '64', + 'netmask': '255.255.255.0', + 'bridge': None, + 'bridge_interface': None, + 'gateway': '10.0.0.1', + 'broadcast': '10.0.0.255', + 'dns1': '10.0.0.1', + 'dns2': '10.0.0.2', + 'vlan': None, + 'host': None, + 'vpn_public_address': None, + 'project_id': None}, + {'label': "project2-net2", + 'injected': False, + 'multi_host': False, + 'cidr': '9.0.0.0/24', + 'cidr_v6': '2001:1dbb::/64', + 'gateway_v6': '2001:1dbb::1', + 'netmask_v6': '64', + 'netmask': '255.255.255.0', + 'bridge': None, + 'bridge_interface': None, + 'gateway': '9.0.0.1', + 'broadcast': '9.0.0.255', + 'dns1': '9.0.0.1', + 'dns2': '9.0.0.2', + 'vlan': None, + 'host': None, + 'vpn_public_address': None, + 'project_id': "fake_project2"}] + + +# this is a base class to be used by all other Quantum Test classes +class QuantumTestCaseBase(object): + + def test_create_and_delete_nets(self): + self._create_nets() + self._delete_nets() + + def _create_nets(self): + for n in networks: + ctx = context.RequestContext('user1', n['project_id']) + self.net_man.create_networks(ctx, + label=n['label'], cidr=n['cidr'], + multi_host=n['multi_host'], + num_networks=1, network_size=256, cidr_v6=n['cidr_v6'], + gateway_v6=n['gateway_v6'], bridge=None, + bridge_interface=None, dns1=n['dns1'], + dns2=n['dns2'], project_id=n['project_id']) + + def _delete_nets(self): + for n in networks: + ctx = context.RequestContext('user1', n['project_id']) + self.net_man.delete_network(ctx, n['cidr']) + + def test_allocate_and_deallocate_instance_static(self): + self._create_nets() + + project_id = "fake_project1" + ctx = context.RequestContext('user1', project_id) + + instance_ref = db.api.instance_create(ctx, {}) + nw_info = self.net_man.allocate_for_instance(ctx, + instance_id=instance_ref['id'], host="", + instance_type_id=instance_ref['instance_type_id'], + project_id=project_id) + + self.assertEquals(len(nw_info), 2) + + # we don't know which order the NICs will be in until we + # introduce the notion of priority + # v4 cidr + self.assertTrue(nw_info[0][0]['cidr'].startswith("10.") or \ + nw_info[1][0]['cidr'].startswith("10.")) + self.assertTrue(nw_info[0][0]['cidr'].startswith("192.") or \ + nw_info[1][0]['cidr'].startswith("192.")) + + # v4 address + self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("10.") or \ + nw_info[1][1]['ips'][0]['ip'].startswith("10.")) + self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("192.") or \ + nw_info[1][1]['ips'][0]['ip'].startswith("192.")) + + # v6 cidr + self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1dba:") or \ + nw_info[1][0]['cidr_v6'].startswith("2001:1dba:")) + self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1db8:") or \ + nw_info[1][0]['cidr_v6'].startswith("2001:1db8:")) + + # v6 address + self.assertTrue(\ + nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dba:") or \ + nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1dba:")) + self.assertTrue(\ + nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1db8:") or \ + nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1db8:")) + + self.net_man.deallocate_for_instance(ctx, + instance_id=instance_ref['id'], + project_id=project_id) + + self._delete_nets() + + def test_allocate_and_deallocate_instance_dynamic(self): + self._create_nets() + project_id = "fake_project2" + ctx = context.RequestContext('user1', project_id) + + net_ids = self.net_man.q_conn.get_networks_for_tenant(project_id) + requested_networks = [(net_id, None) for net_id in net_ids] + + self.net_man.validate_networks(ctx, requested_networks) + + instance_ref = db.api.instance_create(ctx, {}) + nw_info = self.net_man.allocate_for_instance(ctx, + instance_id=instance_ref['id'], host="", + instance_type_id=instance_ref['instance_type_id'], + project_id=project_id, + requested_networks=requested_networks) + + self.assertEquals(len(nw_info), 2) + + # we don't know which order the NICs will be in until we + # introduce the notion of priority + # v4 cidr + self.assertTrue(nw_info[0][0]['cidr'].startswith("9.") or \ + nw_info[1][0]['cidr'].startswith("9.")) + self.assertTrue(nw_info[0][0]['cidr'].startswith("192.") or \ + nw_info[1][0]['cidr'].startswith("192.")) + + # v4 address + self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("9.") or \ + nw_info[1][1]['ips'][0]['ip'].startswith("9.")) + self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("192.") or \ + nw_info[1][1]['ips'][0]['ip'].startswith("192.")) + + # v6 cidr + self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1dbb:") or \ + nw_info[1][0]['cidr_v6'].startswith("2001:1dbb:")) + self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1db9:") or \ + nw_info[1][0]['cidr_v6'].startswith("2001:1db9:")) + + # v6 address + self.assertTrue(\ + nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dbb:") or \ + nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1dbb:")) + self.assertTrue(\ + nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1db9:") or \ + nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1db9:")) + + self.net_man.deallocate_for_instance(ctx, + instance_id=instance_ref['id'], + project_id=project_id) + + self._delete_nets() + + def test_validate_bad_network(self): + ctx = context.RequestContext('user1', 'fake_project1') + self.assertRaises(exception.NetworkNotFound, + self.net_man.validate_networks, ctx, [("", None)]) + + +class QuantumFakeIPAMTestCase(QuantumTestCaseBase, test.TestCase): + + def setUp(self): + super(QuantumFakeIPAMTestCase, self).setUp() + self.net_man = quantum_manager.QuantumManager( \ + ipam_lib="nova.network.quantum.fake") + + +class QuantumNovaIPAMTestCase(QuantumTestCaseBase, test.TestCase): + + def setUp(self): + super(QuantumNovaIPAMTestCase, self).setUp() + self.net_man = quantum_manager.QuantumManager( \ + ipam_lib="nova.network.quantum.nova_ipam_lib") + + # tests seem to create some networks by default, which + # don't want. So we delete them. + + ctx = context.RequestContext('user1', 'fake_project1').elevated() + for n in db.network_get_all(ctx): + db.network_delete_safe(ctx, n['id']) + +# Cannot run this unit tests auotmatically for now, as it requires +# melange to be running locally. +# +#class QuantumMelangeIPAMTestCase(QuantumTestCaseBase, test.TestCase): +# +# def setUp(self): +# super(QuantumMelangeIPAMTestCase, self).setUp() +# self.net_man = quantum_manager.QuantumManager( \ +# ipam_lib="nova.network.quantum.melange_ipam_lib") -- cgit From 5823a72690155d9d69e4d23a81be2ea0945809dc Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 25 Aug 2011 21:09:15 -0700 Subject: add priority for static networks --- nova/network/quantum/fake.py | 13 ++++++----- nova/network/quantum/manager.py | 9 ++++---- nova/network/quantum/melange_ipam_lib.py | 30 ++++++++++++++++++++++--- nova/network/quantum/nova_ipam_lib.py | 22 ++++++++++-------- nova/tests/test_quantum.py | 38 ++++++++++++++------------------ 5 files changed, 69 insertions(+), 43 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py index ff2b1e9d5..a923bbf1a 100644 --- a/nova/network/quantum/fake.py +++ b/nova/network/quantum/fake.py @@ -102,8 +102,8 @@ class FakeQuantumIPAMLib(): self.subnets = {} def create_subnet(self, context, label, tenant_id, quantum_net_id, - cidr=None, gateway_v6=None, cidr_v6=None, - dns1=None, dns2=None): + priority, cidr=None, gateway_v6=None, + cidr_v6=None, dns1=None, dns2=None): if int(cidr.split("/")[1]) != 24: raise Exception("fake ipam_lib only supports /24s") v4_ips = [] @@ -116,6 +116,7 @@ class FakeQuantumIPAMLib(): "instance_id": None}) self.subnets[quantum_net_id] = {\ "label": label, + "priority": priority, "gateway": net_start + "1", "netmask": "255.255.255.0", "broadcast": net_start + "255", @@ -138,12 +139,14 @@ class FakeQuantumIPAMLib(): del self.subnets[net_id] def get_project_and_global_net_ids(self, context, project_id): - net_ids = [] + net_list = [] + id_priority_map = {} for nid, s in self.subnets.items(): if s['project_id'] == project_id or \ s['project_id'] == None: - net_ids.append((nid, s['project_id'])) - return net_ids + net_list.append((nid, s['project_id'])) + id_priority_map[nid] = s['priority'] + return sorted(net_list, key=lambda x: id_priority_map[x[0]]) def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): subnet = self.subnets[quantum_net_id] diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index f712a93c4..2c2fd4dd7 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -59,7 +59,7 @@ class QuantumManager(manager.FlatManager): FLAGS.quantum_default_tenant_id quantum_net_id = bridge if quantum_net_id: - if not q_conn.network_exists(q_tenant_id, quantum_net_id): + if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): raise Exception("Unable to find existing quantum " \ " network for tenant '%s' with net-id '%s'" % \ (q_tenant_id, quantum_net_id)) @@ -68,8 +68,9 @@ class QuantumManager(manager.FlatManager): quantum_net_id = self.q_conn.create_network(q_tenant_id, label) ipam_tenant_id = kwargs.get("project_id", None) + priority = kwargs.get("priority", 0) self.ipam.create_subnet(context, label, ipam_tenant_id, quantum_net_id, - cidr, gateway_v6, cidr_v6, dns1, dns2) + priority, cidr, gateway_v6, cidr_v6, dns1, dns2) def delete_network(self, context, fixed_range): project_id = context.project_id @@ -105,7 +106,7 @@ class QuantumManager(manager.FlatManager): # Create a port via quantum and attach the vif for (net_id, project_id) in net_proj_pairs: vif_rec = manager.FlatManager.add_virtual_interface(self, - context, instance, None) + context, instance_id, None) q_tenant_id = project_id or FLAGS.quantum_default_tenant_id self.q_conn.create_and_attach_port(q_tenant_id, net_id, @@ -212,7 +213,7 @@ class QuantumManager(manager.FlatManager): self.ipam.deallocate_ips_by_vif(context, ipam_tenant_id, net_id, vif_ref) - self.net_manager.db.virtual_interface_delete_by_instance(admin_context, + db.virtual_interface_delete_by_instance(admin_context, instance_id) self._do_trigger_security_group_members_refresh_for_instance( instance_id) diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index 46038c349..526be6327 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -36,7 +36,7 @@ class QuantumMelangeIPAMLib: self.m_conn = melange_connection.MelangeConnection() def create_subnet(self, context, label, project_id, - quantum_net_id, cidr=None, + quantum_net_id, priority, cidr=None, gateway_v6=None, cidr_v6=None, dns1=None, dns2=None): tenant_id = project_id or FLAGS.quantum_default_tenant_id @@ -49,6 +49,13 @@ class QuantumMelangeIPAMLib: project_id=tenant_id, dns1=dns1, dns2=dns2) + # create a entry in the network table just to store + # the priority order for this network + net = {"bridge": quantum_net_id, + "project_id": project_id, + "priority": priority} + network = self.db.network_create_safe(context, net) + def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref): tenant_id = project_id or FLAGS.quantum_default_tenant_id self.m_conn.allocate_ip(quantum_net_id, @@ -61,19 +68,26 @@ class QuantumMelangeIPAMLib: for b in all_blocks['ip_blocks']: if b['cidr'] == cidr: return b['network_id'] + raise Exception("No network found for cidr %s" % cidr) def delete_subnets_by_net_id(self, context, net_id, project_id): + admin_context = context.elevated() tenant_id = project_id or FLAGS.quantum_default_tenant_id all_blocks = self.m_conn.get_blocks(tenant_id) for b in all_blocks['ip_blocks']: if b['network_id'] == net_id: self.m_conn.delete_block(b['id'], tenant_id) + network = db.network_get_by_bridge(admin_context, net_id) + if network is not None: + db.network_delete_safe(context, network['id']) + # get all networks with this project_id, as well as all networks # where the project-id is not set (these are shared networks) def get_project_and_global_net_ids(self, context, project_id): + admin_context = context.elevated() id_proj_map = {} - if not project_id: + if project_id is None: raise Exception("get_project_and_global_net_ids must be called" \ " with a non-null project_id") tenant_id = project_id @@ -84,7 +98,17 @@ class QuantumMelangeIPAMLib: all_provider_blocks = self.m_conn.get_blocks(tenant_id) for b in all_provider_blocks['ip_blocks']: id_proj_map[b['network_id']] = tenant_id - return id_proj_map.items() + + id_priority_map = {} + network = db.network_get_by_bridge(admin_context, net_id) + for net_id, project_id in id_project_map.item(): + network = db.network_get_by_bridge(admin_context, net_id) + if network is None: + del id_proj_map[net_id] + else: + id_priority_map[net_id] = network['priority'] + return sorted(id_priority_map.items(), + key=lambda x: id_priority_map[x[0]]) # FIXME: there must be a more efficient way to do this, # talk to the melange folks diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index 0fc74fa49..a9a6dcb29 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -42,10 +42,9 @@ class QuantumNovaIPAMLib: self.net_manager = net_manager def create_subnet(self, context, label, tenant_id, - quantum_net_id, cidr=None, + quantum_net_id, priority, cidr=None, gateway_v6=None, cidr_v6=None, dns1=None, dns2=None): - print "creating subnet %s" % cidr admin_context = context.elevated() subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) manager.FlatManager.create_networks(self.net_manager, @@ -53,9 +52,10 @@ class QuantumNovaIPAMLib: False, 1, subnet_size, cidr_v6, gateway_v6, quantum_net_id, None, dns1, dns2) - # now grab the network and update project_id + # now grab the network and update project_id + priority network = db.network_get_by_bridge(admin_context, quantum_net_id) - net = {"project_id": tenant_id} + net = {"project_id": tenant_id, + "priority": priority} db.network_update(admin_context, network['id'], net) def get_network_id_by_cidr(self, context, cidr, project_id): @@ -82,7 +82,13 @@ class QuantumNovaIPAMLib: admin_context = context.elevated() networks = db.project_get_networks(admin_context, project_id, False) networks.extend(db.project_get_networks(admin_context, None, False)) - return [(n['bridge'], n['project_id']) for n in networks] + id_priority_map = {} + net_list = [] + for n in networks: + net_id = n['bridge'] + net_list.append((net_id, n["project_id"])) + id_priority_map[net_id] = n['priority'] + return sorted(net_list, key=lambda x: id_priority_map[x[0]]) def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): admin_context = context.elevated() @@ -104,8 +110,7 @@ class QuantumNovaIPAMLib: 'broadcast': n['broadcast'], 'netmask': n['netmask'], 'dns1': n['dns1'], - 'dns2': n['dns2'] - } + 'dns2': n['dns2']} subnet_data_v6 = { 'network_id': n['bridge'], 'cidr': n['cidr_v6'], @@ -113,8 +118,7 @@ class QuantumNovaIPAMLib: 'broadcast': None, 'netmask': None, 'dns1': None, - 'dns2': None - } + 'dns2': None} return (subnet_data_v4, subnet_data_v6) def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 378beb6ed..80cab950e 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -41,7 +41,8 @@ networks = [{'label': 'project1-net1', 'vlan': None, 'host': None, 'vpn_public_address': None, - 'project_id': 'fake_project1'}, + 'project_id': 'fake_project1', + 'priority': 1}, {'label': 'project2-net1', 'injected': False, 'multi_host': False, @@ -59,7 +60,7 @@ networks = [{'label': 'project1-net1', 'vlan': None, 'host': None, 'project_id': 'fake_project2', - 'vpn_public_address': '192.168.1.2'}, + 'priority': 1}, {'label': "public", 'injected': False, 'multi_host': False, @@ -76,8 +77,8 @@ networks = [{'label': 'project1-net1', 'dns2': '10.0.0.2', 'vlan': None, 'host': None, - 'vpn_public_address': None, - 'project_id': None}, + 'project_id': None, + 'priority': 0}, {'label': "project2-net2", 'injected': False, 'multi_host': False, @@ -94,8 +95,8 @@ networks = [{'label': 'project1-net1', 'dns2': '9.0.0.2', 'vlan': None, 'host': None, - 'vpn_public_address': None, - 'project_id': "fake_project2"}] + 'project_id': "fake_project2", + 'priority': 2}] # this is a base class to be used by all other Quantum Test classes @@ -114,7 +115,8 @@ class QuantumTestCaseBase(object): num_networks=1, network_size=256, cidr_v6=n['cidr_v6'], gateway_v6=n['gateway_v6'], bridge=None, bridge_interface=None, dns1=n['dns1'], - dns2=n['dns2'], project_id=n['project_id']) + dns2=n['dns2'], project_id=n['project_id'], + priority=n['priority']) def _delete_nets(self): for n in networks: @@ -138,29 +140,21 @@ class QuantumTestCaseBase(object): # we don't know which order the NICs will be in until we # introduce the notion of priority # v4 cidr - self.assertTrue(nw_info[0][0]['cidr'].startswith("10.") or \ - nw_info[1][0]['cidr'].startswith("10.")) - self.assertTrue(nw_info[0][0]['cidr'].startswith("192.") or \ - nw_info[1][0]['cidr'].startswith("192.")) + self.assertTrue(nw_info[0][0]['cidr'].startswith("10.")) + self.assertTrue(nw_info[1][0]['cidr'].startswith("192.")) # v4 address - self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("10.") or \ - nw_info[1][1]['ips'][0]['ip'].startswith("10.")) - self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("192.") or \ - nw_info[1][1]['ips'][0]['ip'].startswith("192.")) + self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("10.")) + self.assertTrue(nw_info[1][1]['ips'][0]['ip'].startswith("192.")) # v6 cidr - self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1dba:") or \ - nw_info[1][0]['cidr_v6'].startswith("2001:1dba:")) - self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1db8:") or \ - nw_info[1][0]['cidr_v6'].startswith("2001:1db8:")) + self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1dba:")) + self.assertTrue(nw_info[1][0]['cidr_v6'].startswith("2001:1db8:")) # v6 address self.assertTrue(\ - nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dba:") or \ - nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1dba:")) + nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dba:")) self.assertTrue(\ - nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1db8:") or \ nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1db8:")) self.net_man.deallocate_for_instance(ctx, -- cgit From 16290908eaca73caa6b3f2ce36fb8add0b7d3615 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 25 Aug 2011 22:45:24 -0700 Subject: fix for quantum api changes, change nova-mange to have quantum_list command --- nova/network/quantum/client.py | 2 +- nova/network/quantum/quantum_connection.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index a0c7dc6d8..b57294c55 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -63,7 +63,7 @@ class Client(object): """A base client class - derived from Glance.BaseClient""" - action_prefix = '/v0.1/tenants/{tenant_id}' + action_prefix = '/v1.0/tenants/{tenant_id}' """Action query strings""" networks_path = "/networks" diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index 3aa017bcd..d6f749bf2 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -46,9 +46,9 @@ class QuantumClientConnection: logger=LOG) def create_network(self, tenant_id, network_name): - data = {'network': {'net-name': network_name}} + data = {'network': {'name': network_name}} resdict = self.client.create_network(data, tenant=tenant_id) - return resdict["networks"]["network"]["id"] + return resdict["network"]["id"] def delete_network(self, tenant_id, net_id): self.client.delete_network(net_id, tenant=tenant_id) -- cgit From e8d02ac1b5e9a45cc19992d232d4148f9db720ca Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 25 Aug 2011 23:02:46 -0700 Subject: rearrange imports --- .../migrate_repo/versions/041_add_network_priority.py | 5 ++--- nova/network/quantum/fake.py | 5 +++-- nova/network/quantum/manager.py | 2 +- nova/network/quantum/melange_connection.py | 1 + nova/network/quantum/melange_ipam_lib.py | 1 + nova/network/quantum/nova_ipam_lib.py | 4 ++-- nova/network/quantum/quantum_connection.py | 10 +++++----- nova/tests/test_quantum.py | 2 +- 8 files changed, 16 insertions(+), 14 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py index e619b1fcd..e69380199 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py @@ -1,4 +1,4 @@ -# Copyright 2011 OpenStack LLC. +# Copyright 2011 Nicira, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -13,14 +13,13 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime - from sqlalchemy import * from migrate import * from nova import log as logging from nova import utils + meta = MetaData() # Add priority column to networks table diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py index a923bbf1a..3f6996e63 100644 --- a/nova/network/quantum/fake.py +++ b/nova/network/quantum/fake.py @@ -15,12 +15,13 @@ # License for the specific language governing permissions and limitations # under the License. +import math +from netaddr import IPNetwork + from nova import exception from nova import ipv6 from nova import log as logging from nova import utils -import math -from netaddr import IPNetwork LOG = logging.getLogger("network.quantum.fake") diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 2c2fd4dd7..e49d0cfdc 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -20,10 +20,10 @@ from nova import exception from nova import flags from nova import log as logging from nova import manager -from nova import utils from nova.network import manager from nova.network.quantum import quantum_connection from nova.network.quantum import fake +from nova import utils LOG = logging.getLogger("quantum_manager") diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index b3955138d..2d884fa60 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -19,6 +19,7 @@ import httplib import socket import urllib import json + from nova import flags diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index 526be6327..5b58520c2 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -21,6 +21,7 @@ from nova import flags from nova import log as logging from nova.network.quantum import melange_connection + LOG = logging.getLogger("quantum_melange_ipam") FLAGS = flags.FLAGS diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index a9a6dcb29..b72edeeff 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -17,15 +17,15 @@ import math -#from nova import context from nova import db from nova import exception from nova import flags from nova import ipv6 from nova import log as logging -from nova import utils from nova.network import manager from nova.network.quantum import melange_connection as melange +from nova import utils + LOG = logging.getLogger("quantum_nova_ipam_lib") diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index d6f749bf2..7a96d23fd 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -17,9 +17,9 @@ from nova import flags from nova import log as logging +from nova.network.quantum import client as quantum_client from nova import utils -from nova.network.quantum.client import Client LOG = logging.getLogger("nova.network.quantum") FLAGS = flags.FLAGS @@ -40,10 +40,10 @@ flags.DEFINE_string('quantum_default_tenant_id', class QuantumClientConnection: def __init__(self): - self.client = Client(FLAGS.quantum_connection_host, - FLAGS.quantum_connection_port, - format="json", - logger=LOG) + self.client = quantum_client.Client(FLAGS.quantum_connection_host, + FLAGS.quantum_connection_port, + format="json", + logger=LOG) def create_network(self, tenant_id, network_name): data = {'network': {'name': network_name}} diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 80cab950e..8ef22ac7e 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -19,8 +19,8 @@ from nova import context from nova import db from nova import exception from nova import log as logging -from nova import test from nova.network.quantum import manager as quantum_manager +from nova import test LOG = logging.getLogger('nova.tests.quantum_network') -- cgit From 3a91fa89ea23d22cd34336aa9281a439579d4ce0 Mon Sep 17 00:00:00 2001 From: Brad Hall Date: Fri, 26 Aug 2011 15:48:53 -0700 Subject: Minor changes based on recent quantum changes --- nova/network/quantum/client.py | 3 ++- nova/network/quantum/manager.py | 3 ++- nova/network/quantum/quantum_connection.py | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index b57294c55..613369c7d 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -169,7 +169,8 @@ class Client(object): httplib.CREATED, httplib.ACCEPTED, httplib.NO_CONTENT): - return self.deserialize(data, status_code) + if data is not None and len(data): + return self.deserialize(data, status_code) else: raise Exception("Server returned error: %s" % res.read()) diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index e49d0cfdc..79bb65939 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -119,7 +119,8 @@ class QuantumManager(manager.FlatManager): def get_instance_nw_info(self, context, instance_id, instance_type_id, host): network_info = [] - project_id = context.project_id + instance = db.instance_get(context, instance_id) + project_id = instance.project_id admin_context = context.elevated() vifs = db.virtual_interface_get_by_instance(admin_context, diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index 7a96d23fd..c0eaad1fd 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -67,9 +67,9 @@ class QuantumClientConnection: (interface_id, net_id, tenant_id)) port_data = {'port': {'port-state': 'ACTIVE'}} resdict = self.client.create_port(net_id, port_data, tenant=tenant_id) - port_id = resdict["ports"]["port"]["id"] + port_id = resdict["port"]["id"] - attach_data = {'port': {'attachment-id': interface_id}} + attach_data = {'attachment': {'id': interface_id}} self.client.attach_resource(net_id, port_id, attach_data, tenant=tenant_id) @@ -92,6 +92,6 @@ class QuantumClientConnection: port_id = p["id"] port_get_resdict = self.client.show_port_attachment(net_id, port_id, tenant=tenant_id) - if attachment_id == port_get_resdict["attachment"]: + if attachment_id == port_get_resdict["attachment"]["id"]: return (net_id, port_id) return (None, None) -- cgit From d5b489383710605b10067550417a4e62a5f4f3e1 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Sun, 28 Aug 2011 11:37:19 -0700 Subject: use 'uuid' field in networks table rather than 'bridge'. Specify project_id when creating instance in unit test --- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 13 +++++++++++++ nova/exception.py | 4 ++++ nova/network/quantum/manager.py | 5 +++-- nova/network/quantum/nova_ipam_lib.py | 30 +++++++++++++++++------------- nova/tests/test_quantum.py | 6 ++++-- 6 files changed, 46 insertions(+), 17 deletions(-) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index 9ff3a1c74..17ef0bd0b 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -708,6 +708,11 @@ def network_get_by_bridge(context, bridge): return IMPL.network_get_by_bridge(context, bridge) +def network_get_by_uuid(context, uuid): + """Get a network by uuid or raise if it does not exist.""" + return IMPL.network_get_by_uuid(context, uuid) + + def network_get_by_cidr(context, cidr): """Get a network by cidr or raise if it does not exist""" return IMPL.network_get_by_cidr(context, cidr) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index d96b951a1..80ce76e8f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1846,6 +1846,19 @@ def network_get_by_bridge(context, bridge): return result +@require_admin_context +def network_get_by_uuid(context, uuid): + session = get_session() + result = session.query(models.Network).\ + filter_by(uuid=uuid).\ + filter_by(deleted=False).\ + first() + + if not result: + raise exception.NetworkNotFoundForUUID(uuid=uuid) + return result + + @require_admin_context def network_get_by_cidr(context, cidr): session = get_session() diff --git a/nova/exception.py b/nova/exception.py index 66740019b..8d6e84d74 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -411,6 +411,10 @@ class NetworkNotFoundForBridge(NetworkNotFound): message = _("Network could not be found for bridge %(bridge)s") +class NetworkNotFoundForUUID(NetworkNotFound): + message = _("Network could not be found for uuid %(uuid)s") + + class NetworkNotFoundForCidr(NetworkNotFound): message = _("Network could not be found with cidr %(cidr)s.") diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 79bb65939..932bdd82f 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -51,13 +51,14 @@ class QuantumManager(manager.FlatManager): def create_networks(self, context, label, cidr, multi_host, num_networks, network_size, cidr_v6, gateway_v6, bridge, - bridge_interface, dns1=None, dns2=None, **kwargs): + bridge_interface, dns1=None, dns2=None, uuid=None, + **kwargs): if num_networks != 1: raise Exception("QuantumManager requires that only one" " network is created per call") q_tenant_id = kwargs["project_id"] or \ FLAGS.quantum_default_tenant_id - quantum_net_id = bridge + quantum_net_id = uuid if quantum_net_id: if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): raise Exception("Unable to find existing quantum " \ diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index b72edeeff..5bca9d024 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -47,15 +47,19 @@ class QuantumNovaIPAMLib: dns1=None, dns2=None): admin_context = context.elevated() subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) - manager.FlatManager.create_networks(self.net_manager, + networks = manager.FlatManager.create_networks(self.net_manager, admin_context, label, cidr, False, 1, subnet_size, cidr_v6, gateway_v6, quantum_net_id, None, dns1, dns2) - # now grab the network and update project_id + priority - network = db.network_get_by_bridge(admin_context, quantum_net_id) + if len(networks) != 1: + raise Exception("Error creating network entry") + + # now grab the network and update uuid, project_id, priority + network = networks[0] net = {"project_id": tenant_id, - "priority": priority} + "priority": priority, + "uuid": quantum_net_id} db.network_update(admin_context, network['id'], net) def get_network_id_by_cidr(self, context, cidr, project_id): @@ -64,11 +68,11 @@ class QuantumNovaIPAMLib: if not network: raise Exception("No network with fixed_range = %s" \ % fixed_range) - return network['bridge'] + return network['uuid'] def delete_subnets_by_net_id(self, context, net_id, project_id): admin_context = context.elevated() - network = db.network_get_by_bridge(admin_context, net_id) + network = db.network_get_by_uuid(admin_context, net_id) if not network: raise Exception("No network with net_id = %s" % net_id) manager.FlatManager.delete_network(self.net_manager, @@ -85,14 +89,14 @@ class QuantumNovaIPAMLib: id_priority_map = {} net_list = [] for n in networks: - net_id = n['bridge'] + net_id = n['uuid'] net_list.append((net_id, n["project_id"])) id_priority_map[net_id] = n['priority'] return sorted(net_list, key=lambda x: id_priority_map[x[0]]) def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): admin_context = context.elevated() - network = db.network_get_by_bridge(admin_context, quantum_net_id) + network = db.network_get_by_uuid(admin_context, quantum_net_id) if network['cidr']: address = db.fixed_ip_associate_pool(admin_context, network['id'], @@ -102,9 +106,9 @@ class QuantumNovaIPAMLib: db.fixed_ip_update(admin_context, address, values) def get_subnets_by_net_id(self, context, tenant_id, net_id): - n = db.network_get_by_bridge(context.elevated(), net_id) + n = db.network_get_by_uuid(context.elevated(), net_id) subnet_data_v4 = { - 'network_id': n['bridge'], + 'network_id': n['uuid'], 'cidr': n['cidr'], 'gateway': n['gateway'], 'broadcast': n['broadcast'], @@ -112,7 +116,7 @@ class QuantumNovaIPAMLib: 'dns1': n['dns1'], 'dns2': n['dns2']} subnet_data_v6 = { - 'network_id': n['bridge'], + 'network_id': n['uuid'], 'cidr': n['cidr_v6'], 'gateway': n['gateway_v6'], 'broadcast': None, @@ -129,7 +133,7 @@ class QuantumNovaIPAMLib: def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): admin_context = context.elevated() - network = db.network_get_by_bridge(admin_context, net_id) + network = db.network_get_by_uuid(admin_context, net_id) vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) if network['cidr_v6']: ip = ipv6.to_global(network['cidr_v6'], @@ -140,7 +144,7 @@ class QuantumNovaIPAMLib: def verify_subnet_exists(self, context, tenant_id, quantum_net_id): admin_context = context.elevated() - network = db.network_get_by_bridge(admin_context, quantum_net_id) + network = db.network_get_by_uuid(admin_context, quantum_net_id) def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref): try: diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 8ef22ac7e..ae9900b67 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -129,7 +129,8 @@ class QuantumTestCaseBase(object): project_id = "fake_project1" ctx = context.RequestContext('user1', project_id) - instance_ref = db.api.instance_create(ctx, {}) + instance_ref = db.api.instance_create(ctx, + {"project_id": project_id}) nw_info = self.net_man.allocate_for_instance(ctx, instance_id=instance_ref['id'], host="", instance_type_id=instance_ref['instance_type_id'], @@ -173,7 +174,8 @@ class QuantumTestCaseBase(object): self.net_man.validate_networks(ctx, requested_networks) - instance_ref = db.api.instance_create(ctx, {}) + instance_ref = db.api.instance_create(ctx, + {"project_id": project_id}) nw_info = self.net_man.allocate_for_instance(ctx, instance_id=instance_ref['id'], host="", instance_type_id=instance_ref['instance_type_id'], -- cgit From 1bfc7ce80c3936a19434dfc45c44f8a1acfd65ed Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Sun, 28 Aug 2011 12:53:32 -0700 Subject: fix issue with setting 'Active' caused by Quantum API changes. Other misc fixes --- nova/network/quantum/quantum_connection.py | 6 +++--- nova/virt/libvirt/vif.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index c0eaad1fd..a13867af2 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -65,7 +65,7 @@ class QuantumClientConnection: def create_and_attach_port(self, tenant_id, net_id, interface_id): LOG.debug("Connecting interface %s to net %s for %s" % \ (interface_id, net_id, tenant_id)) - port_data = {'port': {'port-state': 'ACTIVE'}} + port_data = {'port': {'state': 'ACTIVE'}} resdict = self.client.create_port(net_id, port_data, tenant=tenant_id) port_id = resdict["port"]["id"] @@ -74,13 +74,13 @@ class QuantumClientConnection: tenant=tenant_id) def detach_and_delete_port(self, tenant_id, net_id, port_id): - LOG.debug("Deleteing port %s on net %s for %s" % \ + LOG.debug("Deleting port %s on net %s for %s" % \ (port_id, net_id, tenant_id)) self.client.detach_resource(net_id, port_id, tenant=tenant_id) self.client.delete_port(net_id, port_id, tenant=tenant_id) - # FIXME: this will be inefficient until API implements querying + # FIXME: (danwent) this will be inefficient until API implements querying def get_port_by_attachment(self, tenant_id, attachment_id): net_list_resdict = self.client.list_networks(tenant=tenant_id) diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 0b7438011..077c32474 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -101,7 +101,7 @@ class LibvirtOpenVswitchDriver(VIFDriver): """VIF driver for Open vSwitch.""" def get_dev_name(_self, iface_id): - return "tap-" + iface_id[0:15] + return "tap" + iface_id[0:11] def plug(self, instance, network, mapping): iface_id = mapping['vif_uuid'] -- cgit From 56891283f117997042363aee2e3ce00a5a12d9e0 Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Sun, 28 Aug 2011 15:39:27 -0700 Subject: update melange ipam lib to use network uuid, not bridge --- nova/network/quantum/melange_ipam_lib.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index 5b58520c2..cfdcde045 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -52,7 +52,7 @@ class QuantumMelangeIPAMLib: # create a entry in the network table just to store # the priority order for this network - net = {"bridge": quantum_net_id, + net = {"uuid": quantum_net_id, "project_id": project_id, "priority": priority} network = self.db.network_create_safe(context, net) @@ -79,7 +79,7 @@ class QuantumMelangeIPAMLib: if b['network_id'] == net_id: self.m_conn.delete_block(b['id'], tenant_id) - network = db.network_get_by_bridge(admin_context, net_id) + network = db.network_get_by_uuid(admin_context, net_id) if network is not None: db.network_delete_safe(context, network['id']) @@ -101,9 +101,8 @@ class QuantumMelangeIPAMLib: id_proj_map[b['network_id']] = tenant_id id_priority_map = {} - network = db.network_get_by_bridge(admin_context, net_id) for net_id, project_id in id_project_map.item(): - network = db.network_get_by_bridge(admin_context, net_id) + network = db.network_get_by_uuid(admin_context, net_id) if network is None: del id_proj_map[net_id] else: @@ -111,8 +110,11 @@ class QuantumMelangeIPAMLib: return sorted(id_priority_map.items(), key=lambda x: id_priority_map[x[0]]) - # FIXME: there must be a more efficient way to do this, - # talk to the melange folks + # FIXME: (danwent) Melange actually returns the subnet info + # when we query for a particular interface. we may want to + # reworks the ipam_manager python API to let us take advantage of + # this, as right now we have to get all blocks and cycle through + # them. def get_subnets_by_net_id(self, context, project_id, net_id): subnet_v4 = None subnet_v6 = None -- cgit From 431cd5d17780aa7ea9d03b028a78ec4e20b22440 Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Sun, 28 Aug 2011 17:37:07 -0700 Subject: always set network_id in virtual_interfaces table, otherwise API commands that show IP addresses get confused --- nova/network/quantum/manager.py | 24 +++++++++++++++++++----- nova/network/quantum/melange_ipam_lib.py | 11 ++++++++--- nova/network/quantum/nova_ipam_lib.py | 2 +- 3 files changed, 28 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 932bdd82f..fb13a8496 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -105,14 +105,28 @@ class QuantumManager(manager.FlatManager): project_id) # Create a port via quantum and attach the vif - for (net_id, project_id) in net_proj_pairs: + for (quantum_net_id, project_id) in net_proj_pairs: + + # FIXME: (danwent). We'd like to have the manager be completely + # decoupled from the nova networks table. + # However, other parts of nova sometimes go behind + # our back and access network data directly from the DB. So + # for now, the quantum manager knows that there is a nova + # networks DB table and accesses it here. + # updating the virtual_interfaces table to use UUIDs would + # be one solution, but this would require significant work + # elsewhere. + network_ref = db.network_get_by_uuid(context, quantum_net_id) + vif_rec = manager.FlatManager.add_virtual_interface(self, - context, instance_id, None) + context, instance_id, network_ref['id']) + # talk to Quantum API to create and attach port. q_tenant_id = project_id or FLAGS.quantum_default_tenant_id - self.q_conn.create_and_attach_port(q_tenant_id, net_id, + self.q_conn.create_and_attach_port(q_tenant_id, quantum_net_id, vif_rec['uuid']) - self.ipam.allocate_fixed_ip(context, project_id, net_id, vif_rec) + self.ipam.allocate_fixed_ip(context, project_id, quantum_net_id, + vif_rec) return self.get_instance_nw_info(context, instance_id, instance_type_id, host) @@ -173,7 +187,7 @@ class QuantumManager(manager.FlatManager): if v6_subnet['cidr']: network_dict['cidr_v6'] = v6_subnet['cidr'] info['ip6s'] = [ip_dict(ip, v6_subnet) for ip in v6_ips] - # TODO(tr3buchet): handle ip6 routes here as well + if v6_subnet['gateway']: info['gateway6'] = v6_subnet['gateway'] diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index cfdcde045..e2e09f139 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -50,11 +50,16 @@ class QuantumMelangeIPAMLib: project_id=tenant_id, dns1=dns1, dns2=dns2) - # create a entry in the network table just to store - # the priority order for this network + # create a entry in the network table, even though + # most data is stored in melange. This is used to + # store data not kept by melange (e.g., priority) + # and to 'fake' other parts of nova (e.g., the API) + # until we get get all accesses to be via the + # network manager API. net = {"uuid": quantum_net_id, "project_id": project_id, - "priority": priority} + "priority": priority, + "label": label} network = self.db.network_create_safe(context, net) def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref): diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index 5bca9d024..6e7e5d244 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -55,7 +55,7 @@ class QuantumNovaIPAMLib: if len(networks) != 1: raise Exception("Error creating network entry") - # now grab the network and update uuid, project_id, priority + # now grab the network and update additional fields network = networks[0] net = {"project_id": tenant_id, "priority": priority, -- cgit From 822d92ed1f6a5f2f0951c5e43be6ce0c8fb75e65 Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Sun, 28 Aug 2011 19:12:43 -0700 Subject: remove fake IPAM lib, since qmanager must now access nova DB directly --- nova/network/quantum/fake.py | 124 ------------------------------------------- nova/tests/test_quantum.py | 8 --- 2 files changed, 132 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py index 3f6996e63..00cdd8e08 100644 --- a/nova/network/quantum/fake.py +++ b/nova/network/quantum/fake.py @@ -91,127 +91,3 @@ class FakeQuantumClientConnection: return (net_id, port_id) return (None, None) - - -def get_ipam_lib(net_man): - return FakeQuantumIPAMLib() - - -class FakeQuantumIPAMLib(): - - def __init__(self): - self.subnets = {} - - def create_subnet(self, context, label, tenant_id, quantum_net_id, - priority, cidr=None, gateway_v6=None, - cidr_v6=None, dns1=None, dns2=None): - if int(cidr.split("/")[1]) != 24: - raise Exception("fake ipam_lib only supports /24s") - v4_ips = [] - net_start = cidr[0:cidr.rfind(".") + 1] - subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) - for i in xrange(2, subnet_size - 1): - v4_ips.append({"ip": net_start + str(i), - "allocated": False, - "virtual_interface_id": None, - "instance_id": None}) - self.subnets[quantum_net_id] = {\ - "label": label, - "priority": priority, - "gateway": net_start + "1", - "netmask": "255.255.255.0", - "broadcast": net_start + "255", - "cidr": cidr, - "gateway_v6": gateway_v6, - "cidr_v6": cidr_v6, - "dns1": dns1, - "dns2": dns2, - "project_id": tenant_id, - "v4_ips": v4_ips} - - def get_network_id_by_cidr(self, context, cidr, project_id): - for net_id, s in self.subnets.items(): - if s['cidr'] == cidr or s['cidr_v6'] == cidr: - return net_id - return None - - def delete_subnets_by_net_id(self, context, net_id, project_id): - self.verify_subnet_exists(context, project_id, net_id) - del self.subnets[net_id] - - def get_project_and_global_net_ids(self, context, project_id): - net_list = [] - id_priority_map = {} - for nid, s in self.subnets.items(): - if s['project_id'] == project_id or \ - s['project_id'] == None: - net_list.append((nid, s['project_id'])) - id_priority_map[nid] = s['priority'] - return sorted(net_list, key=lambda x: id_priority_map[x[0]]) - - def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): - subnet = self.subnets[quantum_net_id] - for i in xrange(0, len(subnet['v4_ips'])): - ip = subnet['v4_ips'][i] - if not ip['allocated']: - subnet['v4_ips'][i] = {'ip': ip['ip'], - 'allocated': True, - 'virtual_interface_id': vif_rec['uuid'], - 'instance_id': vif_rec['instance_id']} - return - raise Exception("Unable to find available IP for net '%s'" %\ - quantum_net_id) - - def get_subnets_by_net_id(self, context, tenant_id, net_id): - self.verify_subnet_exists(context, tenant_id, net_id) - - subnet_data = self.subnets[net_id] - subnet_data_v4 = { - 'network_id': net_id, - 'cidr': subnet_data['cidr'], - 'gateway': subnet_data['gateway'], - 'broadcast': subnet_data['broadcast'], - 'netmask': subnet_data['netmask'], - 'dns1': subnet_data['dns1'], - 'dns2': subnet_data['dns2']} - subnet_data_v6 = { - 'network_id': net_id, - 'cidr': subnet_data['cidr_v6'], - 'gateway': subnet_data['gateway_v6'], - 'broadcast': None, - 'netmask': None, - 'dns1': None, - 'dns2': None} - return (subnet_data_v4, subnet_data_v6) - - def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): - self.verify_subnet_exists(context, project_id, net_id) - - subnet_data = self.subnets[net_id] - if subnet_data['cidr_v6']: - ip = ipv6.to_global(subnet_data['cidr_v6'], - "ca:fe:de:ad:be:ef", - project_id) - return [ip] - return [] - - def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): - self.verify_subnet_exists(context, project_id, net_id) - - subnet_data = self.subnets[net_id] - for ip in subnet_data['v4_ips']: - if ip['virtual_interface_id'] == vif_id: - return [ip['ip']] - return [] - - def verify_subnet_exists(self, context, tenant_id, quantum_net_id): - if quantum_net_id not in self.subnets: - raise exception.NetworkNotFound(network_id=quantum_net_id) - - def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref): - s = self.subnets[net_id] - for ip in s['v4_ips']: - if ip['virtual_interface_id'] == vif_ref['id']: - ip['allocated'] = False - ip['instance_id'] = None - ip['virtual_interface_id'] = None diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index ae9900b67..e7a8a01fa 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -224,14 +224,6 @@ class QuantumTestCaseBase(object): self.net_man.validate_networks, ctx, [("", None)]) -class QuantumFakeIPAMTestCase(QuantumTestCaseBase, test.TestCase): - - def setUp(self): - super(QuantumFakeIPAMTestCase, self).setUp() - self.net_man = quantum_manager.QuantumManager( \ - ipam_lib="nova.network.quantum.fake") - - class QuantumNovaIPAMTestCase(QuantumTestCaseBase, test.TestCase): def setUp(self): -- cgit From 716303049eaee59841ca4679d73ecb4e5be52cfd Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Sun, 28 Aug 2011 19:13:02 -0700 Subject: add doc-strings for all major modules --- nova/network/quantum/manager.py | 97 +++++++++++++++++++++++++----- nova/network/quantum/melange_ipam_lib.py | 81 ++++++++++++++++++------- nova/network/quantum/nova_ipam_lib.py | 85 ++++++++++++++++++-------- nova/network/quantum/quantum_connection.py | 28 ++++++++- 4 files changed, 226 insertions(+), 65 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index fb13a8496..a002a3d7b 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -35,8 +35,27 @@ flags.DEFINE_string('quantum_ipam_lib', class QuantumManager(manager.FlatManager): + """ NetworkManager class that communicates with a Quantum service + via a web services API to provision VM network connectivity. + + For IP Address management, QuantumManager can be configured to + use either Nova's local DB or the Melange IPAM service. + + Currently, the QuantumManager does NOT support any of the 'gateway' + functionality implemented by the Nova VlanManager, including: + * floating IPs + * DHCP + * NAT gateway + + Support for these capabilities are targted for future releases. + """ def __init__(self, ipam_lib=None, *args, **kwargs): + """ Initialize two key libraries, the connection to a + Quantum service, and the library for implementing IPAM. + + Calls inherited FlatManager constructor. + """ if FLAGS.fake_network: self.q_conn = fake.FakeQuantumClientConnection() @@ -53,6 +72,17 @@ class QuantumManager(manager.FlatManager): network_size, cidr_v6, gateway_v6, bridge, bridge_interface, dns1=None, dns2=None, uuid=None, **kwargs): + """ Unlike other NetworkManagers, with QuantumManager, each + create_networks calls should create only a single network. + + Two scenarios exist: + - no 'uuid' is specified, in which case we contact + Quantum and create a new network. + - an existing 'uuid' is specified, corresponding to + a Quantum network created out of band. + + In both cases, we initialize a subnet using the IPAM lib. + """ if num_networks != 1: raise Exception("QuantumManager requires that only one" " network is created per call") @@ -74,27 +104,46 @@ class QuantumManager(manager.FlatManager): priority, cidr, gateway_v6, cidr_v6, dns1, dns2) def delete_network(self, context, fixed_range): + """ Lookup network by IPv4 cidr, delete both the IPAM + subnet and the corresponding Quantum network. + """ project_id = context.project_id quantum_net_id = self.ipam.get_network_id_by_cidr( context, fixed_range, project_id) self.ipam.delete_subnets_by_net_id(context, quantum_net_id, project_id) - try: - q_tenant_id = project_id or FLAGS.quantum_default_tenant_id - self.q_conn.delete_network(q_tenant_id, quantum_net_id) - except Exception, e: - raise Exception("Unable to delete Quantum Network with " - "fixed_range = %s (ports still in use?)." % fixed_range) + q_tenant_id = project_id or FLAGS.quantum_default_tenant_id + self.q_conn.delete_network(q_tenant_id, quantum_net_id) def allocate_for_instance(self, context, **kwargs): + """ Called by compute when it is creating a new VM. + + There are three key tasks: + - Determine the number and order of vNICs to create + - Allocate IP addresses + - Create ports on a Quantum network and attach vNICs. + + We support two approaches to determining vNICs: + - By default, a VM gets a vNIC for any network belonging + to the VM's project, and a vNIC for any "global" network + that has a NULL project_id. vNIC order is determined + by the network's 'priority' field. + - If the 'os-create-server-ext' was used to create the VM, + only the networks in 'requested_networks' are used to + create vNICs, and the vNIC order is determiend by the + order in the requested_networks array. + + For each vNIC, use the FlatManager to create the entries + in the virtual_interfaces table, contact Quantum to + create a port and attachment the vNIC, and use the IPAM + lib to allocate IP addresses. + """ instance_id = kwargs.pop('instance_id') instance_type_id = kwargs['instance_type_id'] host = kwargs.pop('host') project_id = kwargs.pop('project_id') LOG.debug(_("network allocations for instance %s"), instance_id) - # if using the create-server-networks extension, 'requested_networks' - # will be defined, otherwise, just grab relevant nets from IPAM requested_networks = kwargs.get('requested_networks') if requested_networks: @@ -116,10 +165,12 @@ class QuantumManager(manager.FlatManager): # updating the virtual_interfaces table to use UUIDs would # be one solution, but this would require significant work # elsewhere. - network_ref = db.network_get_by_uuid(context, quantum_net_id) + admin_context = context.elevated() + network_ref = db.network_get_by_uuid(admin_context, + quantum_net_id) vif_rec = manager.FlatManager.add_virtual_interface(self, - context, instance_id, network_ref['id']) + context, instance_id, network_ref['id']) # talk to Quantum API to create and attach port. q_tenant_id = project_id or FLAGS.quantum_default_tenant_id @@ -133,6 +184,18 @@ class QuantumManager(manager.FlatManager): def get_instance_nw_info(self, context, instance_id, instance_type_id, host): + """ This method is used by compute to fetch all network data + that should be used when creating the VM. + + The method simply loops through all virtual interfaces + stored in the nova DB and queries the IPAM lib to get + the associated IP data. + + The format of returned data is 'defined' by the initial + set of NetworkManagers found in nova/network/manager.py . + Ideally this 'interface' will be more formally defined + in the future. + """ network_info = [] instance = db.instance_get(context, instance_id) project_id = instance.project_id @@ -202,6 +265,11 @@ class QuantumManager(manager.FlatManager): return network_info def deallocate_for_instance(self, context, **kwargs): + """ Called when a VM is terminated. Loop through each virtual + interface in the Nova DB and remove the Quantum port and + clear the IP allocation using the IPAM. Finally, remove + the virtual interfaces from the Nova DB. + """ instance_id = kwargs.get('instance_id') project_id = kwargs.pop('project_id', None) @@ -234,11 +302,12 @@ class QuantumManager(manager.FlatManager): self._do_trigger_security_group_members_refresh_for_instance( instance_id) - # validates that this tenant has quantum networks with the associated - # UUIDs. This is called by the 'os-create-server-ext' API extension - # code so that we can return an API error code to the caller if they - # request an invalid network. def validate_networks(self, context, networks): + """ Validates that this tenant has quantum networks with the associated + UUIDs. This is called by the 'os-create-server-ext' API extension + code so that we can return an API error code to the caller if they + request an invalid network. + """ if networks is None: return diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index e2e09f139..7b7baf281 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -32,43 +32,54 @@ def get_ipam_lib(net_man): class QuantumMelangeIPAMLib: + """ Implements Quantum IP Address Management (IPAM) interface + using the Melange service, which is access using the Melange + web services API. + """ def __init__(self): + """ Initialize class used to connect to Melange server""" self.m_conn = melange_connection.MelangeConnection() def create_subnet(self, context, label, project_id, quantum_net_id, priority, cidr=None, gateway_v6=None, cidr_v6=None, dns1=None, dns2=None): - tenant_id = project_id or FLAGS.quantum_default_tenant_id - if cidr: - self.m_conn.create_block(quantum_net_id, cidr, + """ Contact Melange and create a subnet for any non-NULL + IPv4 or IPv6 subnets. + + Also create a entry in the Nova networks DB, but only + to store values not represented in Melange or to + temporarily provide compatibility with Nova code that + accesses IPAM data directly via the DB (e.g., nova-api) + """ + tenant_id = project_id or FLAGS.quantum_default_tenant_id + if cidr: + self.m_conn.create_block(quantum_net_id, cidr, project_id=tenant_id, dns1=dns1, dns2=dns2) - if cidr_v6: - self.m_conn.create_block(quantum_net_id, cidr_v6, + if cidr_v6: + self.m_conn.create_block(quantum_net_id, cidr_v6, project_id=tenant_id, dns1=dns1, dns2=dns2) - # create a entry in the network table, even though - # most data is stored in melange. This is used to - # store data not kept by melange (e.g., priority) - # and to 'fake' other parts of nova (e.g., the API) - # until we get get all accesses to be via the - # network manager API. - net = {"uuid": quantum_net_id, + net = {"uuid": quantum_net_id, "project_id": project_id, "priority": priority, "label": label} - network = self.db.network_create_safe(context, net) + network = self.db.network_create_safe(context, net) def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref): + """ Pass call to allocate fixed IP on to Melange""" tenant_id = project_id or FLAGS.quantum_default_tenant_id self.m_conn.allocate_ip(quantum_net_id, vif_ref['uuid'], project_id=tenant_id, mac_address=vif_ref['address']) def get_network_id_by_cidr(self, context, cidr, project_id): + """ Find the Quantum UUID associated with a IPv4 CIDR + address for the specified tenant. + """ tenant_id = project_id or FLAGS.quantum_default_tenant_id all_blocks = self.m_conn.get_blocks(tenant_id) for b in all_blocks['ip_blocks']: @@ -77,6 +88,9 @@ class QuantumMelangeIPAMLib: raise Exception("No network found for cidr %s" % cidr) def delete_subnets_by_net_id(self, context, net_id, project_id): + """ Find Melange block associated with the Quantum UUID, + then tell Melange to delete that block. + """ admin_context = context.elevated() tenant_id = project_id or FLAGS.quantum_default_tenant_id all_blocks = self.m_conn.get_blocks(tenant_id) @@ -88,9 +102,11 @@ class QuantumMelangeIPAMLib: if network is not None: db.network_delete_safe(context, network['id']) - # get all networks with this project_id, as well as all networks - # where the project-id is not set (these are shared networks) def get_project_and_global_net_ids(self, context, project_id): + """ Fetches all networks associated with this project, or + that are "global" (i.e., have no project set). + Returns list sorted by 'priority'. + """ admin_context = context.elevated() id_proj_map = {} if project_id is None: @@ -115,12 +131,16 @@ class QuantumMelangeIPAMLib: return sorted(id_priority_map.items(), key=lambda x: id_priority_map[x[0]]) - # FIXME: (danwent) Melange actually returns the subnet info - # when we query for a particular interface. we may want to - # reworks the ipam_manager python API to let us take advantage of - # this, as right now we have to get all blocks and cycle through - # them. def get_subnets_by_net_id(self, context, project_id, net_id): + """ Returns information about the IPv4 and IPv6 subnets + associated with a Quantum Network UUID. + """ + + # FIXME: (danwent) Melange actually returns the subnet info + # when we query for a particular interface. we may want to + # reworks the ipam_manager python API to let us take advantage of + # this, as right now we have to get all blocks and cycle through + # them. subnet_v4 = None subnet_v6 = None tenant_id = project_id or FLAGS.quantum_default_tenant_id @@ -142,26 +162,41 @@ class QuantumMelangeIPAMLib: return (subnet_v4, subnet_v6) def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): - return self.get_ips_by_interface(context, net_id, vif_id, + """ Returns a list of IPv4 address strings associated with + the specified virtual interface. + """ + return self._get_ips_by_interface(context, net_id, vif_id, project_id, 4) def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): - return self.get_ips_by_interface(context, net_id, vif_id, + """ Returns a list of IPv6 address strings associated with + the specified virtual interface. + """ + return self._get_ips_by_interface(context, net_id, vif_id, project_id, 6) - def get_ips_by_interface(self, context, net_id, vif_id, project_id, + def _get_ips_by_interface(self, context, net_id, vif_id, project_id, ip_version): + """ Helper method to fetch v4 or v6 addresses for a particular + virtual interface. + """ tenant_id = project_id or FLAGS.quantum_default_tenant_id ip_list = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id) return [ip['address'] for ip in ip_list \ if IPNetwork(ip['address']).version == ip_version] def verify_subnet_exists(self, context, project_id, quantum_net_id): + """ Confirms that a subnet exists that is associated with the + specified Quantum Network UUID. + """ tenant_id = project_id or FLAGS.quantum_default_tenant_id v4_subnet, v6_subnet = self.get_subnets_by_net_id(context, tenant_id, quantum_net_id) return v4_subnet is not None def deallocate_ips_by_vif(self, context, project_id, net_id, vif_ref): + """ Deallocate all fixed IPs associated with the specified + virtual interface. + """ tenant_id = project_id or FLAGS.quantum_default_tenant_id self.m_conn.deallocate_ips(net_id, vif_ref['uuid'], tenant_id) diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index 6e7e5d244..ce7d3efcb 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -37,52 +37,69 @@ def get_ipam_lib(net_man): class QuantumNovaIPAMLib: + """ Implements Quantum IP Address Management (IPAM) interface + using the local Nova database. This implementation is inline + with how IPAM is used by other NetworkManagers. + """ def __init__(self, net_manager): + """ Holds a reference to the "parent" network manager, used + to take advantage of various FlatManager methods to avoid + code duplication. + """ self.net_manager = net_manager def create_subnet(self, context, label, tenant_id, quantum_net_id, priority, cidr=None, gateway_v6=None, cidr_v6=None, dns1=None, dns2=None): - admin_context = context.elevated() - subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) - networks = manager.FlatManager.create_networks(self.net_manager, + """ Re-use the basic FlatManager create_networks method to + initialize the networks and fixed_ips tables in Nova DB. + + Also stores a few more fields in the networks table that + are needed by Quantum but not the FlatManager. + """ + admin_context = context.elevated() + subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) + networks = manager.FlatManager.create_networks(self.net_manager, admin_context, label, cidr, False, 1, subnet_size, cidr_v6, gateway_v6, quantum_net_id, None, dns1, dns2) - if len(networks) != 1: - raise Exception("Error creating network entry") + if len(networks) != 1: + raise Exception("Error creating network entry") - # now grab the network and update additional fields - network = networks[0] - net = {"project_id": tenant_id, + network = networks[0] + net = {"project_id": tenant_id, "priority": priority, "uuid": quantum_net_id} - db.network_update(admin_context, network['id'], net) + db.network_update(admin_context, network['id'], net) def get_network_id_by_cidr(self, context, cidr, project_id): - admin_context = context.elevated() - network = db.network_get_by_cidr(admin_context, cidr) - if not network: - raise Exception("No network with fixed_range = %s" \ - % fixed_range) - return network['uuid'] + """ Grabs Quantum network UUID based on IPv4 CIDR. """ + admin_context = context.elevated() + network = db.network_get_by_cidr(admin_context, cidr) + if not network: + raise Exception("No network with fixed_range = %s" % fixed_range) + return network['uuid'] def delete_subnets_by_net_id(self, context, net_id, project_id): - admin_context = context.elevated() - network = db.network_get_by_uuid(admin_context, net_id) - if not network: - raise Exception("No network with net_id = %s" % net_id) - manager.FlatManager.delete_network(self.net_manager, + """ Deletes a network based on Quantum UUID. Uses FlatManager + delete_network to avoid duplication. + """ + admin_context = context.elevated() + network = db.network_get_by_uuid(admin_context, net_id) + if not network: + raise Exception("No network with net_id = %s" % net_id) + manager.FlatManager.delete_network(self.net_manager, admin_context, network['cidr'], require_disassociated=False) def get_project_and_global_net_ids(self, context, project_id): - - # get all networks with this project_id, as well as all networks - # where the project-id is not set (these are shared networks) + """ Fetches all networks associated with this project, or + that are "global" (i.e., have no project set). + Returns list sorted by 'priority'. + """ admin_context = context.elevated() networks = db.project_get_networks(admin_context, project_id, False) networks.extend(db.project_get_networks(admin_context, None, False)) @@ -95,6 +112,8 @@ class QuantumNovaIPAMLib: return sorted(net_list, key=lambda x: id_priority_map[x[0]]) def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): + """ Allocates a single fixed IPv4 address for a virtual interface. + """ admin_context = context.elevated() network = db.network_get_by_uuid(admin_context, quantum_net_id) if network['cidr']: @@ -106,6 +125,9 @@ class QuantumNovaIPAMLib: db.fixed_ip_update(admin_context, address, values) def get_subnets_by_net_id(self, context, tenant_id, net_id): + """ Returns information about the IPv4 and IPv6 subnets + associated with a Quantum Network UUID. + """ n = db.network_get_by_uuid(context.elevated(), net_id) subnet_data_v4 = { 'network_id': n['uuid'], @@ -126,12 +148,18 @@ class QuantumNovaIPAMLib: return (subnet_data_v4, subnet_data_v6) def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): + """ Returns a list of IPv4 address strings associated with + the specified virtual interface, based on the fixed_ips table. + """ vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) fixed_ips = db.fixed_ip_get_by_virtual_interface(context, vif_rec['id']) return [f['address'] for f in fixed_ips] def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): + """ Returns a list containing a single IPv6 address strings + associated with the specified virtual interface. + """ admin_context = context.elevated() network = db.network_get_by_uuid(admin_context, net_id) vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) @@ -143,10 +171,17 @@ class QuantumNovaIPAMLib: return [] def verify_subnet_exists(self, context, tenant_id, quantum_net_id): + """ Confirms that a subnet exists that is associated with the + specified Quantum Network UUID. Raises an exception if no + such subnet exists. + """ admin_context = context.elevated() - network = db.network_get_by_uuid(admin_context, quantum_net_id) + db.network_get_by_uuid(admin_context, quantum_net_id) def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref): + """ Deallocate all fixed IPs associated with the specified + virtual interface. + """ try: admin_context = context.elevated() fixed_ips = db.fixed_ip_get_by_virtual_interface(admin_context, @@ -156,5 +191,5 @@ class QuantumNovaIPAMLib: {'allocated': False, 'virtual_interface_id': None}) except exception.FixedIpNotFoundForInstance: - LOG.error(_('Failed to deallocate fixed IP for vif %s' % \ + LOG.error(_('No fixed IPs to deallocate for vif %s' % \ vif_ref['id'])) diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index a13867af2..bd3592c2c 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -38,31 +38,49 @@ flags.DEFINE_string('quantum_default_tenant_id', class QuantumClientConnection: + """ Abstracts connection to Quantum service into higher level + operations performed by the QuantumManager. + + Separating this out as a class also let's us create a 'fake' + version of this class for unit tests. + """ def __init__(self): + """ Initialize Quantum client class based on flags. """ self.client = quantum_client.Client(FLAGS.quantum_connection_host, FLAGS.quantum_connection_port, format="json", logger=LOG) def create_network(self, tenant_id, network_name): + """ Create network using specified name, return Quantum + network UUID. + """ data = {'network': {'name': network_name}} resdict = self.client.create_network(data, tenant=tenant_id) return resdict["network"]["id"] def delete_network(self, tenant_id, net_id): + """ Deletes Quantum network with specified UUID. """ self.client.delete_network(net_id, tenant=tenant_id) def network_exists(self, tenant_id, net_id): + """ Determine if a Quantum network exists for the + specified tenant. + """ try: self.client.show_network_details(net_id, tenant=tenant_id) except: - # FIXME: client lib should expose more granular exceptions + # FIXME: (danwent) client lib should expose granular exceptions # so we can confirm we're getting a 404 and not some other error return False return True def create_and_attach_port(self, tenant_id, net_id, interface_id): + """ Creates a Quantum port on the specified network, sets + status to ACTIVE to enable traffic, and attaches the + vNIC with the specified interface-id. + """ LOG.debug("Connecting interface %s to net %s for %s" % \ (interface_id, net_id, tenant_id)) port_data = {'port': {'state': 'ACTIVE'}} @@ -74,15 +92,19 @@ class QuantumClientConnection: tenant=tenant_id) def detach_and_delete_port(self, tenant_id, net_id, port_id): + """ Detach and delete the specified Quantum port. """ LOG.debug("Deleting port %s on net %s for %s" % \ (port_id, net_id, tenant_id)) self.client.detach_resource(net_id, port_id, tenant=tenant_id) self.client.delete_port(net_id, port_id, tenant=tenant_id) - # FIXME: (danwent) this will be inefficient until API implements querying def get_port_by_attachment(self, tenant_id, attachment_id): - + """ Given a tenant, search for the Quantum network and port + UUID that has the specified interface-id attachment. + """ + # FIXME: (danwent) this will be inefficient until the Quantum + # API implements querying a port by the interface-id net_list_resdict = self.client.list_networks(tenant=tenant_id) for n in net_list_resdict["networks"]: net_id = n['id'] -- cgit From 605fe4f19af3af830a2a8c82809e9ce5909c602d Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Sun, 28 Aug 2011 20:00:38 -0700 Subject: restore fixed_ip_associate_pool in nova/db/sqlalchemy.py to its original form before this branch. Figured out how to make unit tests pass without requiring that this function changes --- nova/db/sqlalchemy/api.py | 4 +++- nova/tests/test_quantum.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 80ce76e8f..00af62682 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -688,8 +688,10 @@ def fixed_ip_associate(context, address, instance_id, network_id=None): def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): session = get_session() with session.begin(): + network_or_none = or_(models.FixedIp.network_id == network_id, + models.FixedIp.network_id == None) fixed_ip_ref = session.query(models.FixedIp).\ - filter_by(network_id=network_id).\ + filter(network_or_none).\ filter_by(reserved=False).\ filter_by(deleted=False).\ filter_by(instance=None).\ diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index e7a8a01fa..3efdba910 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -17,6 +17,8 @@ from nova import context from nova import db +from nova.db.sqlalchemy import models +from nova.db.sqlalchemy.session import get_session from nova import exception from nova import log as logging from nova.network.quantum import manager as quantum_manager @@ -238,6 +240,15 @@ class QuantumNovaIPAMTestCase(QuantumTestCaseBase, test.TestCase): for n in db.network_get_all(ctx): db.network_delete_safe(ctx, n['id']) + # I've found that other unit tests have a nasty habit of + # of creating fixed IPs and not cleaning up, which can + # confuse these tests, so we clean them all. + session = get_session() + result = session.query(models.FixedIp).all() + with session.begin(): + for fip_ref in result: + session.delete(fip_ref) + # Cannot run this unit tests auotmatically for now, as it requires # melange to be running locally. # -- cgit From 81d30e900d3329f40bfd05682b73e73951c435ca Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Mon, 29 Aug 2011 08:31:56 -0700 Subject: update file name for db migrate script after merge --- .../versions/041_add_network_priority.py | 44 ---------------------- .../versions/043_add_network_priority.py | 44 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 44 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py deleted file mode 100644 index e69380199..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/041_add_network_priority.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2011 Nicira, Inc. -# 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. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging -from nova import utils - - -meta = MetaData() - -# Add priority column to networks table -priority = Column('priority', Integer()) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - # grab tables and (column for dropping later) - networks = Table('networks', meta, autoload=True) - - try: - networks.create_column(priority) - except Exception: - logging.error(_("priority column not added to networks table")) - raise - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - networks.drop_column(priority) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py new file mode 100644 index 000000000..e69380199 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py @@ -0,0 +1,44 @@ +# Copyright 2011 Nicira, Inc. +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging +from nova import utils + + +meta = MetaData() + +# Add priority column to networks table +priority = Column('priority', Integer()) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + + # grab tables and (column for dropping later) + networks = Table('networks', meta, autoload=True) + + try: + networks.create_column(priority) + except Exception: + logging.error(_("priority column not added to networks table")) + raise + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + networks.drop_column(priority) -- cgit From ed1399b5a4a023b606263b5464dfe684e460a126 Mon Sep 17 00:00:00 2001 From: "danwent@gmail.com" <> Date: Mon, 29 Aug 2011 08:33:12 -0700 Subject: update file name for db migrate script after merge (again) --- .../versions/043_add_network_priority.py | 44 ---------------------- .../versions/044_add_network_priority.py | 44 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 44 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py deleted file mode 100644 index e69380199..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/043_add_network_priority.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2011 Nicira, Inc. -# 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. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging -from nova import utils - - -meta = MetaData() - -# Add priority column to networks table -priority = Column('priority', Integer()) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - # grab tables and (column for dropping later) - networks = Table('networks', meta, autoload=True) - - try: - networks.create_column(priority) - except Exception: - logging.error(_("priority column not added to networks table")) - raise - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - networks.drop_column(priority) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py new file mode 100644 index 000000000..e69380199 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py @@ -0,0 +1,44 @@ +# Copyright 2011 Nicira, Inc. +# 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. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging +from nova import utils + + +meta = MetaData() + +# Add priority column to networks table +priority = Column('priority', Integer()) + + +def upgrade(migrate_engine): + meta.bind = migrate_engine + + # grab tables and (column for dropping later) + networks = Table('networks', meta, autoload=True) + + try: + networks.create_column(priority) + except Exception: + logging.error(_("priority column not added to networks table")) + raise + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + networks.drop_column(priority) -- cgit From 7e8e39160d1329f4923334fa822310d266651907 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Mon, 29 Aug 2011 10:43:26 -0700 Subject: access db directly in networkmanagers's delete_network method, so stubbed test call works correctly --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index d1883ff8d..b625e7823 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -791,7 +791,7 @@ class NetworkManager(manager.SchedulerDependentManager): if require_disassociated and network.project_id is not None: raise ValueError(_('Network must be disassociated from project %s' ' before delete' % network.project_id)) - self.db.network_delete_safe(context, network.id) + db.network_delete_safe(context, network.id) @property def _bottom_reserved_ips(self): # pylint: disable=R0201 -- cgit From c96a9ae1b84ee370ff5d8282a8e0531a97c5a396 Mon Sep 17 00:00:00 2001 From: Brad Hall Date: Tue, 23 Aug 2011 21:18:47 -0700 Subject: Address code review feedback from Rick and Matt --- nova/network/manager.py | 2 +- nova/network/quantum/client.py | 63 +++++++++++---------- nova/network/quantum/fake.py | 19 +++---- nova/network/quantum/manager.py | 35 ++++++------ nova/network/quantum/melange_connection.py | 6 +- nova/network/quantum/melange_ipam_lib.py | 90 +++++++++++++++--------------- nova/network/quantum/nova_ipam_lib.py | 39 ++++++------- nova/network/quantum/quantum_connection.py | 10 ++-- nova/tests/test_quantum.py | 18 +++--- 9 files changed, 141 insertions(+), 141 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index b625e7823..426ff2f33 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -547,7 +547,7 @@ class NetworkManager(manager.SchedulerDependentManager): 'network_id': network_id, 'uuid': str(utils.gen_uuid())} # try FLAG times to create a vif record with a unique mac_address - for i in range(FLAGS.create_unique_mac_address_attempts): + for i in xrange(FLAGS.create_unique_mac_address_attempts): try: return self.db.virtual_interface_create(context, vif) except exception.VirtualInterfaceCreateException: diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index 613369c7d..1927015c2 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -22,11 +22,12 @@ import socket import urllib -# this is a simple json-only serializer to use until -# we can just grab the standard serializer -# from the quantum library -class Serializer: - +class JSONSerializer(object): +""" +This is a simple json-only serializer to use until we can just grab +the standard serializer from the quantum library. +TODO(danwent): replace serializer with quantum implementation +""" def serialize(self, data, content_type): try: return json.dumps(data) @@ -40,12 +41,12 @@ class Serializer: class api_call(object): """A Decorator to add support for format and tenant overriding""" - def __init__(self, f): - self.f = f + def __init__(self, func): + self.func = func def __get__(self, instance, owner): def with_params(*args, **kwargs): - # Temporarily set format and tenant for this request + """Temporarily set format and tenant for this request""" (format, tenant) = (instance.format, instance.tenant) if 'format' in kwargs: @@ -53,14 +54,16 @@ class api_call(object): if 'tenant' in kwargs: instance.tenant = kwargs['tenant'] - ret = self.f(instance, *args) - (instance.format, instance.tenant) = (format, tenant) + ret = None + try: + ret = self.func(instance, *args) + finally: + (instance.format, instance.tenant) = (format, tenant) return ret return with_params class Client(object): - """A base client class - derived from Glance.BaseClient""" action_prefix = '/v1.0/tenants/{tenant_id}' @@ -73,8 +76,8 @@ class Client(object): attachment_path = "/networks/%s/ports/%s/attachment" def __init__(self, host="127.0.0.1", port=9696, use_ssl=False, tenant=None, - format="xml", testingStub=None, key_file=None, cert_file=None, - logger=None): + format="xml", testing_stub=None, key_file=None, cert_file=None, + logger=None): """ Creates a new client to some service. @@ -83,7 +86,7 @@ class Client(object): :param use_ssl: True to use SSL, False to use HTTP :param tenant: The tenant ID to make requests with :param format: The format to query the server with - :param testingStub: A class that stubs basic server methods for tests + :param testing_stub: A class that stubs basic server methods for tests :param key_file: The SSL key file to use if use_ssl is true :param cert_file: The SSL cert file to use if use_ssl is true """ @@ -93,7 +96,7 @@ class Client(object): self.tenant = tenant self.format = format self.connection = None - self.testingStub = testingStub + self.testing_stub = testing_stub self.key_file = key_file self.cert_file = cert_file self.logger = logger @@ -102,9 +105,9 @@ class Client(object): """ Returns the proper connection type """ - if self.testingStub: - return self.testingStub - if self.use_ssl: + if self.testing_stub: + return self.testing_stub + elif self.use_ssl: return httplib.HTTPSConnection else: return httplib.HTTPConnection @@ -126,7 +129,7 @@ class Client(object): # Ensure we have a tenant id if not self.tenant: - raise Exception("Tenant ID not set") + raise Exception(_("Tenant ID not set")) # Add format and tenant_id action += ".%s" % self.format @@ -151,8 +154,8 @@ class Client(object): c = connection_type(self.host, self.port) if self.logger: - self.logger.debug("Quantum Client Request:\n" \ - + method + " " + action + "\n") + self.logger.debug(_("Quantum Client Request:\n%s %s\n" % + (method, action))) if body: self.logger.debug(body) @@ -169,14 +172,14 @@ class Client(object): httplib.CREATED, httplib.ACCEPTED, httplib.NO_CONTENT): - if data is not None and len(data): + if not data: return self.deserialize(data, status_code) else: - raise Exception("Server returned error: %s" % res.read()) + raise Exception(_("Server returned error: %s" % res.read())) except (socket.error, IOError), e: - raise Exception("Unable to connect to " - "server. Got error: %s" % e) + raise Exception(_("Unable to connect to " + "server. Got error: %s" % e)) def get_status_code(self, response): """ @@ -189,18 +192,18 @@ class Client(object): return response.status def serialize(self, data): - if data is None: + if not data: return None elif type(data) is dict: - return Serializer().serialize(data, self.content_type()) + return JSONSerializer().serialize(data, self.content_type()) else: - raise Exception("unable to deserialize object of type = '%s'" \ - % type(data)) + raise Exception(_("unable to deserialize object of type = '%s'" % + type(data))) def deserialize(self, data, status_code): if status_code == 202: return data - return Serializer().deserialize(data, self.content_type()) + return JSONSerializer().deserialize(data, self.content_type()) def content_type(self, format=None): if not format: diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py index 00cdd8e08..f668edfed 100644 --- a/nova/network/quantum/fake.py +++ b/nova/network/quantum/fake.py @@ -15,9 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import math -from netaddr import IPNetwork - from nova import exception from nova import ipv6 from nova import log as logging @@ -29,7 +26,7 @@ LOG = logging.getLogger("network.quantum.fake") # this class can be used for unit functional/testing on nova, # as it does not actually make remote calls to the Quantum service -class FakeQuantumClientConnection: +class FakeQuantumClientConnection(object): def __init__(self): self.nets = {} @@ -56,20 +53,20 @@ class FakeQuantumClientConnection: def network_exists(self, tenant_id, net_id): try: return self.nets[net_id]['tenant-id'] == tenant_id - except: + except KeyError: return False def _confirm_not_attached(self, interface_id): for n in self.nets.values(): for p in n['ports'].values(): if p['attachment-id'] == interface_id: - raise Exception("interface '%s' is already attached" %\ - interface_id) + raise Exception(_("interface '%s' is already attached" % + interface_id)) def create_and_attach_port(self, tenant_id, net_id, interface_id): if not self.network_exists(tenant_id, net_id): - raise Exception("network %s does not exist for tenant %s" %\ - (net_id, tenant_id)) + raise Exception(_("network %s does not exist for tenant %s" % + (net_id, tenant_id))) self._confirm_not_attached(interface_id) uuid = str(utils.gen_uuid()) @@ -79,8 +76,8 @@ class FakeQuantumClientConnection: def detach_and_delete_port(self, tenant_id, net_id, port_id): if not self.network_exists(tenant_id, net_id): - raise Exception("network %s does not exist for tenant %s" %\ - (net_id, tenant_id)) + raise Exception(_("network %s does not exist for tenant %s" %\ + (net_id, tenant_id))) del self.nets[net_id]['ports'][port_id] def get_port_by_attachment(self, tenant_id, attachment_id): diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index a002a3d7b..975598324 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -84,16 +84,15 @@ class QuantumManager(manager.FlatManager): In both cases, we initialize a subnet using the IPAM lib. """ if num_networks != 1: - raise Exception("QuantumManager requires that only one" - " network is created per call") - q_tenant_id = kwargs["project_id"] or \ - FLAGS.quantum_default_tenant_id + raise Exception(_("QuantumManager requires that only one" + " network is created per call")) + q_tenant_id = kwargs.get("project_id", FLAGS.quantum_default_tenant_id) quantum_net_id = uuid if quantum_net_id: if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): - raise Exception("Unable to find existing quantum " \ - " network for tenant '%s' with net-id '%s'" % \ - (q_tenant_id, quantum_net_id)) + raise Exception(_("Unable to find existing quantum " \ + " network for tenant '%s' with net-id '%s'" % \ + (q_tenant_id, quantum_net_id))) else: # otherwise, create network from default quantum pool quantum_net_id = self.q_conn.create_network(q_tenant_id, label) @@ -156,18 +155,18 @@ class QuantumManager(manager.FlatManager): # Create a port via quantum and attach the vif for (quantum_net_id, project_id) in net_proj_pairs: - # FIXME: (danwent). We'd like to have the manager be completely - # decoupled from the nova networks table. - # However, other parts of nova sometimes go behind - # our back and access network data directly from the DB. So + # FIXME(danwent): We'd like to have the manager be + # completely decoupled from the nova networks table. + # However, other parts of nova sometimes go behind our + # back and access network data directly from the DB. So # for now, the quantum manager knows that there is a nova - # networks DB table and accesses it here. - # updating the virtual_interfaces table to use UUIDs would - # be one solution, but this would require significant work + # networks DB table and accesses it here. updating the + # virtual_interfaces table to use UUIDs would be one + # solution, but this would require significant work # elsewhere. admin_context = context.elevated() network_ref = db.network_get_by_uuid(admin_context, - quantum_net_id) + quantum_net_id) vif_rec = manager.FlatManager.add_virtual_interface(self, context, instance_id, network_ref['id']) @@ -177,7 +176,7 @@ class QuantumManager(manager.FlatManager): self.q_conn.create_and_attach_port(q_tenant_id, quantum_net_id, vif_rec['uuid']) self.ipam.allocate_fixed_ip(context, project_id, quantum_net_id, - vif_rec) + vif_rec) return self.get_instance_nw_info(context, instance_id, instance_type_id, host) @@ -214,8 +213,8 @@ class QuantumManager(manager.FlatManager): net_id, port_id = self.q_conn.get_port_by_attachment( q_tenant_id, vif['uuid']) if not net_id: - raise Exception(_("No network for for virtual interface %s") %\ - vif['uuid']) + raise Exception(_("No network for for virtual interface %s") % + vif['uuid']) (v4_subnet, v6_subnet) = self.ipam.get_subnets_by_net_id(context, ipam_tenant_id, net_id) v4_ips = self.ipam.get_v4_ips_by_interface(context, diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index 2d884fa60..1ee0c29a2 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -73,10 +73,10 @@ class MelangeConnection(object): response_str = response.read() if response.status < 400: return response_str - raise Exception("Server returned error: %s", response_str) + raise Exception(_("Server returned error: %s", response_str)) except (socket.error, IOError), e: - raise Exception("Unable to connect to " - "server. Got error: %s" % e) + raise Exception(_("Unable to connect to " + "server. Got error: %s" % e)) def allocate_ip(self, network_id, vif_id, project_id=None, mac_address=None): diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index 7b7baf281..24a7c5404 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -31,7 +31,7 @@ def get_ipam_lib(net_man): return QuantumMelangeIPAMLib() -class QuantumMelangeIPAMLib: +class QuantumMelangeIPAMLib(object): """ Implements Quantum IP Address Management (IPAM) interface using the Melange service, which is access using the Melange web services API. @@ -42,9 +42,9 @@ class QuantumMelangeIPAMLib: self.m_conn = melange_connection.MelangeConnection() def create_subnet(self, context, label, project_id, - quantum_net_id, priority, cidr=None, - gateway_v6=None, cidr_v6=None, - dns1=None, dns2=None): + quantum_net_id, priority, cidr=None, + gateway_v6=None, cidr_v6=None, + dns1=None, dns2=None): """ Contact Melange and create a subnet for any non-NULL IPv4 or IPv6 subnets. @@ -56,25 +56,25 @@ class QuantumMelangeIPAMLib: tenant_id = project_id or FLAGS.quantum_default_tenant_id if cidr: self.m_conn.create_block(quantum_net_id, cidr, - project_id=tenant_id, - dns1=dns1, dns2=dns2) + project_id=tenant_id, + dns1=dns1, dns2=dns2) if cidr_v6: self.m_conn.create_block(quantum_net_id, cidr_v6, project_id=tenant_id, dns1=dns1, dns2=dns2) net = {"uuid": quantum_net_id, - "project_id": project_id, - "priority": priority, - "label": label} + "project_id": project_id, + "priority": priority, + "label": label} network = self.db.network_create_safe(context, net) def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref): """ Pass call to allocate fixed IP on to Melange""" tenant_id = project_id or FLAGS.quantum_default_tenant_id self.m_conn.allocate_ip(quantum_net_id, - vif_ref['uuid'], project_id=tenant_id, - mac_address=vif_ref['address']) + vif_ref['uuid'], project_id=tenant_id, + mac_address=vif_ref['address']) def get_network_id_by_cidr(self, context, cidr, project_id): """ Find the Quantum UUID associated with a IPv4 CIDR @@ -85,7 +85,7 @@ class QuantumMelangeIPAMLib: for b in all_blocks['ip_blocks']: if b['cidr'] == cidr: return b['network_id'] - raise Exception("No network found for cidr %s" % cidr) + raise Exception(_("No network found for cidr %s" % cidr)) def delete_subnets_by_net_id(self, context, net_id, project_id): """ Find Melange block associated with the Quantum UUID, @@ -107,38 +107,38 @@ class QuantumMelangeIPAMLib: that are "global" (i.e., have no project set). Returns list sorted by 'priority'. """ - admin_context = context.elevated() - id_proj_map = {} if project_id is None: - raise Exception("get_project_and_global_net_ids must be called" \ - " with a non-null project_id") - tenant_id = project_id - all_tenant_blocks = self.m_conn.get_blocks(tenant_id) - for b in all_tenant_blocks['ip_blocks']: - id_proj_map[b['network_id']] = tenant_id - tenant_id = FLAGS.quantum_default_tenant_id - all_provider_blocks = self.m_conn.get_blocks(tenant_id) - for b in all_provider_blocks['ip_blocks']: - id_proj_map[b['network_id']] = tenant_id - - id_priority_map = {} - for net_id, project_id in id_project_map.item(): - network = db.network_get_by_uuid(admin_context, net_id) - if network is None: - del id_proj_map[net_id] - else: - id_priority_map[net_id] = network['priority'] - return sorted(id_priority_map.items(), - key=lambda x: id_priority_map[x[0]]) + raise Exception(_("get_project_and_global_net_ids must be called" + " with a non-null project_id")) + + admin_context = context.elevated() + + # Decorate with priority + priority_nets = [] + for tenant_id in (project_id, FLAGS.quantum_default_tenant_id): + blocks = self.m_conn.get_blocks(tenant_id) + for ip_block in blocks['ip_blocks']: + network_id = ip_block['network_id'] + network = db.network_get_by_uuid(admin_context, network_id) + if network: + priority = network['priority'] + priority_nets.append((priority, network_id, tenant_id)) + + # Sort by priority + priority_nets.sort() + + # Undecorate + return [(network_id, tenant_id) + for priority, network_id, tenant_id in priority_nets] def get_subnets_by_net_id(self, context, project_id, net_id): """ Returns information about the IPv4 and IPv6 subnets associated with a Quantum Network UUID. """ - # FIXME: (danwent) Melange actually returns the subnet info - # when we query for a particular interface. we may want to - # reworks the ipam_manager python API to let us take advantage of + # FIXME(danwent): Melange actually returns the subnet info + # when we query for a particular interface. We may want to + # rework the ipam_manager python API to let us take advantage of # this, as right now we have to get all blocks and cycle through # them. subnet_v4 = None @@ -148,12 +148,12 @@ class QuantumMelangeIPAMLib: for b in all_blocks['ip_blocks']: if b['network_id'] == net_id: subnet = {'network_id': b['network_id'], - 'cidr': b['cidr'], - 'gateway': b['gateway'], - 'broadcast': b['broadcast'], - 'netmask': b['netmask'], - 'dns1': b['dns1'], - 'dns2': b['dns2']} + 'cidr': b['cidr'], + 'gateway': b['gateway'], + 'broadcast': b['broadcast'], + 'netmask': b['netmask'], + 'dns1': b['dns1'], + 'dns2': b['dns2']} if IPNetwork(b['cidr']).version == 6: subnet_v6 = subnet @@ -182,8 +182,8 @@ class QuantumMelangeIPAMLib: """ tenant_id = project_id or FLAGS.quantum_default_tenant_id ip_list = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id) - return [ip['address'] for ip in ip_list \ - if IPNetwork(ip['address']).version == ip_version] + return [ip['address'] for ip in ip_list + if IPNetwork(ip['address']).version == ip_version] def verify_subnet_exists(self, context, project_id, quantum_net_id): """ Confirms that a subnet exists that is associated with the diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index ce7d3efcb..71e723cb9 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -36,7 +36,7 @@ def get_ipam_lib(net_man): return QuantumNovaIPAMLib(net_man) -class QuantumNovaIPAMLib: +class QuantumNovaIPAMLib(object): """ Implements Quantum IP Address Management (IPAM) interface using the local Nova database. This implementation is inline with how IPAM is used by other NetworkManagers. @@ -50,9 +50,9 @@ class QuantumNovaIPAMLib: self.net_manager = net_manager def create_subnet(self, context, label, tenant_id, - quantum_net_id, priority, cidr=None, - gateway_v6=None, cidr_v6=None, - dns1=None, dns2=None): + quantum_net_id, priority, cidr=None, + gateway_v6=None, cidr_v6=None, + dns1=None, dns2=None): """ Re-use the basic FlatManager create_networks method to initialize the networks and fixed_ips tables in Nova DB. @@ -60,6 +60,7 @@ class QuantumNovaIPAMLib: are needed by Quantum but not the FlatManager. """ admin_context = context.elevated() + # FIXME(danwent): Use the netaddr library here subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) networks = manager.FlatManager.create_networks(self.net_manager, admin_context, label, cidr, @@ -67,12 +68,12 @@ class QuantumNovaIPAMLib: gateway_v6, quantum_net_id, None, dns1, dns2) if len(networks) != 1: - raise Exception("Error creating network entry") + raise Exception(_("Error creating network entry")) network = networks[0] net = {"project_id": tenant_id, - "priority": priority, - "uuid": quantum_net_id} + "priority": priority, + "uuid": quantum_net_id} db.network_update(admin_context, network['id'], net) def get_network_id_by_cidr(self, context, cidr, project_id): @@ -80,7 +81,7 @@ class QuantumNovaIPAMLib: admin_context = context.elevated() network = db.network_get_by_cidr(admin_context, cidr) if not network: - raise Exception("No network with fixed_range = %s" % fixed_range) + raise Exception(_("No network with fixed_range = %s" % fixed_range)) return network['uuid'] def delete_subnets_by_net_id(self, context, net_id, project_id): @@ -90,10 +91,10 @@ class QuantumNovaIPAMLib: admin_context = context.elevated() network = db.network_get_by_uuid(admin_context, net_id) if not network: - raise Exception("No network with net_id = %s" % net_id) + raise Exception(_("No network with net_id = %s" % net_id)) manager.FlatManager.delete_network(self.net_manager, - admin_context, network['cidr'], - require_disassociated=False) + admin_context, network['cidr'], + require_disassociated=False) def get_project_and_global_net_ids(self, context, project_id): """ Fetches all networks associated with this project, or @@ -118,8 +119,8 @@ class QuantumNovaIPAMLib: network = db.network_get_by_uuid(admin_context, quantum_net_id) if network['cidr']: address = db.fixed_ip_associate_pool(admin_context, - network['id'], - vif_rec['instance_id']) + network['id'], + vif_rec['instance_id']) values = {'allocated': True, 'virtual_interface_id': vif_rec['id']} db.fixed_ip_update(admin_context, address, values) @@ -186,10 +187,10 @@ class QuantumNovaIPAMLib: admin_context = context.elevated() fixed_ips = db.fixed_ip_get_by_virtual_interface(admin_context, vif_ref['id']) - for f in fixed_ips: - db.fixed_ip_update(admin_context, f['address'], - {'allocated': False, - 'virtual_interface_id': None}) + for fixed_ip in fixed_ips: + db.fixed_ip_update(admin_context, fixed_ip['address'], + {'allocated': False, + 'virtual_interface_id': None}) except exception.FixedIpNotFoundForInstance: - LOG.error(_('No fixed IPs to deallocate for vif %s' % \ - vif_ref['id'])) + LOG.error(_('No fixed IPs to deallocate for vif %s' % + vif_ref['id'])) diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index bd3592c2c..e2218c68d 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -37,7 +37,7 @@ flags.DEFINE_string('quantum_default_tenant_id', 'Default tenant id when creating quantum networks') -class QuantumClientConnection: +class QuantumClientConnection(object): """ Abstracts connection to Quantum service into higher level operations performed by the QuantumManager. @@ -71,7 +71,7 @@ class QuantumClientConnection: try: self.client.show_network_details(net_id, tenant=tenant_id) except: - # FIXME: (danwent) client lib should expose granular exceptions + # FIXME(danwent): client lib should expose granular exceptions # so we can confirm we're getting a 404 and not some other error return False return True @@ -81,8 +81,8 @@ class QuantumClientConnection: status to ACTIVE to enable traffic, and attaches the vNIC with the specified interface-id. """ - LOG.debug("Connecting interface %s to net %s for %s" % \ - (interface_id, net_id, tenant_id)) + LOG.debug(_("Connecting interface %s to net %s for %s" % + (interface_id, net_id, tenant_id))) port_data = {'port': {'state': 'ACTIVE'}} resdict = self.client.create_port(net_id, port_data, tenant=tenant_id) port_id = resdict["port"]["id"] @@ -103,7 +103,7 @@ class QuantumClientConnection: """ Given a tenant, search for the Quantum network and port UUID that has the specified interface-id attachment. """ - # FIXME: (danwent) this will be inefficient until the Quantum + # FIXME(danwent): this will be inefficient until the Quantum # API implements querying a port by the interface-id net_list_resdict = self.client.list_networks(tenant=tenant_id) for n in net_list_resdict["networks"]: diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 3efdba910..2cc83adf1 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -155,9 +155,9 @@ class QuantumTestCaseBase(object): self.assertTrue(nw_info[1][0]['cidr_v6'].startswith("2001:1db8:")) # v6 address - self.assertTrue(\ + self.assertTrue( nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dba:")) - self.assertTrue(\ + self.assertTrue( nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1db8:")) self.net_man.deallocate_for_instance(ctx, @@ -233,24 +233,24 @@ class QuantumNovaIPAMTestCase(QuantumTestCaseBase, test.TestCase): self.net_man = quantum_manager.QuantumManager( \ ipam_lib="nova.network.quantum.nova_ipam_lib") - # tests seem to create some networks by default, which - # don't want. So we delete them. + # Tests seem to create some networks by default, which + # we don't want. So we delete them. ctx = context.RequestContext('user1', 'fake_project1').elevated() for n in db.network_get_all(ctx): db.network_delete_safe(ctx, n['id']) - # I've found that other unit tests have a nasty habit of - # of creating fixed IPs and not cleaning up, which can - # confuse these tests, so we clean them all. + # NOTE(danwent): I've found that other unit tests have a nasty + # habit of of creating fixed IPs and not cleaning up, which + # can confuse these tests, so we clean them all. session = get_session() result = session.query(models.FixedIp).all() with session.begin(): for fip_ref in result: session.delete(fip_ref) -# Cannot run this unit tests auotmatically for now, as it requires -# melange to be running locally. +# FIXME(danwent): Cannot run this unit tests automatically for now, as +# it requires melange to be running locally. # #class QuantumMelangeIPAMTestCase(QuantumTestCaseBase, test.TestCase): # -- cgit From 3058e5eeb5e1068b7b5f721b985c735bc5486c92 Mon Sep 17 00:00:00 2001 From: Brad Hall Date: Wed, 24 Aug 2011 02:11:56 -0700 Subject: Couple of fixes to the review feedback changes --- nova/network/quantum/client.py | 12 ++++++------ nova/network/quantum/manager.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index 1927015c2..b457190c1 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -23,11 +23,11 @@ import urllib class JSONSerializer(object): -""" -This is a simple json-only serializer to use until we can just grab -the standard serializer from the quantum library. -TODO(danwent): replace serializer with quantum implementation -""" + """ + This is a simple json-only serializer to use until we can just grab + the standard serializer from the quantum library. + TODO(danwent): replace serializer with quantum implementation + """ def serialize(self, data, content_type): try: return json.dumps(data) @@ -172,7 +172,7 @@ class Client(object): httplib.CREATED, httplib.ACCEPTED, httplib.NO_CONTENT): - if not data: + if data is not None and len(data): return self.deserialize(data, status_code) else: raise Exception(_("Server returned error: %s" % res.read())) diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 975598324..d29d1e06b 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -86,7 +86,7 @@ class QuantumManager(manager.FlatManager): if num_networks != 1: raise Exception(_("QuantumManager requires that only one" " network is created per call")) - q_tenant_id = kwargs.get("project_id", FLAGS.quantum_default_tenant_id) + q_tenant_id = kwargs["project_id"] or FLAGS.quantum_default_tenant_id quantum_net_id = uuid if quantum_net_id: if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): @@ -179,7 +179,7 @@ class QuantumManager(manager.FlatManager): vif_rec) return self.get_instance_nw_info(context, instance_id, - instance_type_id, host) + instance_type_id, host) def get_instance_nw_info(self, context, instance_id, instance_type_id, host): -- cgit From 13544ec52d4f84032b7925b1dab00fbdc5ca0c79 Mon Sep 17 00:00:00 2001 From: Brad Hall Date: Wed, 24 Aug 2011 03:07:11 -0700 Subject: pep8 fixes --- nova/network/quantum/client.py | 4 ++-- nova/network/quantum/nova_ipam_lib.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index b457190c1..82f23b6b5 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -76,8 +76,8 @@ class Client(object): attachment_path = "/networks/%s/ports/%s/attachment" def __init__(self, host="127.0.0.1", port=9696, use_ssl=False, tenant=None, - format="xml", testing_stub=None, key_file=None, cert_file=None, - logger=None): + format="xml", testing_stub=None, key_file=None, + cert_file=None, logger=None): """ Creates a new client to some service. diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index 71e723cb9..4f62887d1 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -81,7 +81,8 @@ class QuantumNovaIPAMLib(object): admin_context = context.elevated() network = db.network_get_by_cidr(admin_context, cidr) if not network: - raise Exception(_("No network with fixed_range = %s" % fixed_range)) + raise Exception(_("No network with fixed_range = %s" % + fixed_range)) return network['uuid'] def delete_subnets_by_net_id(self, context, net_id, project_id): -- cgit From 551b4b1b16c894551e5337663374a40aa46663d7 Mon Sep 17 00:00:00 2001 From: Brad Hall Date: Wed, 24 Aug 2011 03:14:39 -0700 Subject: Catch exception for instances that aren't there If an instance failed to spawn it will be in a shutdown state but the instance won't actually be there when we attempt to deallocate for the instance. For now we catch the exception and just log a message. Also made some minor formatting changes. --- nova/network/quantum/manager.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index d29d1e06b..c03622218 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -287,17 +287,21 @@ class QuantumManager(manager.FlatManager): (net_id, port_id) = self.q_conn.get_port_by_attachment( q_tenant_id, interface_id) if not net_id: - LOG.error("Unable to find port with attachment: %s" % \ - (interface_id)) + LOG.error("Unable to find port with attachment: %s" % + (interface_id)) continue self.q_conn.detach_and_delete_port(q_tenant_id, - net_id, port_id) + net_id, port_id) self.ipam.deallocate_ips_by_vif(context, ipam_tenant_id, - net_id, vif_ref) + net_id, vif_ref) - db.virtual_interface_delete_by_instance(admin_context, - instance_id) + try: + db.virtual_interface_delete_by_instance(admin_context, + instance_id) + except exception.InstanceNotFound: + LOG.error(_("Attempted to deallocate non-existent instance: %s" % + (instance_id))) self._do_trigger_security_group_members_refresh_for_instance( instance_id) -- cgit From 3d17d22926e2f589648fdac26302a58e8c4e9069 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Wed, 24 Aug 2011 11:36:41 -0700 Subject: fix more tests --- nova/tests/api/openstack/contrib/test_createserverext.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova') diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index e5eed14fe..01c7587e3 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -89,6 +89,9 @@ class CreateserverextTest(test.TestCase): self.networks = None return [{'id': '1234', 'display_name': 'fakeinstance', 'uuid': FAKE_UUID, + 'user_id': 'fake', + 'project_id': 'fake', + 'display_description': 'fakedescription', 'created_at': "", 'updated_at': ""}] -- cgit From 0571c86d18c242f46e44e380b257cfc40598d31b Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Wed, 24 Aug 2011 12:17:58 -0700 Subject: use dict.get for user_id, project_id, and display_description in servers view as suggested by ed leaf, so that not all tests require these fields --- nova/api/openstack/views/servers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index fd7c040d4..6fd09aae6 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -65,9 +65,9 @@ class ViewBuilder(object): inst_dict = { 'id': inst['id'], 'name': inst['display_name'], - 'user_id': inst['user_id'], - 'tenant_id': inst['project_id'], - 'description': inst['display_description'], + 'user_id': inst.get('user_id', ''), + 'tenant_id': inst.get('project_id', ''), + 'description': inst.get('display_description', ''), 'status': common.status_from_power_state(inst.get('state'))} ctxt = nova.context.get_admin_context() -- cgit From 358c346a7d01336f23b21a18f33c3213b3672f0a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 25 Aug 2011 15:53:13 -0500 Subject: updated libvirt tests to use fake_network_info --- nova/network/manager.py | 2 +- nova/tests/fake_network_info.py | 30 ++++++++++++++++++++----- nova/tests/test_libvirt.py | 49 +++++++++++++++++++++++------------------ 3 files changed, 53 insertions(+), 28 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index dcd5aad9a..9a4b09ad8 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -483,7 +483,6 @@ class NetworkManager(manager.SchedulerDependentManager): 'bridge': network['bridge'], 'id': network['id'], 'cidr': network['cidr'], - 'cidr_v6': network['cidr_v6'], 'injected': network['injected'], 'vlan': network['vlan'], 'bridge_interface': network['bridge_interface'], @@ -509,6 +508,7 @@ class NetworkManager(manager.SchedulerDependentManager): if network['cidr_v6']: info['ip6s'] = [ip6_dict()] + network_dict['cidr_v6'] = network['cidr_v6'], # TODO(tr3buchet): handle ip6 routes here as well if network['gateway_v6']: info['gateway6'] = network['gateway_v6'] diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 072585195..0bf117a90 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -16,11 +16,13 @@ # under the License. from nova import db +from nova import flags from nova import test from nova.network import manager as network_manager HOST = "testhost" +FLAGS = flags.FLAGS class FakeModel(dict): @@ -32,15 +34,14 @@ class FakeModel(dict): return self[name] -def fake_network(n): - return {'id': n, +def fake_network(n, ipv6=None): + if ipv6 = None: + ipv6 = Flags.use_ipv6 + rval = {'id': n, 'label': 'test%d' % n, 'injected': False, 'multi_host': False, 'cidr': '192.168.%d.0/24' % n, - 'cidr_v6': '2001:db8:0:%x::/64' % n, - 'gateway_v6': '2001:db8:0:%x::1' % n, - 'netmask_v6': '64', 'netmask': '255.255.255.0', 'bridge': 'fake_br%d' % n, 'bridge_interface': 'fake_eth%d' % n, @@ -52,6 +53,10 @@ def fake_network(n): 'host': None, 'project_id': 'fake_project', 'vpn_public_address': '192.168.%d.2' % n} + if ipv6: + rval['cidr_v6'] = '2001:db8:0:%x::/64' % n + rval['gateway_v6'] = '2001:db8:0:%x::1' % n + rval['netmask_v6'] = '64' def fixed_ips(num_networks, num_ips, num_floating_ips=0): @@ -91,7 +96,22 @@ def vifs(n): 'instance_id': 0} +def ipv4_like(ip, s): + ip = ip.split('.') + s = s.split('.') + + for i, octet in enumerate(s): + if octet == '*': + continue + if octet != ip[i]: + return False + return True + + def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): + # stubs is the self.stubs from the test + # ips_per_vif is the number of ips each vif will have + # num_floating_ips is number of float ips for each fixed ip network = network_manager.FlatManager(host=HOST) network.db = db diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 6a213b4f0..1b5a2d55d 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -36,6 +36,7 @@ from nova.api.ec2 import cloud from nova.compute import power_state from nova.virt.libvirt import connection from nova.virt.libvirt import firewall +from nova.tests import fake_network_info libvirt = None FLAGS = flags.FLAGS @@ -45,7 +46,8 @@ def _concurrency(wait, done, target): wait.wait() done.send() - +_fake_network_info = fake_network_info.fake_get_instance_nw_info +_ipv4_like = fake_network_info.ipv4_like def _create_network_info(count=1, ipv6=None): if ipv6 is None: ipv6 = FLAGS.use_ipv6 @@ -276,12 +278,12 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(self.context, self.test_instance) result = conn._prepare_xml_info(instance_ref, - _create_network_info(), + _fake_network_info(self.stubs, 1), False) self.assertTrue(len(result['nics']) == 1) result = conn._prepare_xml_info(instance_ref, - _create_network_info(2), + _fake_network_info(self.stubs, 2), False) self.assertTrue(len(result['nics']) == 2) @@ -406,7 +408,7 @@ class LibvirtConnTestCase(test.TestCase): def test_multi_nic(self): instance_data = dict(self.test_instance) - network_info = _create_network_info(2) + network_info = _fake_network_info(self.stubs, 2) conn = connection.LibvirtConnection(True) instance_ref = db.instance_create(self.context, instance_data) xml = conn.to_xml(instance_ref, network_info, False) @@ -416,9 +418,9 @@ class LibvirtConnTestCase(test.TestCase): parameters = interfaces[0].findall('./filterref/parameter') self.assertEquals(interfaces[0].get('type'), 'bridge') self.assertEquals(parameters[0].get('name'), 'IP') - self.assertEquals(parameters[0].get('value'), '10.11.12.13') + self.assertTrue(_ipv4_like(parameters[0].get('value'), '192.168') self.assertEquals(parameters[1].get('name'), 'DHCPSERVER') - self.assertEquals(parameters[1].get('value'), '10.0.0.1') + self.assertTrue(_ipv4_like(parameters[1].get('value'), '192.168.*.1') def _check_xml_and_container(self, instance): user_context = context.RequestContext(self.user_id, @@ -432,7 +434,7 @@ class LibvirtConnTestCase(test.TestCase): uri = conn.get_uri() self.assertEquals(uri, 'lxc:///') - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) xml = conn.to_xml(instance_ref, network_info) tree = xml_to_tree(xml) @@ -503,9 +505,11 @@ class LibvirtConnTestCase(test.TestCase): common_checks = [ (lambda t: t.find('.').tag, 'domain'), (lambda t: t.find(parameter).get('name'), 'IP'), - (lambda t: t.find(parameter).get('value'), '10.11.12.13'), + (lambda t: _ipv4_like(t.find(parameter).get('value'), '192.168'), + True), (lambda t: t.findall(parameter)[1].get('name'), 'DHCPSERVER'), - (lambda t: t.findall(parameter)[1].get('value'), '10.0.0.1'), + (lambda t: _ipv4_like(t.findall(parameter)[1].get('value'), + '192.168.*.1'), True), (lambda t: t.find('./devices/serial/source').get( 'path').split('/')[1], 'console.log'), (lambda t: t.find('./memory').text, '2097152')] @@ -530,7 +534,7 @@ class LibvirtConnTestCase(test.TestCase): uri = conn.get_uri() self.assertEquals(uri, expected_uri) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) xml = conn.to_xml(instance_ref, network_info, rescue) tree = xml_to_tree(xml) for i, (check, expected_result) in enumerate(checks): @@ -645,7 +649,7 @@ class LibvirtConnTestCase(test.TestCase): self.create_fake_libvirt_mock() instance_ref = db.instance_create(self.context, self.test_instance) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) # Start test self.mox.ReplayAll() @@ -828,7 +832,7 @@ class LibvirtConnTestCase(test.TestCase): conn.firewall_driver.setattr('setup_basic_filtering', fake_none) conn.firewall_driver.setattr('prepare_instance_filter', fake_none) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) try: conn.spawn(self.context, instance, network_info) @@ -1062,7 +1066,7 @@ class IptablesFirewallTestCase(test.TestCase): from nova.network import linux_net linux_net.iptables_manager.execute = fake_iptables_execute - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) self.fw.prepare_instance_filter(instance_ref, network_info) self.fw.apply_instance_filter(instance_ref, network_info) @@ -1076,7 +1080,8 @@ class IptablesFirewallTestCase(test.TestCase): instance_chain = None for rule in self.out_rules: # This is pretty crude, but it'll do for now - if '-d 10.11.12.13 -j' in rule: + # last two octets change + if re.search('-d 192.168.[0-9]{1,3}.[0-9]{1,3} -j', rule): instance_chain = rule.split(' ')[-1] break self.assertTrue(instance_chain, "The instance chain wasn't added") @@ -1112,14 +1117,14 @@ class IptablesFirewallTestCase(test.TestCase): def test_filters_for_instance_with_ip_v6(self): self.flags(use_ipv6=True) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) rulesv4, rulesv6 = self.fw._filters_for_instance("fake", network_info) self.assertEquals(len(rulesv4), 2) self.assertEquals(len(rulesv6), 3) def test_filters_for_instance_without_ip_v6(self): self.flags(use_ipv6=False) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) rulesv4, rulesv6 = self.fw._filters_for_instance("fake", network_info) self.assertEquals(len(rulesv4), 2) self.assertEquals(len(rulesv6), 0) @@ -1129,7 +1134,7 @@ class IptablesFirewallTestCase(test.TestCase): ipv6_rules_per_network = 3 networks_count = 5 instance_ref = self._create_instance_ref() - network_info = _create_network_info(networks_count) + network_info = _fake_network_info(self.stubs, networks_count) ipv4_len = len(self.fw.iptables.ipv4['filter'].rules) ipv6_len = len(self.fw.iptables.ipv6['filter'].rules) inst_ipv4, inst_ipv6 = self.fw.instance_rules(instance_ref, @@ -1169,7 +1174,7 @@ class IptablesFirewallTestCase(test.TestCase): instance_ref = self._create_instance_ref() _setup_networking(instance_ref['id'], self.test_ip) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) self.fw.setup_basic_filtering(instance_ref, network_info) self.fw.prepare_instance_filter(instance_ref, network_info) self.fw.apply_instance_filter(instance_ref, network_info) @@ -1190,7 +1195,7 @@ class IptablesFirewallTestCase(test.TestCase): # create a firewall via setup_basic_filtering like libvirt_conn.spawn # should have a chain with 0 rules - network_info = _create_network_info(1) + network_info = _fake_network_info(self.stubs, 1) self.fw.setup_basic_filtering(instance_ref, network_info) self.assertTrue('provider' in self.fw.iptables.ipv4['filter'].chains) rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules @@ -1390,7 +1395,7 @@ class NWFilterTestCase(test.TestCase): self.security_group.id) instance = db.instance_get(self.context, inst_id) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) self.fw.setup_basic_filtering(instance, network_info) self.fw.prepare_instance_filter(instance, network_info) self.fw.apply_instance_filter(instance, network_info) @@ -1400,7 +1405,7 @@ class NWFilterTestCase(test.TestCase): def test_create_network_filters(self): instance_ref = self._create_instance() - network_info = _create_network_info(3) + network_info = _fake_network_info(self.stubs, 3) result = self.fw._create_network_filters(instance_ref, network_info, "fake") @@ -1424,7 +1429,7 @@ class NWFilterTestCase(test.TestCase): instance = db.instance_get(self.context, inst_id) _setup_networking(instance_ref['id'], self.test_ip) - network_info = _create_network_info() + network_info = _fake_network_info(self.stubs, 1) self.fw.setup_basic_filtering(instance, network_info) self.fw.prepare_instance_filter(instance, network_info) self.fw.apply_instance_filter(instance, network_info) -- cgit From 416b270374d64f976c91a3efb74dcb467bfbcc8c Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 25 Aug 2011 16:02:52 -0500 Subject: fixed a couple of syntax errors --- nova/tests/fake_network_info.py | 2 +- nova/tests/test_libvirt.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 0bf117a90..113647d7f 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -35,7 +35,7 @@ class FakeModel(dict): def fake_network(n, ipv6=None): - if ipv6 = None: + if ipv6 == None: ipv6 = Flags.use_ipv6 rval = {'id': n, 'label': 'test%d' % n, diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 1b5a2d55d..06ad7d13a 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -418,9 +418,9 @@ class LibvirtConnTestCase(test.TestCase): parameters = interfaces[0].findall('./filterref/parameter') self.assertEquals(interfaces[0].get('type'), 'bridge') self.assertEquals(parameters[0].get('name'), 'IP') - self.assertTrue(_ipv4_like(parameters[0].get('value'), '192.168') + self.assertTrue(_ipv4_like(parameters[0].get('value'), '192.168')) self.assertEquals(parameters[1].get('name'), 'DHCPSERVER') - self.assertTrue(_ipv4_like(parameters[1].get('value'), '192.168.*.1') + self.assertTrue(_ipv4_like(parameters[1].get('value'), '192.168.*.1')) def _check_xml_and_container(self, instance): user_context = context.RequestContext(self.user_id, -- cgit From 5daa66cb91f95ff341ce5ec762fb1060f22e587f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 25 Aug 2011 16:10:25 -0500 Subject: typo --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 113647d7f..295ac8a97 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -36,7 +36,7 @@ class FakeModel(dict): def fake_network(n, ipv6=None): if ipv6 == None: - ipv6 = Flags.use_ipv6 + ipv6 = FLAGS.use_ipv6 rval = {'id': n, 'label': 'test%d' % n, 'injected': False, -- cgit From 590e7445d937485280e60743318000d0bfc80236 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 25 Aug 2011 16:49:09 -0500 Subject: forget a return --- nova/tests/fake_network_info.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 295ac8a97..b05cf7c95 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -58,6 +58,8 @@ def fake_network(n, ipv6=None): rval['gateway_v6'] = '2001:db8:0:%x::1' % n rval['netmask_v6'] = '64' + return rval + def fixed_ips(num_networks, num_ips, num_floating_ips=0): for network in xrange(num_networks): -- cgit From 9df1fe10f39d4b1b4753a5c5366e68dd1efa9e77 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 25 Aug 2011 17:04:26 -0500 Subject: altered fake network model --- nova/tests/fake_network_info.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index b05cf7c95..47f9abd05 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -42,10 +42,13 @@ def fake_network(n, ipv6=None): 'injected': False, 'multi_host': False, 'cidr': '192.168.%d.0/24' % n, + 'cidr_v6': None, 'netmask': '255.255.255.0', + 'netmask_v6': None, 'bridge': 'fake_br%d' % n, 'bridge_interface': 'fake_eth%d' % n, 'gateway': '192.168.%d.1' % n, + 'gateway_v6': None, 'broadcast': '192.168.%d.255' % n, 'dns1': '192.168.%d.3' % n, 'dns2': '192.168.%d.4' % n, -- cgit From a7e7be67a79fab4348f68c5738656f1ac401c39a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 12:24:35 -0500 Subject: misplaced comma... --- nova/network/manager.py | 2 +- nova/tests/fake_network_info.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 9a4b09ad8..74e7bbf26 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -508,7 +508,7 @@ class NetworkManager(manager.SchedulerDependentManager): if network['cidr_v6']: info['ip6s'] = [ip6_dict()] - network_dict['cidr_v6'] = network['cidr_v6'], + network_dict['cidr_v6'] = network['cidr_v6'] # TODO(tr3buchet): handle ip6 routes here as well if network['gateway_v6']: info['gateway6'] = network['gateway_v6'] diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 47f9abd05..81edd998b 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -60,6 +60,8 @@ def fake_network(n, ipv6=None): rval['cidr_v6'] = '2001:db8:0:%x::/64' % n rval['gateway_v6'] = '2001:db8:0:%x::1' % n rval['netmask_v6'] = '64' + print 'asdf %s' % rval['cidr_v6'] + print type(rval['cidr_v6']) return rval -- cgit From 79602b6d9d7f3ef9777cc0e5f7a0476e1f71ffa0 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 12:26:09 -0500 Subject: forgot test print statements --- nova/tests/fake_network_info.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 81edd998b..47f9abd05 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -60,8 +60,6 @@ def fake_network(n, ipv6=None): rval['cidr_v6'] = '2001:db8:0:%x::/64' % n rval['gateway_v6'] = '2001:db8:0:%x::1' % n rval['netmask_v6'] = '64' - print 'asdf %s' % rval['cidr_v6'] - print type(rval['cidr_v6']) return rval -- cgit From 2d3c516f5943efac0ab836818a759ba9291910b4 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 12:28:14 -0500 Subject: added memory_mb to instance flavor test model --- nova/tests/fake_network_info.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 47f9abd05..6feb1e8f8 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -80,7 +80,8 @@ def fixed_ips(num_networks, num_ips, num_floating_ips=0): flavor = {'id': 0, - 'rxtx_cap': 3} + 'rxtx_cap': 3, + 'memory_mb': 512} def floating_ips(fixed_ip_id): for i in xrange(154): -- cgit From f684e293b02a168e3e45d915645142229fdc7561 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 12:29:44 -0500 Subject: added vcpus to instance flavor test model --- nova/tests/fake_network_info.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 6feb1e8f8..d89ddbe9f 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -81,7 +81,8 @@ def fixed_ips(num_networks, num_ips, num_floating_ips=0): flavor = {'id': 0, 'rxtx_cap': 3, - 'memory_mb': 512} + 'memory_mb': 512, + 'vcpus': 2} def floating_ips(fixed_ip_id): for i in xrange(154): -- cgit From 9f0015c727986d17a14f905a1779cc31397a071f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 12:33:08 -0500 Subject: updated instance type fake model --- nova/tests/fake_network_info.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index d89ddbe9f..a8e36c06b 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -80,9 +80,14 @@ def fixed_ips(num_networks, num_ips, num_floating_ips=0): flavor = {'id': 0, - 'rxtx_cap': 3, + 'name': 'fake_flavor', 'memory_mb': 512, - 'vcpus': 2} + 'vcpus': 2, + 'local_gb': 10, + 'flavor_id': 0, + 'swap': 0, + 'rxtx_quota': 0, + 'rxtx_cap': 3} def floating_ips(fixed_ip_id): for i in xrange(154): -- cgit From 49d24725031d7ebfb1a90517bcead02c09a4ebaf Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 12:38:20 -0500 Subject: update libvirt --- nova/virt/libvirt/firewall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index c2f4f91e8..0db10c7ce 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -338,8 +338,8 @@ class NWFilterFirewall(FirewallDriver): 'nova-allow-dhcp-server'] if FLAGS.use_ipv6: - networks = [network for (network, _m) in network_info if - network['gateway_v6']] + networks = [network for (network, info) in network_info if + info['gateway6']] if networks: instance_secgroup_filter_children.\ -- cgit From fe74d65f7a8ccba26a03c72edbadf4b00e9a5294 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 13:26:33 -0500 Subject: update libvirt tests --- nova/tests/test_libvirt.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 06ad7d13a..02b2f0cfe 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1377,9 +1377,9 @@ class NWFilterTestCase(test.TestCase): _setup_networking(instance_ref['id'], self.test_ip) - def _ensure_all_called(): + def _ensure_all_called(mac): instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'], - 'fake') + mac.translate(None, ':') secgroup_filter = 'nova-secgroup-%s' % self.security_group['id'] for required in [secgroup_filter, 'allow-dhcp-server', 'no-arp-spoofing', 'no-ip-spoofing', @@ -1396,10 +1396,15 @@ class NWFilterTestCase(test.TestCase): instance = db.instance_get(self.context, inst_id) network_info = _fake_network_info(self.stubs, 1) + # since there is one (network_info) there is one vif + # pass this vif's mac to _ensure_all_called() + # to set the instance_filter properly + mac = network_info[0][1]['mac'] + self.fw.setup_basic_filtering(instance, network_info) self.fw.prepare_instance_filter(instance, network_info) self.fw.apply_instance_filter(instance, network_info) - _ensure_all_called() + _ensure_all_called(mac) self.teardown_security_group() db.instance_destroy(context.get_admin_context(), instance_ref['id']) -- cgit From 2fdf2d3fb05f58d1778671830eab111328f624fc Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 13:28:17 -0500 Subject: forgot ) --- nova/tests/test_libvirt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 02b2f0cfe..ded5a99ab 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1379,7 +1379,7 @@ class NWFilterTestCase(test.TestCase): def _ensure_all_called(mac): instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'], - mac.translate(None, ':') + mac.translate(None, ':')) secgroup_filter = 'nova-secgroup-%s' % self.security_group['id'] for required in [secgroup_filter, 'allow-dhcp-server', 'no-arp-spoofing', 'no-ip-spoofing', -- cgit From 3ae6943354a8c22e65233bca38f38c3521b1cce4 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 13:32:16 -0500 Subject: updated fake values --- nova/tests/fake_network_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index a8e36c06b..079cf539b 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -81,7 +81,7 @@ def fixed_ips(num_networks, num_ips, num_floating_ips=0): flavor = {'id': 0, 'name': 'fake_flavor', - 'memory_mb': 512, + 'memory_mb': 2048, 'vcpus': 2, 'local_gb': 10, 'flavor_id': 0, -- cgit From 698e6052d70bd6da4b6b1d2cfcf096ea576c36a6 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 13:37:25 -0500 Subject: updated fake values --- nova/tests/test_libvirt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index ded5a99ab..6947a70b0 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1120,7 +1120,7 @@ class IptablesFirewallTestCase(test.TestCase): network_info = _fake_network_info(self.stubs, 1) rulesv4, rulesv6 = self.fw._filters_for_instance("fake", network_info) self.assertEquals(len(rulesv4), 2) - self.assertEquals(len(rulesv6), 3) + self.assertEquals(len(rulesv6), 1) def test_filters_for_instance_without_ip_v6(self): self.flags(use_ipv6=False) -- cgit From 02c501391ad9f6bb55771053418f10301e644c8f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 13:44:26 -0500 Subject: updated fake values --- nova/tests/test_libvirt.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 6947a70b0..1b09a3302 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1130,11 +1130,14 @@ class IptablesFirewallTestCase(test.TestCase): self.assertEquals(len(rulesv6), 0) def test_multinic_iptables(self): - ipv4_rules_per_network = 2 - ipv6_rules_per_network = 3 + ipv4_rules_per_addr = 1 + ipv4_addr_per_network = 2 + ipv6_rules_per_addr = 1 + ipv6_addr_per_network = 1 networks_count = 5 instance_ref = self._create_instance_ref() - network_info = _fake_network_info(self.stubs, networks_count) + network_info = _fake_network_info(self.stubs, networks_count, + ipv4_addr_per_network) ipv4_len = len(self.fw.iptables.ipv4['filter'].rules) ipv6_len = len(self.fw.iptables.ipv6['filter'].rules) inst_ipv4, inst_ipv6 = self.fw.instance_rules(instance_ref, @@ -1145,9 +1148,9 @@ class IptablesFirewallTestCase(test.TestCase): ipv4_network_rules = len(ipv4) - len(inst_ipv4) - ipv4_len ipv6_network_rules = len(ipv6) - len(inst_ipv6) - ipv6_len self.assertEquals(ipv4_network_rules, - ipv4_rules_per_network * networks_count) + ipv4_rules_per_addr * ipv4_addr_per_network * networks_count) self.assertEquals(ipv6_network_rules, - ipv6_rules_per_network * networks_count) + ipv6_rules_per_addr * ipv4_addr_per_network * networks_count) def test_do_refresh_security_group_rules(self): instance_ref = self._create_instance_ref() -- cgit From adf03e7274636cd58d9c04461896a50f43934e15 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 13:47:54 -0500 Subject: updated fake values --- nova/tests/test_libvirt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 1b09a3302..d8bc9575b 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1150,7 +1150,7 @@ class IptablesFirewallTestCase(test.TestCase): self.assertEquals(ipv4_network_rules, ipv4_rules_per_addr * ipv4_addr_per_network * networks_count) self.assertEquals(ipv6_network_rules, - ipv6_rules_per_addr * ipv4_addr_per_network * networks_count) + ipv6_rules_per_addr * ipv6_addr_per_network * networks_count) def test_do_refresh_security_group_rules(self): instance_ref = self._create_instance_ref() -- cgit From 477510214e80be21a40bff6c053090793653f6a1 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 15:09:39 -0500 Subject: couple of pep8s --- nova/tests/fake_network_info.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py index 079cf539b..2e8cf60ec 100644 --- a/nova/tests/fake_network_info.py +++ b/nova/tests/fake_network_info.py @@ -89,6 +89,7 @@ flavor = {'id': 0, 'rxtx_quota': 0, 'rxtx_cap': 3} + def floating_ips(fixed_ip_id): for i in xrange(154): yield {'id': 0, -- cgit From d0f42b4bf6e60485c7bebe1c60dccce48a0ddcb3 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 16:55:52 -0500 Subject: stubbed some stuff in test_libvirt --- nova/tests/test_libvirt.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index d8bc9575b..58e78daf4 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1063,10 +1063,17 @@ class IptablesFirewallTestCase(test.TestCase): return '', '' print cmd, kwargs + def get_fixed_ips(*args, **kwargs): + ips = [] + for network, info in network_info: + ips.extend(info['ips']) + return [ip['ip'] for ip in ips] + from nova.network import linux_net linux_net.iptables_manager.execute = fake_iptables_execute network_info = _fake_network_info(self.stubs, 1) + self.stubs.Set(self.db, 'instance_get_fixed_addresses', get_fixed_ips) self.fw.prepare_instance_filter(instance_ref, network_info) self.fw.apply_instance_filter(instance_ref, network_info) -- cgit From 9737731fff7f6d4d19213b453315610bd095a0e6 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 17:00:06 -0500 Subject: updated libvirt test --- nova/tests/test_libvirt.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 58e78daf4..2ff6b3953 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1073,7 +1073,7 @@ class IptablesFirewallTestCase(test.TestCase): linux_net.iptables_manager.execute = fake_iptables_execute network_info = _fake_network_info(self.stubs, 1) - self.stubs.Set(self.db, 'instance_get_fixed_addresses', get_fixed_ips) + self.stubs.Set(db, 'instance_get_fixed_addresses', get_fixed_ips) self.fw.prepare_instance_filter(instance_ref, network_info) self.fw.apply_instance_filter(instance_ref, network_info) @@ -1111,10 +1111,11 @@ class IptablesFirewallTestCase(test.TestCase): self.assertTrue(len(filter(regex.match, self.out_rules)) > 0, "ICMP Echo Request acceptance rule wasn't added") - regex = re.compile('-A .* -j ACCEPT -p tcp -m multiport ' - '--dports 80:81 -s %s' % (src_ip,)) - self.assertTrue(len(filter(regex.match, self.out_rules)) > 0, - "TCP port 80/81 acceptance rule wasn't added") + for ip in get_fixed_ips(): + regex = re.compile('-A .* -j ACCEPT -p tcp -m multiport ' + '--dports 80:81 -s %s' % ip) + self.assertTrue(len(filter(regex.match, self.out_rules)) > 0, + "TCP port 80/81 acceptance rule wasn't added") regex = re.compile('-A .* -j ACCEPT -p tcp ' '-m multiport --dports 80:81 -s 192.168.10.0/24') -- cgit From 156430b2d795833245c069f83c435a8a240556fd Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 26 Aug 2011 17:04:30 -0500 Subject: removed self.test ip and _setup_networking from libvirt --- nova/tests/test_libvirt.py | 64 +++------------------------------------------- 1 file changed, 3 insertions(+), 61 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 2ff6b3953..fe5470a6f 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -41,57 +41,14 @@ from nova.tests import fake_network_info libvirt = None FLAGS = flags.FLAGS +_fake_network_info = fake_network_info.fake_get_instance_nw_info +_ipv4_like = fake_network_info.ipv4_like + def _concurrency(wait, done, target): wait.wait() done.send() -_fake_network_info = fake_network_info.fake_get_instance_nw_info -_ipv4_like = fake_network_info.ipv4_like -def _create_network_info(count=1, ipv6=None): - if ipv6 is None: - ipv6 = FLAGS.use_ipv6 - fake = 'fake' - fake_ip = '10.11.12.13' - fake_ip_2 = '0.0.0.1' - fake_ip_3 = '0.0.0.1' - fake_vlan = 100 - fake_bridge_interface = 'eth0' - network = {'bridge': fake, - 'cidr': fake_ip, - 'cidr_v6': fake_ip, - 'gateway_v6': fake, - 'vlan': fake_vlan, - 'bridge_interface': fake_bridge_interface} - mapping = {'mac': fake, - 'dhcp_server': '10.0.0.1', - 'gateway': fake, - 'gateway6': fake, - 'ips': [{'ip': fake_ip}, {'ip': fake_ip}]} - if ipv6: - mapping['ip6s'] = [{'ip': fake_ip}, - {'ip': fake_ip_2}, - {'ip': fake_ip_3}] - return [(network, mapping) for x in xrange(0, count)] - - -def _setup_networking(instance_id, ip='1.2.3.4', mac='56:12:12:12:12:12'): - ctxt = context.get_admin_context() - network_ref = db.project_get_networks(ctxt, - 'fake', - associate=True)[0] - vif = {'address': mac, - 'network_id': network_ref['id'], - 'instance_id': instance_id} - vif_ref = db.virtual_interface_create(ctxt, vif) - - fixed_ip = {'address': ip, - 'network_id': network_ref['id'], - '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}) - class CacheConcurrencyTestCase(test.TestCase): def setUp(self): @@ -164,7 +121,6 @@ class LibvirtConnTestCase(test.TestCase): self.context = context.get_admin_context() self.flags(instances_path='') self.call_libvirt_dependant_setup = False - self.test_ip = '10.11.12.13' test_instance = {'memory_kb': '1024000', 'basepath': '/some/path', @@ -426,7 +382,6 @@ class LibvirtConnTestCase(test.TestCase): user_context = context.RequestContext(self.user_id, self.project_id) instance_ref = db.instance_create(user_context, instance) - _setup_networking(instance_ref['id'], self.test_ip) self.flags(libvirt_type='lxc') conn = connection.LibvirtConnection(True) @@ -458,8 +413,6 @@ class LibvirtConnTestCase(test.TestCase): network_ref = db.project_get_networks(context.get_admin_context(), self.project_id)[0] - _setup_networking(instance_ref['id'], self.test_ip) - type_uri_map = {'qemu': ('qemu:///system', [(lambda t: t.find('.').get('type'), 'qemu'), (lambda t: t.find('./os/type').text, 'hvm'), @@ -925,7 +878,6 @@ 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) @@ -989,10 +941,6 @@ class IptablesFirewallTestCase(test.TestCase): def test_static_filters(self): instance_ref = self._create_instance_ref() src_instance_ref = self._create_instance_ref() - src_ip = '10.11.12.14' - src_mac = '56:12:12:12:12:13' - _setup_networking(instance_ref['id'], self.test_ip, src_mac) - _setup_networking(src_instance_ref['id'], src_ip) admin_ctxt = context.get_admin_context() secgroup = db.security_group_create(admin_ctxt, @@ -1184,7 +1132,6 @@ class IptablesFirewallTestCase(test.TestCase): fakefilter.nwfilterLookupByName instance_ref = self._create_instance_ref() - _setup_networking(instance_ref['id'], self.test_ip) network_info = _fake_network_info(self.stubs, 1) self.fw.setup_basic_filtering(instance_ref, network_info) self.fw.prepare_instance_filter(instance_ref, network_info) @@ -1200,7 +1147,6 @@ class IptablesFirewallTestCase(test.TestCase): def test_provider_firewall_rules(self): # setup basic instance data instance_ref = self._create_instance_ref() - _setup_networking(instance_ref['id'], self.test_ip) # FRAGILE: peeks at how the firewall names chains chain_name = 'inst-%s' % instance_ref['id'] @@ -1270,7 +1216,6 @@ 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) @@ -1386,8 +1331,6 @@ class NWFilterTestCase(test.TestCase): instance_ref = self._create_instance() inst_id = instance_ref['id'] - _setup_networking(instance_ref['id'], self.test_ip) - def _ensure_all_called(mac): instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'], mac.translate(None, ':')) @@ -1444,7 +1387,6 @@ class NWFilterTestCase(test.TestCase): instance = db.instance_get(self.context, inst_id) - _setup_networking(instance_ref['id'], self.test_ip) network_info = _fake_network_info(self.stubs, 1) self.fw.setup_basic_filtering(instance, network_info) self.fw.prepare_instance_filter(instance, network_info) -- cgit From edc43980ff56777e8200022f888c7170ca9baaea Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Tue, 30 Aug 2011 11:26:59 -0400 Subject: Use getCapabilities rather than getInfo() since some versions of libvirt dont provide dmi information --- nova/virt/libvirt/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 363a20ed0..6ae458537 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -196,7 +196,7 @@ class LibvirtConnection(driver.ComputeDriver): def _test_connection(self): try: - self._wrapped_conn.getInfo() + self._wrapped_conn.geCapabilities() return True except libvirt.libvirtError as e: if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \ -- cgit From b515d427e05010ba5a984dd549cb6418629de50d Mon Sep 17 00:00:00 2001 From: Thuleau Édouard Date: Tue, 30 Aug 2011 18:18:23 +0200 Subject: With OS API, if the property 'ramdisk_id' isn't set on the AMI image, Nova can not instantiate it. With EC2 API, the AMI image can be instantiate. --- nova/api/openstack/create_instance_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 483ff4985..269e46dba 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -283,7 +283,7 @@ class CreateInstanceHelper(object): try: ramdisk_id = image_meta['properties']['ramdisk_id'] except KeyError: - raise exception.RamdiskNotFoundForImage(image_id=image_id) + ramdisk_id = None return kernel_id, ramdisk_id -- cgit From e8db401156cdb842ef7bf15e44e34ac5ae672e46 Mon Sep 17 00:00:00 2001 From: Thuleau Édouard Date: Tue, 30 Aug 2011 18:27:48 +0200 Subject: The exception 'RamdiskNotFoundForImage' is no longer used. --- nova/exception.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'nova') diff --git a/nova/exception.py b/nova/exception.py index 32981f4d5..aa3407480 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -402,10 +402,6 @@ class KernelNotFoundForImage(ImageNotFound): message = _("Kernel not found for image %(image_id)s.") -class RamdiskNotFoundForImage(ImageNotFound): - message = _("Ramdisk not found for image %(image_id)s.") - - class UserNotFound(NotFound): message = _("User %(user_id)s could not be found.") -- cgit From ced3ea3e8d7cf02f988d968d6078182815226719 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Wed, 31 Aug 2011 12:23:43 -0700 Subject: fix for chris behrens' comment - move tenant_id => project_id mapping to compute.api.get_all --- nova/api/openstack/servers.py | 5 ----- nova/compute/api.py | 1 + nova/tests/api/openstack/test_servers.py | 11 ++++++----- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 3e76fa1a0..5bbb4e52e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -107,11 +107,6 @@ class Controller(object): LOG.error(reason) raise exception.InvalidInput(reason=reason) - # translate tenant_id filter to internal project_id - if 'tenant_id' in search_opts: - search_opts['project_id'] = search_opts['tenant_id'] - del search_opts['tenant_id'] - # By default, compute's get_all() will return deleted instances. # If an admin hasn't specified a 'deleted' search option, we need # to filter out deleted instances by setting the filter ourselves. diff --git a/nova/compute/api.py b/nova/compute/api.py index 60a13631a..95d6a901d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -854,6 +854,7 @@ class API(base.Base): 'image': 'image_ref', 'name': 'display_name', 'instance_name': 'name', + 'tenant_id': 'project_id', 'recurse_zones': None, 'flavor': _remap_flavor_filter, 'fixed_ip': _remap_fixed_ip_filter} diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 435644dcc..1ecb35f63 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1200,13 +1200,14 @@ class ServersTest(test.TestCase): self.assertEqual(servers[0]['id'], 100) def test_tenant_id_filter_converts_to_project_id_for_admin(self): - def fake_get_all(compute_self, context, search_opts=None): - self.assertNotEqual(search_opts, None) - self.assertEqual(search_opts['project_id'], 'faketenant') - self.assertFalse(search_opts.get('tenant_id')) + def fake_get_all(context, filters=None): + self.assertNotEqual(filters, None) + self.assertEqual(filters['project_id'], 'faketenant') + self.assertFalse(filters.get('tenant_id')) return [stub_instance(100)] - self.stubs.Set(nova.compute.API, 'get_all', fake_get_all) + self.stubs.Set(nova.db.api, 'instance_get_all_by_filters', + fake_get_all) self.flags(allow_admin_api=True) req = webob.Request.blank('/v1.1/fake/servers?tenant_id=faketenant') -- cgit From f4dc231069c530f8f6055b1b7fa006750795b6e4 Mon Sep 17 00:00:00 2001 From: Brad Hall Date: Wed, 31 Aug 2011 18:54:15 -0700 Subject: Add comment for an uncommon failure case that we need to fix --- nova/network/quantum/manager.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova') diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index c03622218..2fd9175ae 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -213,6 +213,12 @@ class QuantumManager(manager.FlatManager): net_id, port_id = self.q_conn.get_port_by_attachment( q_tenant_id, vif['uuid']) if not net_id: + # TODO(bgh): We need to figure out a way to tell if we + # should actually be raising this exception or not. + # In the case that a VM spawn failed it may not have + # attached the vif and raising the exception here + # prevents deltion of the VM. In that case we should + # probably just log, continue, and move on. raise Exception(_("No network for for virtual interface %s") % vif['uuid']) (v4_subnet, v6_subnet) = self.ipam.get_subnets_by_net_id(context, -- cgit From 3ae639e5acd965999138e307508933ae1624b476 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Wed, 31 Aug 2011 23:12:09 -0700 Subject: add specific exceptions for quantum client. Fix doc-strings in client.py --- nova/network/quantum/client.py | 93 ++++++++++++++---------------- nova/network/quantum/quantum_connection.py | 7 +-- 2 files changed, 46 insertions(+), 54 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index 82f23b6b5..22a4b4d63 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -39,6 +39,24 @@ class JSONSerializer(object): return json.loads(data) +# FIXME: (danwent) the full client lib will expose more +# granular exceptions, for now, just try to distinguish +# between the cases we care about. +class QuantumNotFoundException(Exception); + """ Indicates that Quantum Server returned 404""" + pass + + +class QuantumServerException(Exception): + """ Indicates any non-404 error from Quantum Server""" + pass + + +class QuantumIOException(Exception): + """ Indicates network IO trouble reaching Quantum Server""" + pass + + class api_call(object): """A Decorator to add support for format and tenant overriding""" def __init__(self, func): @@ -78,8 +96,7 @@ class Client(object): def __init__(self, host="127.0.0.1", port=9696, use_ssl=False, tenant=None, format="xml", testing_stub=None, key_file=None, cert_file=None, logger=None): - """ - Creates a new client to some service. + """ Creates a new client to some service. :param host: The host where service resides :param port: The port where service resides @@ -102,9 +119,7 @@ class Client(object): self.logger = logger def get_connection_type(self): - """ - Returns the proper connection type - """ + """ Returns the proper connection type """ if self.testing_stub: return self.testing_stub elif self.use_ssl: @@ -114,8 +129,7 @@ class Client(object): def do_request(self, method, action, body=None, headers=None, params=None): - """ - Connects to the server and issues a request. + """ Connects to the server and issues a request. Returns the result data, or raises an appropriate exception if HTTP status code is not 2xx @@ -168,6 +182,10 @@ class Client(object): self.logger.debug("Quantum Client Reply (code = %s) :\n %s" \ % (str(status_code), data)) + if status_code == httplib.NOT_FOUND: + raise QuantumNotFoundException( + _("Quantum entity not found: %s" % data)) + if status_code in (httplib.OK, httplib.CREATED, httplib.ACCEPTED, @@ -175,15 +193,16 @@ class Client(object): if data is not None and len(data): return self.deserialize(data, status_code) else: - raise Exception(_("Server returned error: %s" % res.read())) + raise QuantumServerException( + _("Server %(status_code)s error: %(data)s" + % locals())) except (socket.error, IOError), e: - raise Exception(_("Unable to connect to " + raise QuantumIOException(_("Unable to connect to " "server. Got error: %s" % e)) def get_status_code(self, response): - """ - Returns the integer status code from the response, which + """ Returns the integer status code from the response, which can be either a Webob.Response (used in testing) or httplib.Response """ if hasattr(response, 'status_int'): @@ -212,99 +231,73 @@ class Client(object): @api_call def list_networks(self): - """ - Fetches a list of all networks for a tenant - """ + """ Fetches a list of all networks for a tenant """ return self.do_request("GET", self.networks_path) @api_call def show_network_details(self, network): - """ - Fetches the details of a certain network - """ + """ Fetches the details of a certain network """ return self.do_request("GET", self.network_path % (network)) @api_call def create_network(self, body=None): - """ - Creates a new network - """ + """ Creates a new network """ body = self.serialize(body) return self.do_request("POST", self.networks_path, body=body) @api_call def update_network(self, network, body=None): - """ - Updates a network - """ + """ Updates a network """ body = self.serialize(body) return self.do_request("PUT", self.network_path % (network), body=body) @api_call def delete_network(self, network): - """ - Deletes the specified network - """ + """ Deletes the specified network """ return self.do_request("DELETE", self.network_path % (network)) @api_call def list_ports(self, network): - """ - Fetches a list of ports on a given network - """ + """ Fetches a list of ports on a given network """ return self.do_request("GET", self.ports_path % (network)) @api_call def show_port_details(self, network, port): - """ - Fetches the details of a certain port - """ + """ Fetches the details of a certain port """ return self.do_request("GET", self.port_path % (network, port)) @api_call def create_port(self, network, body=None): - """ - Creates a new port on a given network - """ + """ Creates a new port on a given network """ body = self.serialize(body) return self.do_request("POST", self.ports_path % (network), body=body) @api_call def delete_port(self, network, port): - """ - Deletes the specified port from a network - """ + """ Deletes the specified port from a network """ return self.do_request("DELETE", self.port_path % (network, port)) @api_call def set_port_state(self, network, port, body=None): - """ - Sets the state of the specified port - """ + """ Sets the state of the specified port """ body = self.serialize(body) return self.do_request("PUT", self.port_path % (network, port), body=body) @api_call def show_port_attachment(self, network, port): - """ - Fetches the attachment-id associated with the specified port - """ + """ Fetches the attachment-id associated with the specified port """ return self.do_request("GET", self.attachment_path % (network, port)) @api_call def attach_resource(self, network, port, body=None): - """ - Sets the attachment-id of the specified port - """ + """ Sets the attachment-id of the specified port """ body = self.serialize(body) return self.do_request("PUT", self.attachment_path % (network, port), body=body) @api_call def detach_resource(self, network, port): - """ - Removes the attachment-id of the specified port - """ + """ Removes the attachment-id of the specified port """ return self.do_request("DELETE", self.attachment_path % (network, port)) diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index e2218c68d..90d5e8e04 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -70,11 +70,10 @@ class QuantumClientConnection(object): """ try: self.client.show_network_details(net_id, tenant=tenant_id) - except: - # FIXME(danwent): client lib should expose granular exceptions - # so we can confirm we're getting a 404 and not some other error + return True + except client.QuantumNotFoundException: + # Not really an error. Real errors will be propogated to caller return False - return True def create_and_attach_port(self, tenant_id, net_id, interface_id): """ Creates a Quantum port on the specified network, sets -- cgit From 02093fbe185e52a3c22b748811e60e988150a352 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Wed, 31 Aug 2011 23:37:26 -0700 Subject: more review cleanup --- nova/network/quantum/manager.py | 16 ++++++++-------- nova/network/quantum/nova_ipam_lib.py | 8 +++----- 2 files changed, 11 insertions(+), 13 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 1d2c7c664..153f6c0f2 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -103,16 +103,16 @@ class QuantumManager(manager.FlatManager): priority, cidr, gateway_v6, cidr_v6, dns1, dns2) def delete_network(self, context, fixed_range): - """ Lookup network by IPv4 cidr, delete both the IPAM - subnet and the corresponding Quantum network. - """ - project_id = context.project_id - quantum_net_id = self.ipam.get_network_id_by_cidr( + """ Lookup network by IPv4 cidr, delete both the IPAM + subnet and the corresponding Quantum network. + """ + project_id = context.project_id + quantum_net_id = self.ipam.get_network_id_by_cidr( context, fixed_range, project_id) - self.ipam.delete_subnets_by_net_id(context, quantum_net_id, + self.ipam.delete_subnets_by_net_id(context, quantum_net_id, project_id) - q_tenant_id = project_id or FLAGS.quantum_default_tenant_id - self.q_conn.delete_network(q_tenant_id, quantum_net_id) + q_tenant_id = project_id or FLAGS.quantum_default_tenant_id + self.q_conn.delete_network(q_tenant_id, quantum_net_id) def allocate_for_instance(self, context, **kwargs): """ Called by compute when it is creating a new VM. diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index 4f62887d1..4b08b906a 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -15,7 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. -import math +import netaddr from nova import db from nova import exception @@ -60,8 +60,7 @@ class QuantumNovaIPAMLib(object): are needed by Quantum but not the FlatManager. """ admin_context = context.elevated() - # FIXME(danwent): Use the netaddr library here - subnet_size = int(math.pow(2, (32 - int(cidr.split("/")[1])))) + subnet_size = len(netaddr.IPNetwork(cidr)) networks = manager.FlatManager.create_networks(self.net_manager, admin_context, label, cidr, False, 1, subnet_size, cidr_v6, @@ -114,8 +113,7 @@ class QuantumNovaIPAMLib(object): return sorted(net_list, key=lambda x: id_priority_map[x[0]]) def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): - """ Allocates a single fixed IPv4 address for a virtual interface. - """ + """ Allocates a single fixed IPv4 address for a virtual interface.""" admin_context = context.elevated() network = db.network_get_by_uuid(admin_context, quantum_net_id) if network['cidr']: -- cgit From 76a60bf27cc8864e397139a3497b1f571ce38d88 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Wed, 31 Aug 2011 23:42:24 -0700 Subject: typo --- nova/network/quantum/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index 22a4b4d63..67582d8bc 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -42,7 +42,7 @@ class JSONSerializer(object): # FIXME: (danwent) the full client lib will expose more # granular exceptions, for now, just try to distinguish # between the cases we care about. -class QuantumNotFoundException(Exception); +class QuantumNotFoundException(Exception): """ Indicates that Quantum Server returned 404""" pass -- cgit From 5b3b3d1b357c085c2088df7d76df8392118fb82e Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 1 Sep 2011 09:39:36 -0700 Subject: additional review cleanup --- nova/network/quantum/client.py | 12 ++++++++---- nova/network/quantum/fake.py | 10 ++++++---- nova/network/quantum/manager.py | 2 +- nova/network/quantum/melange_connection.py | 4 +++- nova/network/quantum/melange_ipam_lib.py | 3 ++- nova/network/quantum/nova_ipam_lib.py | 2 +- nova/network/quantum/quantum_connection.py | 8 ++++---- nova/tests/test_quantum.py | 21 +++++++++++---------- 8 files changed, 36 insertions(+), 26 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index 67582d8bc..455bb8a79 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -22,11 +22,15 @@ import socket import urllib +#FIXME(danwent): All content in this file should be removed once the +# packaging work for the quantum client libraries is complete. +# At that point, we will be able to just install the libraries as a +# dependency and import from quantum.client.* and quantum.common.* +# Until then, we have simplified versions of these classes in this file. + class JSONSerializer(object): - """ - This is a simple json-only serializer to use until we can just grab + """ This is a simple json-only serializer to use until we can just grab the standard serializer from the quantum library. - TODO(danwent): replace serializer with quantum implementation """ def serialize(self, data, content_type): try: @@ -39,7 +43,7 @@ class JSONSerializer(object): return json.loads(data) -# FIXME: (danwent) the full client lib will expose more +# The full client lib will expose more # granular exceptions, for now, just try to distinguish # between the cases we care about. class QuantumNotFoundException(Exception): diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py index f668edfed..6a4005c59 100644 --- a/nova/network/quantum/fake.py +++ b/nova/network/quantum/fake.py @@ -65,8 +65,9 @@ class FakeQuantumClientConnection(object): def create_and_attach_port(self, tenant_id, net_id, interface_id): if not self.network_exists(tenant_id, net_id): - raise Exception(_("network %s does not exist for tenant %s" % - (net_id, tenant_id))) + raise Exception( + _("network %(net_id)s does not exist for tenant %(tenant_id)" + % locals())) self._confirm_not_attached(interface_id) uuid = str(utils.gen_uuid()) @@ -76,8 +77,9 @@ class FakeQuantumClientConnection(object): def detach_and_delete_port(self, tenant_id, net_id, port_id): if not self.network_exists(tenant_id, net_id): - raise Exception(_("network %s does not exist for tenant %s" %\ - (net_id, tenant_id))) + raise exception.NotFound( + _("network %s does not exist for tenant %s" % + (net_id, tenant_id))) del self.nets[net_id]['ports'][port_id] def get_port_by_attachment(self, tenant_id, attachment_id): diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 153f6c0f2..709299dd2 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -86,7 +86,7 @@ class QuantumManager(manager.FlatManager): if num_networks != 1: raise Exception(_("QuantumManager requires that only one" " network is created per call")) - q_tenant_id = kwargs["project_id"] or FLAGS.quantum_default_tenant_id + q_tenant_id = kwargs.get("project_id", FLAGS.quantum_default_tenant_id) quantum_net_id = uuid if quantum_net_id: if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index 1ee0c29a2..4dc35d15d 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -35,7 +35,9 @@ flags.DEFINE_string('melange_port', json_content_type = {'Content-type': "application/json"} - +#FIXME(danwent): talk to the Melange folks about creating a +# client lib that we can import as a library, instead of +# have to have all of the client code in here. class MelangeConnection(object): def __init__(self, host=None, port=None, use_ssl=False): diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index 24a7c5404..71c0d7ce6 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -17,6 +17,7 @@ from netaddr import IPNetwork +from nova import exception from nova import flags from nova import log as logging from nova.network.quantum import melange_connection @@ -85,7 +86,7 @@ class QuantumMelangeIPAMLib(object): for b in all_blocks['ip_blocks']: if b['cidr'] == cidr: return b['network_id'] - raise Exception(_("No network found for cidr %s" % cidr)) + raise exception.NotFound(_("No network found for cidr %s" % cidr)) def delete_subnets_by_net_id(self, context, net_id, project_id): """ Find Melange block associated with the Quantum UUID, diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index 4b08b906a..17236a976 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -154,7 +154,7 @@ class QuantumNovaIPAMLib(object): vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) fixed_ips = db.fixed_ip_get_by_virtual_interface(context, vif_rec['id']) - return [f['address'] for f in fixed_ips] + return [fixed_ip['address'] for fixed_ip in fixed_ips] def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): """ Returns a list containing a single IPv6 address strings diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index 90d5e8e04..93892a843 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -80,8 +80,8 @@ class QuantumClientConnection(object): status to ACTIVE to enable traffic, and attaches the vNIC with the specified interface-id. """ - LOG.debug(_("Connecting interface %s to net %s for %s" % - (interface_id, net_id, tenant_id))) + LOG.debug(_("Connecting interface %(interface_id)s to " + "net %(net_id)s for %(tenant_id)s" % locals())) port_data = {'port': {'state': 'ACTIVE'}} resdict = self.client.create_port(net_id, port_data, tenant=tenant_id) port_id = resdict["port"]["id"] @@ -92,8 +92,8 @@ class QuantumClientConnection(object): def detach_and_delete_port(self, tenant_id, net_id, port_id): """ Detach and delete the specified Quantum port. """ - LOG.debug("Deleting port %s on net %s for %s" % \ - (port_id, net_id, tenant_id)) + LOG.debug(_("Deleting port %(port_id)s on net %(net_id)s" + " for %(tenant_id)s" % locals())) self.client.detach_resource(net_id, port_id, tenant=tenant_id) self.client.delete_port(net_id, port_id, tenant=tenant_id) diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 2cc83adf1..0fa4184b1 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -189,29 +189,29 @@ class QuantumTestCaseBase(object): # we don't know which order the NICs will be in until we # introduce the notion of priority # v4 cidr - self.assertTrue(nw_info[0][0]['cidr'].startswith("9.") or \ + self.assertTrue(nw_info[0][0]['cidr'].startswith("9.") or nw_info[1][0]['cidr'].startswith("9.")) - self.assertTrue(nw_info[0][0]['cidr'].startswith("192.") or \ + self.assertTrue(nw_info[0][0]['cidr'].startswith("192.") or nw_info[1][0]['cidr'].startswith("192.")) # v4 address - self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("9.") or \ + self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("9.") or nw_info[1][1]['ips'][0]['ip'].startswith("9.")) - self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("192.") or \ + self.assertTrue(nw_info[0][1]['ips'][0]['ip'].startswith("192.") or nw_info[1][1]['ips'][0]['ip'].startswith("192.")) # v6 cidr - self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1dbb:") or \ + self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1dbb:") or nw_info[1][0]['cidr_v6'].startswith("2001:1dbb:")) - self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1db9:") or \ + self.assertTrue(nw_info[0][0]['cidr_v6'].startswith("2001:1db9:") or nw_info[1][0]['cidr_v6'].startswith("2001:1db9:")) # v6 address self.assertTrue(\ - nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dbb:") or \ + nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dbb:") or nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1dbb:")) self.assertTrue(\ - nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1db9:") or \ + nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1db9:") or nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1db9:")) self.net_man.deallocate_for_instance(ctx, @@ -240,9 +240,10 @@ class QuantumNovaIPAMTestCase(QuantumTestCaseBase, test.TestCase): for n in db.network_get_all(ctx): db.network_delete_safe(ctx, n['id']) - # NOTE(danwent): I've found that other unit tests have a nasty + # Other unit tests (e.g., test_compute.py) have a nasty # habit of of creating fixed IPs and not cleaning up, which - # can confuse these tests, so we clean them all. + # can confuse these tests, so we remove all existing fixed + # ips before starting. session = get_session() result = session.query(models.FixedIp).all() with session.begin(): -- cgit From 35c4cecc8d29da32bd816bb68f8b45c2d03f892f Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 1 Sep 2011 09:42:44 -0700 Subject: undo change in setting q_tenant_id in quantum_manager.create_network --- nova/network/quantum/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 709299dd2..153f6c0f2 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -86,7 +86,7 @@ class QuantumManager(manager.FlatManager): if num_networks != 1: raise Exception(_("QuantumManager requires that only one" " network is created per call")) - q_tenant_id = kwargs.get("project_id", FLAGS.quantum_default_tenant_id) + q_tenant_id = kwargs["project_id"] or FLAGS.quantum_default_tenant_id quantum_net_id = uuid if quantum_net_id: if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): -- cgit From a36e0a1cbc5645ab47041c2627dba80b39b23cc2 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 1 Sep 2011 15:42:01 -0400 Subject: Fixes for minor network manager issues centered around deleting/accessing instances which don't have network information set. --- nova/api/openstack/views/addresses.py | 1 - nova/network/manager.py | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index 8f07a2289..8d38bc9c3 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -88,7 +88,6 @@ class ViewBuilderV11(ViewBuilder): try: return interface['network']['label'] except (TypeError, KeyError) as exc: - LOG.exception(exc) raise TypeError def _extract_ipv4_addresses(self, interface): diff --git a/nova/network/manager.py b/nova/network/manager.py index e6b30d1a0..d7b0abdda 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -448,7 +448,7 @@ class NetworkManager(manager.SchedulerDependentManager): try: fixed_ips = kwargs.get('fixed_ips') or \ self.db.fixed_ip_get_by_instance(context, instance_id) - except exceptions.FixedIpNotFoundForInstance: + except exception.FixedIpNotFoundForInstance: fixed_ips = [] LOG.debug(_("network deallocation for instance |%s|"), instance_id, context=context) @@ -484,6 +484,9 @@ class NetworkManager(manager.SchedulerDependentManager): for vif in vifs: network = vif['network'] + if network is None: + continue + # determine which of the instance's IPs belong to this network network_IPs = [fixed_ip['address'] for fixed_ip in fixed_ips if fixed_ip['network_id'] == network['id']] -- cgit From 833ac1674d33cde3721b2d10a3d9545cc8320b37 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 1 Sep 2011 16:09:23 -0400 Subject: Added test for NULL network. --- nova/network/manager.py | 4 ++-- nova/tests/test_network.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index d7b0abdda..e8c5c0654 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -484,8 +484,8 @@ class NetworkManager(manager.SchedulerDependentManager): for vif in vifs: network = vif['network'] - if network is None: - continue + #if network is None: + # continue # determine which of the instance's IPs belong to this network network_IPs = [fixed_ip['address'] for fixed_ip in fixed_ips if diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 25ff940f0..2ae5a35e3 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -118,9 +118,14 @@ vifs = [{'id': 0, {'id': 1, 'address': 'DE:AD:BE:EF:00:01', 'uuid': '00000000-0000-0000-0000-0000000000000001', - 'network_id': 0, 'network_id': 1, 'network': FakeModel(**networks[1]), + 'instance_id': 0}, + {'id': 2, + 'address': 'DE:AD:BE:EF:00:02', + 'uuid': '00000000-0000-0000-0000-0000000000000002', + 'network_id': 2, + 'network': None, 'instance_id': 0}] -- cgit From aef85a2596e943299542f05e165774250476bc5b Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Thu, 1 Sep 2011 16:11:14 -0400 Subject: Probably shouldn't leave that commented out. --- nova/network/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index e8c5c0654..d7b0abdda 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -484,8 +484,8 @@ class NetworkManager(manager.SchedulerDependentManager): for vif in vifs: network = vif['network'] - #if network is None: - # continue + if network is None: + continue # determine which of the instance's IPs belong to this network network_IPs = [fixed_ip['address'] for fixed_ip in fixed_ips if -- cgit From ab8cfda10ed12dd592ef3b06806a80b1922707c1 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 1 Sep 2011 15:29:00 -0500 Subject: moved cidr_v6 back --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 8fd176c26..b4605eea5 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -499,6 +499,7 @@ class NetworkManager(manager.SchedulerDependentManager): 'bridge': network['bridge'], 'id': network['id'], 'cidr': network['cidr'], + 'cidr_v6': network['cidr_v6'], 'injected': network['injected'], 'vlan': network['vlan'], 'bridge_interface': network['bridge_interface'], @@ -524,7 +525,6 @@ class NetworkManager(manager.SchedulerDependentManager): if network['cidr_v6']: info['ip6s'] = [ip6_dict()] - network_dict['cidr_v6'] = network['cidr_v6'] # TODO(tr3buchet): handle ip6 routes here as well if network['gateway_v6']: info['gateway6'] = network['gateway_v6'] -- cgit From 04cfb6356b920941080fbc58301b6d005d21ac5f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Thu, 1 Sep 2011 15:50:38 -0500 Subject: renamed fake_network_info.py --- nova/tests/fake_network.py | 144 ++++++++++++++++++++++++++++++++++++++++ nova/tests/fake_network_info.py | 144 ---------------------------------------- nova/tests/test_libvirt.py | 6 +- nova/tests/test_network.py | 4 +- 4 files changed, 149 insertions(+), 149 deletions(-) create mode 100644 nova/tests/fake_network.py delete mode 100644 nova/tests/fake_network_info.py (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py new file mode 100644 index 000000000..2e8cf60ec --- /dev/null +++ b/nova/tests/fake_network.py @@ -0,0 +1,144 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Rackspace +# 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. + +from nova import db +from nova import flags +from nova import test +from nova.network import manager as network_manager + + +HOST = "testhost" +FLAGS = flags.FLAGS + + +class FakeModel(dict): + """Represent a model from the db""" + def __init__(self, *args, **kwargs): + self.update(kwargs) + + def __getattr__(self, name): + return self[name] + + +def fake_network(n, ipv6=None): + if ipv6 == None: + ipv6 = FLAGS.use_ipv6 + rval = {'id': n, + 'label': 'test%d' % n, + 'injected': False, + 'multi_host': False, + 'cidr': '192.168.%d.0/24' % n, + 'cidr_v6': None, + 'netmask': '255.255.255.0', + 'netmask_v6': None, + 'bridge': 'fake_br%d' % n, + 'bridge_interface': 'fake_eth%d' % n, + 'gateway': '192.168.%d.1' % n, + 'gateway_v6': None, + 'broadcast': '192.168.%d.255' % n, + 'dns1': '192.168.%d.3' % n, + 'dns2': '192.168.%d.4' % n, + 'vlan': None, + 'host': None, + 'project_id': 'fake_project', + 'vpn_public_address': '192.168.%d.2' % n} + if ipv6: + rval['cidr_v6'] = '2001:db8:0:%x::/64' % n + rval['gateway_v6'] = '2001:db8:0:%x::1' % n + rval['netmask_v6'] = '64' + + return rval + + +def fixed_ips(num_networks, num_ips, num_floating_ips=0): + for network in xrange(num_networks): + for ip in xrange(num_ips): + id = network * num_ips + ip + f_ips = [floating_ips(id).next() for i in xrange(num_floating_ips)] + yield {'id': id, + 'network_id': network, + 'address': '192.168.%d.1%02d' % (network, ip), + 'instance_id': 0, + 'allocated': False, + # and since network_id and vif_id happen to be equivalent + 'virtual_interface_id': network, + 'floating_ips': [FakeModel(**ip) for ip in f_ips]} + + +flavor = {'id': 0, + 'name': 'fake_flavor', + 'memory_mb': 2048, + 'vcpus': 2, + 'local_gb': 10, + 'flavor_id': 0, + 'swap': 0, + 'rxtx_quota': 0, + 'rxtx_cap': 3} + + +def floating_ips(fixed_ip_id): + for i in xrange(154): + yield {'id': 0, + 'address': '10.10.10.%d' % (i + 100), + 'fixed_ip_id': fixed_ip_id, + 'project_id': None, + 'auto_assigned': False} + + +def vifs(n): + for x in xrange(n): + yield {'id': x, + 'address': 'DE:AD:BE:EF:00:%02x' % x, + 'uuid': '00000000-0000-0000-0000-00000000000000%02d' % x, + 'network_id': x, + 'network': FakeModel(**fake_network(x)), + 'instance_id': 0} + + +def ipv4_like(ip, s): + ip = ip.split('.') + s = s.split('.') + + for i, octet in enumerate(s): + if octet == '*': + continue + if octet != ip[i]: + return False + return True + + +def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): + # stubs is the self.stubs from the test + # ips_per_vif is the number of ips each vif will have + # num_floating_ips is number of float ips for each fixed ip + network = network_manager.FlatManager(host=HOST) + network.db = db + + def fixed_ips_fake(*args, **kwargs): + return list(fixed_ips(n, ips_per_vif)) + + def virtual_interfaces_fake(*args, **kwargs): + return [vif for vif in vifs(n)] + + def instance_type_fake(*args, **kwargs): + return flavor + + stubs.Set(db, 'fixed_ip_get_by_instance', fixed_ips_fake) + stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interfaces_fake) + stubs.Set(db, 'instance_type_get', instance_type_fake) + + return network.get_instance_nw_info(None, 0, 0, None) diff --git a/nova/tests/fake_network_info.py b/nova/tests/fake_network_info.py deleted file mode 100644 index 2e8cf60ec..000000000 --- a/nova/tests/fake_network_info.py +++ /dev/null @@ -1,144 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Rackspace -# 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. - -from nova import db -from nova import flags -from nova import test -from nova.network import manager as network_manager - - -HOST = "testhost" -FLAGS = flags.FLAGS - - -class FakeModel(dict): - """Represent a model from the db""" - def __init__(self, *args, **kwargs): - self.update(kwargs) - - def __getattr__(self, name): - return self[name] - - -def fake_network(n, ipv6=None): - if ipv6 == None: - ipv6 = FLAGS.use_ipv6 - rval = {'id': n, - 'label': 'test%d' % n, - 'injected': False, - 'multi_host': False, - 'cidr': '192.168.%d.0/24' % n, - 'cidr_v6': None, - 'netmask': '255.255.255.0', - 'netmask_v6': None, - 'bridge': 'fake_br%d' % n, - 'bridge_interface': 'fake_eth%d' % n, - 'gateway': '192.168.%d.1' % n, - 'gateway_v6': None, - 'broadcast': '192.168.%d.255' % n, - 'dns1': '192.168.%d.3' % n, - 'dns2': '192.168.%d.4' % n, - 'vlan': None, - 'host': None, - 'project_id': 'fake_project', - 'vpn_public_address': '192.168.%d.2' % n} - if ipv6: - rval['cidr_v6'] = '2001:db8:0:%x::/64' % n - rval['gateway_v6'] = '2001:db8:0:%x::1' % n - rval['netmask_v6'] = '64' - - return rval - - -def fixed_ips(num_networks, num_ips, num_floating_ips=0): - for network in xrange(num_networks): - for ip in xrange(num_ips): - id = network * num_ips + ip - f_ips = [floating_ips(id).next() for i in xrange(num_floating_ips)] - yield {'id': id, - 'network_id': network, - 'address': '192.168.%d.1%02d' % (network, ip), - 'instance_id': 0, - 'allocated': False, - # and since network_id and vif_id happen to be equivalent - 'virtual_interface_id': network, - 'floating_ips': [FakeModel(**ip) for ip in f_ips]} - - -flavor = {'id': 0, - 'name': 'fake_flavor', - 'memory_mb': 2048, - 'vcpus': 2, - 'local_gb': 10, - 'flavor_id': 0, - 'swap': 0, - 'rxtx_quota': 0, - 'rxtx_cap': 3} - - -def floating_ips(fixed_ip_id): - for i in xrange(154): - yield {'id': 0, - 'address': '10.10.10.%d' % (i + 100), - 'fixed_ip_id': fixed_ip_id, - 'project_id': None, - 'auto_assigned': False} - - -def vifs(n): - for x in xrange(n): - yield {'id': x, - 'address': 'DE:AD:BE:EF:00:%02x' % x, - 'uuid': '00000000-0000-0000-0000-00000000000000%02d' % x, - 'network_id': x, - 'network': FakeModel(**fake_network(x)), - 'instance_id': 0} - - -def ipv4_like(ip, s): - ip = ip.split('.') - s = s.split('.') - - for i, octet in enumerate(s): - if octet == '*': - continue - if octet != ip[i]: - return False - return True - - -def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): - # stubs is the self.stubs from the test - # ips_per_vif is the number of ips each vif will have - # num_floating_ips is number of float ips for each fixed ip - network = network_manager.FlatManager(host=HOST) - network.db = db - - def fixed_ips_fake(*args, **kwargs): - return list(fixed_ips(n, ips_per_vif)) - - def virtual_interfaces_fake(*args, **kwargs): - return [vif for vif in vifs(n)] - - def instance_type_fake(*args, **kwargs): - return flavor - - stubs.Set(db, 'fixed_ip_get_by_instance', fixed_ips_fake) - stubs.Set(db, 'virtual_interface_get_by_instance', virtual_interfaces_fake) - stubs.Set(db, 'instance_type_get', instance_type_fake) - - return network.get_instance_nw_info(None, 0, 0, None) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index fe5470a6f..190e197f5 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -36,13 +36,13 @@ from nova.api.ec2 import cloud from nova.compute import power_state from nova.virt.libvirt import connection from nova.virt.libvirt import firewall -from nova.tests import fake_network_info +from nova.tests import fake_network libvirt = None FLAGS = flags.FLAGS -_fake_network_info = fake_network_info.fake_get_instance_nw_info -_ipv4_like = fake_network_info.ipv4_like +_fake_network_info = fake_network.fake_get_instance_nw_info +_ipv4_like = fake_network.ipv4_like def _concurrency(wait, done, target): diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 803868cd9..a0079e120 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -22,7 +22,7 @@ from nova import exception from nova import log as logging from nova import test from nova.network import manager as network_manager -from nova.tests import fake_network_info +from nova.tests import fake_network LOG = logging.getLogger('nova.tests.network') @@ -132,7 +132,7 @@ class FlatNetworkTestCase(test.TestCase): is_admin=False) def test_get_instance_nw_info(self): - fake_get_instance_nw_info = fake_network_info.fake_get_instance_nw_info + fake_get_instance_nw_info = fake_network.fake_get_instance_nw_info nw_info = fake_get_instance_nw_info(self.stubs, 0, 2) self.assertFalse(nw_info) -- cgit From f0a6c35149a1b9cc278cd3ba960861da9189b5bf Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 1 Sep 2011 14:39:34 -0700 Subject: remove references to MelangeIPAMTest, as they cannot be used yet --- nova/tests/test_quantum.py | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 0fa4184b1..42acf03e7 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -249,13 +249,3 @@ class QuantumNovaIPAMTestCase(QuantumTestCaseBase, test.TestCase): with session.begin(): for fip_ref in result: session.delete(fip_ref) - -# FIXME(danwent): Cannot run this unit tests automatically for now, as -# it requires melange to be running locally. -# -#class QuantumMelangeIPAMTestCase(QuantumTestCaseBase, test.TestCase): -# -# def setUp(self): -# super(QuantumMelangeIPAMTestCase, self).setUp() -# self.net_man = quantum_manager.QuantumManager( \ -# ipam_lib="nova.network.quantum.melange_ipam_lib") -- cgit From 527670d632788d20aca7a3f12495d4c97e036d51 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 1 Sep 2011 14:40:30 -0700 Subject: melange testing cleanup, localization cleanup --- nova/network/quantum/client.py | 5 +++-- nova/network/quantum/fake.py | 4 ++-- nova/network/quantum/manager.py | 19 +++++++++---------- nova/network/quantum/melange_connection.py | 19 +++++++++++-------- nova/network/quantum/melange_ipam_lib.py | 7 ++++--- 5 files changed, 29 insertions(+), 25 deletions(-) (limited to 'nova') diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index 455bb8a79..f4936695e 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -172,8 +172,9 @@ class Client(object): c = connection_type(self.host, self.port) if self.logger: - self.logger.debug(_("Quantum Client Request:\n%s %s\n" % - (method, action))) + self.logger.debug( + _("Quantum Client Request:\n%(method)s %(action)s\n" % + locals())) if body: self.logger.debug(body) diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py index 6a4005c59..4ecddd2ae 100644 --- a/nova/network/quantum/fake.py +++ b/nova/network/quantum/fake.py @@ -78,8 +78,8 @@ class FakeQuantumClientConnection(object): def detach_and_delete_port(self, tenant_id, net_id, port_id): if not self.network_exists(tenant_id, net_id): raise exception.NotFound( - _("network %s does not exist for tenant %s" % - (net_id, tenant_id))) + _("network %(net_id)s does not exist " + "for tenant %(tenant_id)s" % locals())) del self.nets[net_id]['ports'][port_id] def get_port_by_attachment(self, tenant_id, attachment_id): diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 153f6c0f2..fa16475ac 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -91,8 +91,8 @@ class QuantumManager(manager.FlatManager): if quantum_net_id: if not self.q_conn.network_exists(q_tenant_id, quantum_net_id): raise Exception(_("Unable to find existing quantum " \ - " network for tenant '%s' with net-id '%s'" % \ - (q_tenant_id, quantum_net_id))) + " network for tenant '%(q_tenant_id)s' with " + "net-id '%(quantum_net_id)s'" % locals())) else: # otherwise, create network from default quantum pool quantum_net_id = self.q_conn.create_network(q_tenant_id, label) @@ -252,17 +252,18 @@ class QuantumManager(manager.FlatManager): 'dns': [], 'ips': [ip_dict(ip, v4_subnet) for ip in v4_ips]} - if v6_subnet['cidr']: - network_dict['cidr_v6'] = v6_subnet['cidr'] - info['ip6s'] = [ip_dict(ip, v6_subnet) for ip in v6_ips] + if v6_subnet: + if v6_subnet['cidr']: + network_dict['cidr_v6'] = v6_subnet['cidr'] + info['ip6s'] = [ip_dict(ip, v6_subnet) for ip in v6_ips] - if v6_subnet['gateway']: - info['gateway6'] = v6_subnet['gateway'] + if v6_subnet['gateway']: + info['gateway6'] = v6_subnet['gateway'] dns_dict = {} for s in [v4_subnet, v6_subnet]: for k in ['dns1', 'dns2']: - if s[k]: + if s and s[k]: dns_dict[s[k]] = None info['dns'] = [d for d in dns_dict.keys()] @@ -308,8 +309,6 @@ class QuantumManager(manager.FlatManager): except exception.InstanceNotFound: LOG.error(_("Attempted to deallocate non-existent instance: %s" % (instance_id))) - self._do_trigger_security_group_members_refresh_for_instance( - instance_id) def validate_networks(self, context, networks): """ Validates that this tenant has quantum networks with the associated diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index 4dc35d15d..5a79eff77 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -48,6 +48,7 @@ class MelangeConnection(object): self.host = host self.port = port self.use_ssl = use_ssl + self.version = "v0.1" def get(self, path, params={}, headers={}): return self.do_request("GET", path, params=params, headers=headers) @@ -66,7 +67,9 @@ class MelangeConnection(object): def do_request(self, method, path, body=None, headers={}, params={}): - url = path + '.json?' + urllib.urlencode(params) + url = "/%s/%s.json?%s" % (self.version, + path, + urllib.urlencode(params)) try: connection = self._get_connection() @@ -75,7 +78,7 @@ class MelangeConnection(object): response_str = response.read() if response.status < 400: return response_str - raise Exception(_("Server returned error: %s", response_str)) + raise Exception(_("Server returned error: %s" % response_str)) except (socket.error, IOError), e: raise Exception(_("Unable to connect to " "server. Got error: %s" % e)) @@ -86,7 +89,7 @@ class MelangeConnection(object): request_body = (json.dumps(dict(network=dict(mac_address=mac_address, tenant_id=project_id))) if mac_address else None) - url = ("/ipam%(tenant_scope)s/networks/%(network_id)s/" + url = ("ipam%(tenant_scope)s/networks/%(network_id)s/" "interfaces/%(vif_id)s/ip_allocations" % locals()) response = self.post(url, body=request_body, headers=json_content_type) @@ -96,7 +99,7 @@ class MelangeConnection(object): project_id=None, dns1=None, dns2=None): tenant_scope = "/tenants/%s" % project_id if project_id else "" - url = "/ipam%(tenant_scope)s/ip_blocks" % locals() + url = "ipam%(tenant_scope)s/ip_blocks" % locals() req_params = dict(ip_block=dict(cidr=cidr, network_id=network_id, type='private', dns1=dns1, dns2=dns2)) @@ -106,14 +109,14 @@ class MelangeConnection(object): def delete_block(self, block_id, project_id=None): tenant_scope = "/tenants/%s" % project_id if project_id else "" - url = "/ipam%(tenant_scope)s/ip_blocks/%(block_id)s" % locals() + url = "ipam%(tenant_scope)s/ip_blocks/%(block_id)s" % locals() self.delete(url, headers=json_content_type) def get_blocks(self, project_id=None): tenant_scope = "/tenants/%s" % project_id if project_id else "" - url = "/ipam%(tenant_scope)s/ip_blocks" % locals() + url = "ipam%(tenant_scope)s/ip_blocks" % locals() response = self.get(url, headers=json_content_type) return json.loads(response) @@ -121,7 +124,7 @@ class MelangeConnection(object): def get_allocated_ips(self, network_id, vif_id, project_id=None): tenant_scope = "/tenants/%s" % project_id if project_id else "" - url = ("/ipam%(tenant_scope)s/networks/%(network_id)s/" + url = ("ipam%(tenant_scope)s/networks/%(network_id)s/" "interfaces/%(vif_id)s/ip_allocations" % locals()) response = self.get(url, headers=json_content_type) @@ -130,7 +133,7 @@ class MelangeConnection(object): def deallocate_ips(self, network_id, vif_id, project_id=None): tenant_scope = "/tenants/%s" % project_id if project_id else "" - url = ("/ipam%(tenant_scope)s/networks/%(network_id)s/" + url = ("ipam%(tenant_scope)s/networks/%(network_id)s/" "interfaces/%(vif_id)s/ip_allocations" % locals()) self.delete(url, headers=json_content_type) diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index 71c0d7ce6..dcee0a3f9 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -17,6 +17,7 @@ from netaddr import IPNetwork +from nova import db from nova import exception from nova import flags from nova import log as logging @@ -68,7 +69,8 @@ class QuantumMelangeIPAMLib(object): "project_id": project_id, "priority": priority, "label": label} - network = self.db.network_create_safe(context, net) + admin_context = context.elevated() + network = db.network_create_safe(admin_context, net) def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref): """ Pass call to allocate fixed IP on to Melange""" @@ -100,8 +102,7 @@ class QuantumMelangeIPAMLib(object): self.m_conn.delete_block(b['id'], tenant_id) network = db.network_get_by_uuid(admin_context, net_id) - if network is not None: - db.network_delete_safe(context, network['id']) + db.network_delete_safe(context, network['id']) def get_project_and_global_net_ids(self, context, project_id): """ Fetches all networks associated with this project, or -- cgit From 38373bf8f60dd068dec69933d1456a8deb75bf8e Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 1 Sep 2011 15:02:09 -0700 Subject: move content of quantum/fake.py to test_quantum.py in unit testing class (most original content has been removed anyway) --- nova/network/quantum/fake.py | 92 ----------------------------------------- nova/network/quantum/manager.py | 9 ++-- nova/tests/test_quantum.py | 76 +++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 99 deletions(-) delete mode 100644 nova/network/quantum/fake.py (limited to 'nova') diff --git a/nova/network/quantum/fake.py b/nova/network/quantum/fake.py deleted file mode 100644 index 4ecddd2ae..000000000 --- a/nova/network/quantum/fake.py +++ /dev/null @@ -1,92 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Nicira Networks, Inc -# 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. - -from nova import exception -from nova import ipv6 -from nova import log as logging -from nova import utils - - -LOG = logging.getLogger("network.quantum.fake") - - -# this class can be used for unit functional/testing on nova, -# as it does not actually make remote calls to the Quantum service -class FakeQuantumClientConnection(object): - - def __init__(self): - self.nets = {} - - def get_networks_for_tenant(self, tenant_id): - net_ids = [] - for net_id, n in self.nets.items(): - if n['tenant-id'] == tenant_id: - net_ids.append(net_id) - return net_ids - - def create_network(self, tenant_id, network_name): - - uuid = str(utils.gen_uuid()) - self.nets[uuid] = {'net-name': network_name, - 'tenant-id': tenant_id, - 'ports': {}} - return uuid - - def delete_network(self, tenant_id, net_id): - if self.nets[net_id]['tenant-id'] == tenant_id: - del self.nets[net_id] - - def network_exists(self, tenant_id, net_id): - try: - return self.nets[net_id]['tenant-id'] == tenant_id - except KeyError: - return False - - def _confirm_not_attached(self, interface_id): - for n in self.nets.values(): - for p in n['ports'].values(): - if p['attachment-id'] == interface_id: - raise Exception(_("interface '%s' is already attached" % - interface_id)) - - def create_and_attach_port(self, tenant_id, net_id, interface_id): - if not self.network_exists(tenant_id, net_id): - raise Exception( - _("network %(net_id)s does not exist for tenant %(tenant_id)" - % locals())) - - self._confirm_not_attached(interface_id) - uuid = str(utils.gen_uuid()) - self.nets[net_id]['ports'][uuid] = \ - {"port-state": "ACTIVE", - "attachment-id": interface_id} - - def detach_and_delete_port(self, tenant_id, net_id, port_id): - if not self.network_exists(tenant_id, net_id): - raise exception.NotFound( - _("network %(net_id)s does not exist " - "for tenant %(tenant_id)s" % locals())) - del self.nets[net_id]['ports'][port_id] - - def get_port_by_attachment(self, tenant_id, attachment_id): - for net_id, n in self.nets.items(): - if n['tenant-id'] == tenant_id: - for port_id, p in n['ports'].items(): - if p['attachment-id'] == attachment_id: - return (net_id, port_id) - - return (None, None) diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index fa16475ac..db2b000de 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -50,17 +50,16 @@ class QuantumManager(manager.FlatManager): Support for these capabilities are targted for future releases. """ - def __init__(self, ipam_lib=None, *args, **kwargs): + def __init__(self, q_conn=None, ipam_lib=None, *args, **kwargs): """ Initialize two key libraries, the connection to a Quantum service, and the library for implementing IPAM. Calls inherited FlatManager constructor. """ - if FLAGS.fake_network: - self.q_conn = fake.FakeQuantumClientConnection() - else: - self.q_conn = quantum_connection.QuantumClientConnection() + if not q_conn: + q_conn = quantum_connection.QuantumClientConnection() + self.q_conn = q_conn if not ipam_lib: ipam_lib = FLAGS.quantum_ipam_lib diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 42acf03e7..0b1a1f204 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -20,12 +20,82 @@ from nova import db from nova.db.sqlalchemy import models from nova.db.sqlalchemy.session import get_session from nova import exception +from nova import ipv6 from nova import log as logging from nova.network.quantum import manager as quantum_manager from nova import test +from nova import utils LOG = logging.getLogger('nova.tests.quantum_network') + +# this class can be used for unit functional/testing on nova, +# as it does not actually make remote calls to the Quantum service +class FakeQuantumClientConnection(object): + + def __init__(self): + self.nets = {} + + def get_networks_for_tenant(self, tenant_id): + net_ids = [] + for net_id, n in self.nets.items(): + if n['tenant-id'] == tenant_id: + net_ids.append(net_id) + return net_ids + + def create_network(self, tenant_id, network_name): + + uuid = str(utils.gen_uuid()) + self.nets[uuid] = {'net-name': network_name, + 'tenant-id': tenant_id, + 'ports': {}} + return uuid + + def delete_network(self, tenant_id, net_id): + if self.nets[net_id]['tenant-id'] == tenant_id: + del self.nets[net_id] + + def network_exists(self, tenant_id, net_id): + try: + return self.nets[net_id]['tenant-id'] == tenant_id + except KeyError: + return False + + def _confirm_not_attached(self, interface_id): + for n in self.nets.values(): + for p in n['ports'].values(): + if p['attachment-id'] == interface_id: + raise Exception(_("interface '%s' is already attached" % + interface_id)) + + def create_and_attach_port(self, tenant_id, net_id, interface_id): + if not self.network_exists(tenant_id, net_id): + raise Exception( + _("network %(net_id)s does not exist for tenant %(tenant_id)" + % locals())) + + self._confirm_not_attached(interface_id) + uuid = str(utils.gen_uuid()) + self.nets[net_id]['ports'][uuid] = \ + {"port-state": "ACTIVE", + "attachment-id": interface_id} + + def detach_and_delete_port(self, tenant_id, net_id, port_id): + if not self.network_exists(tenant_id, net_id): + raise exception.NotFound( + _("network %(net_id)s does not exist " + "for tenant %(tenant_id)s" % locals())) + del self.nets[net_id]['ports'][port_id] + + def get_port_by_attachment(self, tenant_id, attachment_id): + for net_id, n in self.nets.items(): + if n['tenant-id'] == tenant_id: + for port_id, p in n['ports'].items(): + if p['attachment-id'] == attachment_id: + return (net_id, port_id) + + return (None, None) + networks = [{'label': 'project1-net1', 'injected': False, 'multi_host': False, @@ -230,8 +300,10 @@ class QuantumNovaIPAMTestCase(QuantumTestCaseBase, test.TestCase): def setUp(self): super(QuantumNovaIPAMTestCase, self).setUp() - self.net_man = quantum_manager.QuantumManager( \ - ipam_lib="nova.network.quantum.nova_ipam_lib") + + self.net_man = quantum_manager.QuantumManager( + ipam_lib="nova.network.quantum.nova_ipam_lib", + q_conn=FakeQuantumClientConnection()) # Tests seem to create some networks by default, which # we don't want. So we delete them. -- cgit From 1081b9d52026afb84128c15a1df0998f80810ce9 Mon Sep 17 00:00:00 2001 From: Thuleau Édouard Date: Fri, 2 Sep 2011 15:48:32 +0200 Subject: Correct tests associated. --- nova/tests/api/openstack/test_servers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 3559e6de5..e5ebedf0e 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3490,10 +3490,14 @@ class TestGetKernelRamdiskFromImage(test.TestCase): self.assertRaises(exception.NotFound, self._get_k_r, image_meta) def test_ami_no_ramdisk(self): - """If an ami is missing a ramdisk it should raise NotFound""" + """If an ami is missing a ramdisk, return kernel ID and None for + ramdisk ID + """ image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami', 'properties': {'kernel_id': 1}} - self.assertRaises(exception.NotFound, self._get_k_r, image_meta) + kernel_id, ramdisk_id = self._get_k_r(image_meta) + self.assertEqual(kernel_id, 1) + self.assertEqual(ramdisk_id, None) def test_ami_kernel_ramdisk_present(self): """Return IDs if both kernel and ramdisk are present""" -- cgit From 5fe5c5dc26276a10b7dc766104a7e2d6c7793dc3 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 2 Sep 2011 11:51:55 -0700 Subject: remove import of 'fake' from nova manager, now that we've moved that to test_quantum.py --- nova/network/quantum/manager.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index db2b000de..c10dc90de 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -22,7 +22,6 @@ from nova import log as logging from nova import manager from nova.network import manager from nova.network.quantum import quantum_connection -from nova.network.quantum import fake from nova import utils LOG = logging.getLogger("quantum_manager") -- cgit From bd1bc5e3c6f52963ce088e2e0a6da41f125d29f1 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 2 Sep 2011 12:11:28 -0700 Subject: more review cleanup --- .../migrate_repo/versions/044_add_network_priority.py | 4 +--- nova/network/quantum/melange_connection.py | 17 +++++++++-------- nova/tests/test_quantum.py | 4 ++-- 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py index e69380199..e3ee6a85f 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py @@ -25,13 +25,11 @@ meta = MetaData() # Add priority column to networks table priority = Column('priority', Integer()) +networks = Table('networks', meta, autoload=True) def upgrade(migrate_engine): meta.bind = migrate_engine - # grab tables and (column for dropping later) - networks = Table('networks', meta, autoload=True) - try: networks.create_column(priority) except Exception: diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index 5a79eff77..d5a1901aa 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -50,13 +50,13 @@ class MelangeConnection(object): self.use_ssl = use_ssl self.version = "v0.1" - def get(self, path, params={}, headers={}): + def get(self, path, params=None, headers=None): return self.do_request("GET", path, params=params, headers=headers) - def post(self, path, body=None, headers={}): + def post(self, path, body=None, headers=None): return self.do_request("POST", path, body=body, headers=headers) - def delete(self, path, headers={}): + def delete(self, path, headers=None): return self.do_request("DELETE", path, headers=headers) def _get_connection(self): @@ -65,12 +65,13 @@ class MelangeConnection(object): else: return httplib.HTTPConnection(self.host, self.port) - def do_request(self, method, path, body=None, headers={}, params={}): - - url = "/%s/%s.json?%s" % (self.version, - path, - urllib.urlencode(params)) + def do_request(self, method, path, body=None, headers=None, params=None): + headers = headers or {} + params = params or {} + url = "/%s/%s.json" % (self.version, path) + if params: + url += "?%s" % urllib.urlencode(params) try: connection = self._get_connection() connection.request(method, url, body, headers) diff --git a/nova/tests/test_quantum.py b/nova/tests/test_quantum.py index 0b1a1f204..0feec9b99 100644 --- a/nova/tests/test_quantum.py +++ b/nova/tests/test_quantum.py @@ -277,10 +277,10 @@ class QuantumTestCaseBase(object): nw_info[1][0]['cidr_v6'].startswith("2001:1db9:")) # v6 address - self.assertTrue(\ + self.assertTrue( nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1dbb:") or nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1dbb:")) - self.assertTrue(\ + self.assertTrue( nw_info[0][1]['ip6s'][0]['ip'].startswith("2001:1db9:") or nw_info[1][1]['ip6s'][0]['ip'].startswith("2001:1db9:")) -- cgit From 435016f27ea36a6780897efe1289328c51e1463f Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 2 Sep 2011 12:31:14 -0700 Subject: move networks declarations within upgrade/downgrade methods --- nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py index e3ee6a85f..9db950c9b 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py @@ -25,11 +25,11 @@ meta = MetaData() # Add priority column to networks table priority = Column('priority', Integer()) -networks = Table('networks', meta, autoload=True) def upgrade(migrate_engine): meta.bind = migrate_engine + networks = Table('networks', meta, autoload=True) try: networks.create_column(priority) except Exception: @@ -39,4 +39,6 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): meta.bind = migrate_engine + + networks = Table('networks', meta, autoload=True) networks.drop_column(priority) -- cgit From 9773d900d35316edbad4468a869ca62a353d3114 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Fri, 2 Sep 2011 12:34:14 -0700 Subject: Fix for LP Bug #839269 --- nova/db/api.py | 6 ++++-- nova/db/sqlalchemy/api.py | 5 +++-- nova/network/manager.py | 3 ++- nova/tests/test_network.py | 3 ++- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index 148887635..efc088e35 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -324,13 +324,15 @@ def migration_get_by_instance_and_status(context, instance_uuid, status): #################### -def fixed_ip_associate(context, address, instance_id, network_id=None): +def fixed_ip_associate(context, address, instance_id, network_id=None, + reserved=False): """Associate fixed ip to instance. Raises if fixed ip is not available. """ - return IMPL.fixed_ip_associate(context, address, instance_id, network_id) + return IMPL.fixed_ip_associate(context, address, instance_id, network_id, + reserved) def fixed_ip_associate_pool(context, network_id, instance_id=None, host=None): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b99667afc..e0be8454e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -669,14 +669,15 @@ def floating_ip_update(context, address, values): @require_admin_context -def fixed_ip_associate(context, address, instance_id, network_id=None): +def fixed_ip_associate(context, address, instance_id, network_id=None, + reserved=False): session = get_session() with session.begin(): network_or_none = or_(models.FixedIp.network_id == network_id, models.FixedIp.network_id == None) fixed_ip_ref = session.query(models.FixedIp).\ filter(network_or_none).\ - filter_by(reserved=False).\ + filter_by(reserved=reserved).\ filter_by(deleted=False).\ filter_by(address=address).\ with_lockmode('update').\ diff --git a/nova/network/manager.py b/nova/network/manager.py index e6b30d1a0..90f414ee2 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -991,7 +991,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): address = network['vpn_private_address'] self.db.fixed_ip_associate(context, address, - instance_id) + instance_id, + reserved=True) else: address = kwargs.get('address', None) if address: diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 25ff940f0..2347544de 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -264,7 +264,8 @@ class VlanNetworkTestCase(test.TestCase): db.fixed_ip_associate(mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn('192.168.0.1') + mox.IgnoreArg(), + reserved=True).AndReturn('192.168.0.1') db.fixed_ip_update(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()) -- cgit From bcb6f7d570ed24e0bf083cd4f4c8be0f20e69918 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 2 Sep 2011 12:40:55 -0700 Subject: change db migrate script again to match other similar scripts --- .../db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py index 9db950c9b..b9b0ea37c 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/044_add_network_priority.py @@ -22,6 +22,9 @@ from nova import utils meta = MetaData() +networks = Table('networks', meta, + Column("id", Integer(), primary_key=True, nullable=False)) + # Add priority column to networks table priority = Column('priority', Integer()) @@ -29,7 +32,6 @@ priority = Column('priority', Integer()) def upgrade(migrate_engine): meta.bind = migrate_engine - networks = Table('networks', meta, autoload=True) try: networks.create_column(priority) except Exception: @@ -39,6 +41,4 @@ def upgrade(migrate_engine): def downgrade(migrate_engine): meta.bind = migrate_engine - - networks = Table('networks', meta, autoload=True) networks.drop_column(priority) -- cgit From cc3bd1da5edc368871d2c8de0e498ab2649ae0dd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 2 Sep 2011 12:52:02 -0700 Subject: revert description changes, use metadata['description'] if it is set to populate field in db --- nova/api/openstack/create_instance_helper.py | 9 ++++++--- nova/api/openstack/schemas/v1.1/server.rng | 1 - nova/api/openstack/servers.py | 6 +----- nova/api/openstack/views/servers.py | 1 - nova/tests/api/openstack/test_servers.py | 28 ---------------------------- 5 files changed, 7 insertions(+), 38 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index ff3be4a01..289f87921 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -161,6 +161,10 @@ class CreateInstanceHelper(object): 'config_drive': config_drive, 'password': password} + # NOTE(vish): This is solely for compatibility with anything + # that is using the display description field. + metadata = server_dict.get('metadata') or {} + display_description = metadata.get('description') or '' return (extra_values, create_method(context, inst_type, @@ -168,10 +172,9 @@ class CreateInstanceHelper(object): kernel_id=kernel_id, ramdisk_id=ramdisk_id, display_name=name, - display_description=server_dict.\ - get('description', ''), + display_description=display_description, key_name=key_name, - metadata=server_dict.get('metadata', {}), + metadata=metadata, access_ip_v4=server_dict.get('accessIPv4'), access_ip_v6=server_dict.get('accessIPv6'), injected_files=injected_files, diff --git a/nova/api/openstack/schemas/v1.1/server.rng b/nova/api/openstack/schemas/v1.1/server.rng index 203728f48..ef835e408 100644 --- a/nova/api/openstack/schemas/v1.1/server.rng +++ b/nova/api/openstack/schemas/v1.1/server.rng @@ -3,7 +3,6 @@ - diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 7f5463e70..46a111cf5 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -185,9 +185,6 @@ class Controller(object): self.helper._validate_server_name(name) update_dict['display_name'] = name.strip() - if 'description' in body['server']: - description = body['server']['description'] - update_dict['display_description'] = description.strip() if 'accessIPv4' in body['server']: access_ipv4 = body['server']['accessIPv4'] update_dict['access_ip_v4'] = access_ipv4.strip() @@ -881,7 +878,6 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): node.setAttribute('uuid', str(server['uuid'])) node.setAttribute('hostId', str(server['hostId'])) node.setAttribute('name', server['name']) - node.setAttribute('description', server['description']) node.setAttribute('created', str(server['created'])) node.setAttribute('updated', str(server['updated'])) node.setAttribute('status', server['status']) @@ -997,7 +993,7 @@ def create_resource(version='1.0'): "attributes": { "server": ["id", "imageId", "name", "flavorId", "hostId", "status", "progress", "adminPass", "flavorRef", - "imageRef", "userId", "tenantId", "description"], + "imageRef", "userId", "tenantId"], "link": ["rel", "type", "href"], }, "dict_collections": { diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index 98ccd817a..ac09b5864 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -68,7 +68,6 @@ class ViewBuilder(object): 'name': inst['display_name'], 'user_id': inst.get('user_id', ''), 'tenant_id': inst.get('project_id', ''), - 'description': inst.get('display_description', ''), 'status': common.status_from_state(vm_state, task_state)} # Return the metadata as a dictionary diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index d5de1aa3c..45ad6e5a8 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -2268,34 +2268,6 @@ class ServersTest(test.TestCase): self.assertEqual(res_dict['server']['id'], 1) self.assertEqual(res_dict['server']['name'], 'server_test') - def test_update_server_description_v1_1(self): - DESC = 'updated_desc' - - def server_update(context, id, params): - # assert that parameter conversion from description - # to display_description worked correctly - self.assertEqual(params.get('display_description'), DESC) - return stub_instance(1, - name='server_test', - description=params['display_description']) - - self.stubs.Set(nova.db.api, 'instance_get', - return_server_with_attributes(name='server_test', - description=DESC)) - - self.stubs.Set(nova.db.api, 'instance_update', - server_update) - - req = webob.Request.blank('/v1.1/fake/servers/1') - req.method = 'PUT' - req.content_type = 'application/json' - req.body = json.dumps({'server': {'description': DESC}}) - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 200) - res_dict = json.loads(res.body) - self.assertEqual(res_dict['server']['id'], 1) - self.assertEqual(res_dict['server']['description'], DESC) - def test_update_server_access_ipv4_v1_1(self): self.stubs.Set(nova.db.api, 'instance_get', return_server_with_attributes(access_ipv4='0.0.0.0')) -- cgit From 752b6c9e26b718ab86f04c25a8c7f977bbea4a22 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 2 Sep 2011 13:05:24 -0700 Subject: feedback from jk0's review, including removing a lot of spaces from docstrings --- nova/db/sqlalchemy/api.py | 2 +- nova/network/manager.py | 2 +- nova/network/quantum/client.py | 45 +++++++++--------- nova/network/quantum/manager.py | 76 +++++++++++++++--------------- nova/network/quantum/melange_connection.py | 2 +- nova/network/quantum/melange_ipam_lib.py | 63 +++++++++++++------------ nova/network/quantum/nova_ipam_lib.py | 56 +++++++++++----------- nova/network/quantum/quantum_connection.py | 34 ++++++------- 8 files changed, 140 insertions(+), 140 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 09356e966..e0da2269d 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -946,7 +946,7 @@ def virtual_interface_get_by_address(context, address): def virtual_interface_get_by_uuid(context, vif_uuid): """Gets a virtual interface from the table. - :param vif_uuid: = the uuid of the interface you're looking to get + :param vif_uuid: the uuid of the interface you're looking to get """ session = get_session() vif_ref = session.query(models.VirtualInterface).\ diff --git a/nova/network/manager.py b/nova/network/manager.py index 426ff2f33..6730e808f 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -547,7 +547,7 @@ class NetworkManager(manager.SchedulerDependentManager): 'network_id': network_id, 'uuid': str(utils.gen_uuid())} # try FLAG times to create a vif record with a unique mac_address - for i in xrange(FLAGS.create_unique_mac_address_attempts): + for _ in xrange(FLAGS.create_unique_mac_address_attempts): try: return self.db.virtual_interface_create(context, vif) except exception.VirtualInterfaceCreateException: diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py index f4936695e..40c68dfdc 100644 --- a/nova/network/quantum/client.py +++ b/nova/network/quantum/client.py @@ -22,14 +22,14 @@ import socket import urllib -#FIXME(danwent): All content in this file should be removed once the +# FIXME(danwent): All content in this file should be removed once the # packaging work for the quantum client libraries is complete. # At that point, we will be able to just install the libraries as a # dependency and import from quantum.client.* and quantum.common.* # Until then, we have simplified versions of these classes in this file. class JSONSerializer(object): - """ This is a simple json-only serializer to use until we can just grab + """This is a simple json-only serializer to use until we can just grab the standard serializer from the quantum library. """ def serialize(self, data, content_type): @@ -47,17 +47,17 @@ class JSONSerializer(object): # granular exceptions, for now, just try to distinguish # between the cases we care about. class QuantumNotFoundException(Exception): - """ Indicates that Quantum Server returned 404""" + """Indicates that Quantum Server returned 404""" pass class QuantumServerException(Exception): - """ Indicates any non-404 error from Quantum Server""" + """Indicates any non-404 error from Quantum Server""" pass class QuantumIOException(Exception): - """ Indicates network IO trouble reaching Quantum Server""" + """Indicates network IO trouble reaching Quantum Server""" pass @@ -100,7 +100,7 @@ class Client(object): def __init__(self, host="127.0.0.1", port=9696, use_ssl=False, tenant=None, format="xml", testing_stub=None, key_file=None, cert_file=None, logger=None): - """ Creates a new client to some service. + """Creates a new client to some service. :param host: The host where service resides :param port: The port where service resides @@ -123,7 +123,7 @@ class Client(object): self.logger = logger def get_connection_type(self): - """ Returns the proper connection type """ + """Returns the proper connection type""" if self.testing_stub: return self.testing_stub elif self.use_ssl: @@ -133,7 +133,7 @@ class Client(object): def do_request(self, method, action, body=None, headers=None, params=None): - """ Connects to the server and issues a request. + """Connects to the server and issues a request. Returns the result data, or raises an appropriate exception if HTTP status code is not 2xx @@ -142,7 +142,6 @@ class Client(object): :param headers: mapping of key/value pairs to add as headers :param params: dictionary of key/value pairs to add to append to action - """ # Ensure we have a tenant id @@ -207,7 +206,7 @@ class Client(object): "server. Got error: %s" % e)) def get_status_code(self, response): - """ Returns the integer status code from the response, which + """Returns the integer status code from the response, which can be either a Webob.Response (used in testing) or httplib.Response """ if hasattr(response, 'status_int'): @@ -236,73 +235,73 @@ class Client(object): @api_call def list_networks(self): - """ Fetches a list of all networks for a tenant """ + """Fetches a list of all networks for a tenant""" return self.do_request("GET", self.networks_path) @api_call def show_network_details(self, network): - """ Fetches the details of a certain network """ + """Fetches the details of a certain network""" return self.do_request("GET", self.network_path % (network)) @api_call def create_network(self, body=None): - """ Creates a new network """ + """Creates a new network""" body = self.serialize(body) return self.do_request("POST", self.networks_path, body=body) @api_call def update_network(self, network, body=None): - """ Updates a network """ + """Updates a network""" body = self.serialize(body) return self.do_request("PUT", self.network_path % (network), body=body) @api_call def delete_network(self, network): - """ Deletes the specified network """ + """Deletes the specified network""" return self.do_request("DELETE", self.network_path % (network)) @api_call def list_ports(self, network): - """ Fetches a list of ports on a given network """ + """Fetches a list of ports on a given network""" return self.do_request("GET", self.ports_path % (network)) @api_call def show_port_details(self, network, port): - """ Fetches the details of a certain port """ + """Fetches the details of a certain port""" return self.do_request("GET", self.port_path % (network, port)) @api_call def create_port(self, network, body=None): - """ Creates a new port on a given network """ + """Creates a new port on a given network""" body = self.serialize(body) return self.do_request("POST", self.ports_path % (network), body=body) @api_call def delete_port(self, network, port): - """ Deletes the specified port from a network """ + """Deletes the specified port from a network""" return self.do_request("DELETE", self.port_path % (network, port)) @api_call def set_port_state(self, network, port, body=None): - """ Sets the state of the specified port """ + """Sets the state of the specified port""" body = self.serialize(body) return self.do_request("PUT", self.port_path % (network, port), body=body) @api_call def show_port_attachment(self, network, port): - """ Fetches the attachment-id associated with the specified port """ + """Fetches the attachment-id associated with the specified port""" return self.do_request("GET", self.attachment_path % (network, port)) @api_call def attach_resource(self, network, port, body=None): - """ Sets the attachment-id of the specified port """ + """Sets the attachment-id of the specified port""" body = self.serialize(body) return self.do_request("PUT", self.attachment_path % (network, port), body=body) @api_call def detach_resource(self, network, port): - """ Removes the attachment-id of the specified port """ + """Removes the attachment-id of the specified port""" return self.do_request("DELETE", self.attachment_path % (network, port)) diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index c10dc90de..23a9aba0d 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -24,7 +24,7 @@ from nova.network import manager from nova.network.quantum import quantum_connection from nova import utils -LOG = logging.getLogger("quantum_manager") +LOG = logging.getLogger("nova.network.quantum.manager") FLAGS = flags.FLAGS @@ -34,26 +34,26 @@ flags.DEFINE_string('quantum_ipam_lib', class QuantumManager(manager.FlatManager): - """ NetworkManager class that communicates with a Quantum service - via a web services API to provision VM network connectivity. + """NetworkManager class that communicates with a Quantum service + via a web services API to provision VM network connectivity. - For IP Address management, QuantumManager can be configured to - use either Nova's local DB or the Melange IPAM service. + For IP Address management, QuantumManager can be configured to + use either Nova's local DB or the Melange IPAM service. - Currently, the QuantumManager does NOT support any of the 'gateway' - functionality implemented by the Nova VlanManager, including: + Currently, the QuantumManager does NOT support any of the 'gateway' + functionality implemented by the Nova VlanManager, including: * floating IPs * DHCP * NAT gateway - Support for these capabilities are targted for future releases. + Support for these capabilities are targted for future releases. """ def __init__(self, q_conn=None, ipam_lib=None, *args, **kwargs): - """ Initialize two key libraries, the connection to a - Quantum service, and the library for implementing IPAM. + """Initialize two key libraries, the connection to a + Quantum service, and the library for implementing IPAM. - Calls inherited FlatManager constructor. + Calls inherited FlatManager constructor. """ if not q_conn: @@ -70,16 +70,16 @@ class QuantumManager(manager.FlatManager): network_size, cidr_v6, gateway_v6, bridge, bridge_interface, dns1=None, dns2=None, uuid=None, **kwargs): - """ Unlike other NetworkManagers, with QuantumManager, each - create_networks calls should create only a single network. + """Unlike other NetworkManagers, with QuantumManager, each + create_networks calls should create only a single network. - Two scenarios exist: + Two scenarios exist: - no 'uuid' is specified, in which case we contact Quantum and create a new network. - an existing 'uuid' is specified, corresponding to a Quantum network created out of band. - In both cases, we initialize a subnet using the IPAM lib. + In both cases, we initialize a subnet using the IPAM lib. """ if num_networks != 1: raise Exception(_("QuantumManager requires that only one" @@ -101,8 +101,8 @@ class QuantumManager(manager.FlatManager): priority, cidr, gateway_v6, cidr_v6, dns1, dns2) def delete_network(self, context, fixed_range): - """ Lookup network by IPv4 cidr, delete both the IPAM - subnet and the corresponding Quantum network. + """Lookup network by IPv4 cidr, delete both the IPAM + subnet and the corresponding Quantum network. """ project_id = context.project_id quantum_net_id = self.ipam.get_network_id_by_cidr( @@ -113,14 +113,14 @@ class QuantumManager(manager.FlatManager): self.q_conn.delete_network(q_tenant_id, quantum_net_id) def allocate_for_instance(self, context, **kwargs): - """ Called by compute when it is creating a new VM. + """Called by compute when it is creating a new VM. - There are three key tasks: + There are three key tasks: - Determine the number and order of vNICs to create - Allocate IP addresses - Create ports on a Quantum network and attach vNICs. - We support two approaches to determining vNICs: + We support two approaches to determining vNICs: - By default, a VM gets a vNIC for any network belonging to the VM's project, and a vNIC for any "global" network that has a NULL project_id. vNIC order is determined @@ -130,10 +130,10 @@ class QuantumManager(manager.FlatManager): create vNICs, and the vNIC order is determiend by the order in the requested_networks array. - For each vNIC, use the FlatManager to create the entries - in the virtual_interfaces table, contact Quantum to - create a port and attachment the vNIC, and use the IPAM - lib to allocate IP addresses. + For each vNIC, use the FlatManager to create the entries + in the virtual_interfaces table, contact Quantum to + create a port and attachment the vNIC, and use the IPAM + lib to allocate IP addresses. """ instance_id = kwargs.pop('instance_id') instance_type_id = kwargs['instance_type_id'] @@ -181,17 +181,17 @@ class QuantumManager(manager.FlatManager): def get_instance_nw_info(self, context, instance_id, instance_type_id, host): - """ This method is used by compute to fetch all network data - that should be used when creating the VM. + """This method is used by compute to fetch all network data + that should be used when creating the VM. - The method simply loops through all virtual interfaces - stored in the nova DB and queries the IPAM lib to get - the associated IP data. + The method simply loops through all virtual interfaces + stored in the nova DB and queries the IPAM lib to get + the associated IP data. - The format of returned data is 'defined' by the initial - set of NetworkManagers found in nova/network/manager.py . - Ideally this 'interface' will be more formally defined - in the future. + The format of returned data is 'defined' by the initial + set of NetworkManagers found in nova/network/manager.py . + Ideally this 'interface' will be more formally defined + in the future. """ network_info = [] instance = db.instance_get(context, instance_id) @@ -269,10 +269,10 @@ class QuantumManager(manager.FlatManager): return network_info def deallocate_for_instance(self, context, **kwargs): - """ Called when a VM is terminated. Loop through each virtual - interface in the Nova DB and remove the Quantum port and - clear the IP allocation using the IPAM. Finally, remove - the virtual interfaces from the Nova DB. + """Called when a VM is terminated. Loop through each virtual + interface in the Nova DB and remove the Quantum port and + clear the IP allocation using the IPAM. Finally, remove + the virtual interfaces from the Nova DB. """ instance_id = kwargs.get('instance_id') project_id = kwargs.pop('project_id', None) @@ -309,7 +309,7 @@ class QuantumManager(manager.FlatManager): (instance_id))) def validate_networks(self, context, networks): - """ Validates that this tenant has quantum networks with the associated + """Validates that this tenant has quantum networks with the associated UUIDs. This is called by the 'os-create-server-ext' API extension code so that we can return an API error code to the caller if they request an invalid network. diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index d5a1901aa..0c744f080 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -35,7 +35,7 @@ flags.DEFINE_string('melange_port', json_content_type = {'Content-type': "application/json"} -#FIXME(danwent): talk to the Melange folks about creating a +# FIXME(danwent): talk to the Melange folks about creating a # client lib that we can import as a library, instead of # have to have all of the client code in here. class MelangeConnection(object): diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index dcee0a3f9..a0ac10fd3 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -24,7 +24,7 @@ from nova import log as logging from nova.network.quantum import melange_connection -LOG = logging.getLogger("quantum_melange_ipam") +LOG = logging.getLogger("nova.network.quantum.melange_ipam_lib") FLAGS = flags.FLAGS @@ -34,26 +34,26 @@ def get_ipam_lib(net_man): class QuantumMelangeIPAMLib(object): - """ Implements Quantum IP Address Management (IPAM) interface - using the Melange service, which is access using the Melange - web services API. + """Implements Quantum IP Address Management (IPAM) interface + using the Melange service, which is access using the Melange + web services API. """ def __init__(self): - """ Initialize class used to connect to Melange server""" + """Initialize class used to connect to Melange server""" self.m_conn = melange_connection.MelangeConnection() def create_subnet(self, context, label, project_id, quantum_net_id, priority, cidr=None, gateway_v6=None, cidr_v6=None, dns1=None, dns2=None): - """ Contact Melange and create a subnet for any non-NULL - IPv4 or IPv6 subnets. + """Contact Melange and create a subnet for any non-NULL + IPv4 or IPv6 subnets. - Also create a entry in the Nova networks DB, but only - to store values not represented in Melange or to - temporarily provide compatibility with Nova code that - accesses IPAM data directly via the DB (e.g., nova-api) + Also create a entry in the Nova networks DB, but only + to store values not represented in Melange or to + temporarily provide compatibility with Nova code that + accesses IPAM data directly via the DB (e.g., nova-api) """ tenant_id = project_id or FLAGS.quantum_default_tenant_id if cidr: @@ -73,15 +73,15 @@ class QuantumMelangeIPAMLib(object): network = db.network_create_safe(admin_context, net) def allocate_fixed_ip(self, context, project_id, quantum_net_id, vif_ref): - """ Pass call to allocate fixed IP on to Melange""" + """Pass call to allocate fixed IP on to Melange""" tenant_id = project_id or FLAGS.quantum_default_tenant_id self.m_conn.allocate_ip(quantum_net_id, vif_ref['uuid'], project_id=tenant_id, mac_address=vif_ref['address']) def get_network_id_by_cidr(self, context, cidr, project_id): - """ Find the Quantum UUID associated with a IPv4 CIDR - address for the specified tenant. + """Find the Quantum UUID associated with a IPv4 CIDR + address for the specified tenant. """ tenant_id = project_id or FLAGS.quantum_default_tenant_id all_blocks = self.m_conn.get_blocks(tenant_id) @@ -91,8 +91,8 @@ class QuantumMelangeIPAMLib(object): raise exception.NotFound(_("No network found for cidr %s" % cidr)) def delete_subnets_by_net_id(self, context, net_id, project_id): - """ Find Melange block associated with the Quantum UUID, - then tell Melange to delete that block. + """Find Melange block associated with the Quantum UUID, + then tell Melange to delete that block. """ admin_context = context.elevated() tenant_id = project_id or FLAGS.quantum_default_tenant_id @@ -105,9 +105,10 @@ class QuantumMelangeIPAMLib(object): db.network_delete_safe(context, network['id']) def get_project_and_global_net_ids(self, context, project_id): - """ Fetches all networks associated with this project, or - that are "global" (i.e., have no project set). - Returns list sorted by 'priority'. + """Fetches all networks associated with this project, or + that are "global" (i.e., have no project set). + Returns list sorted by 'priority' (lowest integer value + is highest priority). """ if project_id is None: raise Exception(_("get_project_and_global_net_ids must be called" @@ -134,8 +135,8 @@ class QuantumMelangeIPAMLib(object): for priority, network_id, tenant_id in priority_nets] def get_subnets_by_net_id(self, context, project_id, net_id): - """ Returns information about the IPv4 and IPv6 subnets - associated with a Quantum Network UUID. + """Returns information about the IPv4 and IPv6 subnets + associated with a Quantum Network UUID. """ # FIXME(danwent): Melange actually returns the subnet info @@ -164,23 +165,23 @@ class QuantumMelangeIPAMLib(object): return (subnet_v4, subnet_v6) def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): - """ Returns a list of IPv4 address strings associated with - the specified virtual interface. + """Returns a list of IPv4 address strings associated with + the specified virtual interface. """ return self._get_ips_by_interface(context, net_id, vif_id, project_id, 4) def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): - """ Returns a list of IPv6 address strings associated with - the specified virtual interface. + """Returns a list of IPv6 address strings associated with + the specified virtual interface. """ return self._get_ips_by_interface(context, net_id, vif_id, project_id, 6) def _get_ips_by_interface(self, context, net_id, vif_id, project_id, ip_version): - """ Helper method to fetch v4 or v6 addresses for a particular - virtual interface. + """Helper method to fetch v4 or v6 addresses for a particular + virtual interface. """ tenant_id = project_id or FLAGS.quantum_default_tenant_id ip_list = self.m_conn.get_allocated_ips(net_id, vif_id, tenant_id) @@ -188,8 +189,8 @@ class QuantumMelangeIPAMLib(object): if IPNetwork(ip['address']).version == ip_version] def verify_subnet_exists(self, context, project_id, quantum_net_id): - """ Confirms that a subnet exists that is associated with the - specified Quantum Network UUID. + """Confirms that a subnet exists that is associated with the + specified Quantum Network UUID. """ tenant_id = project_id or FLAGS.quantum_default_tenant_id v4_subnet, v6_subnet = self.get_subnets_by_net_id(context, tenant_id, @@ -197,8 +198,8 @@ class QuantumMelangeIPAMLib(object): return v4_subnet is not None def deallocate_ips_by_vif(self, context, project_id, net_id, vif_ref): - """ Deallocate all fixed IPs associated with the specified - virtual interface. + """Deallocate all fixed IPs associated with the specified + virtual interface. """ tenant_id = project_id or FLAGS.quantum_default_tenant_id self.m_conn.deallocate_ips(net_id, vif_ref['uuid'], tenant_id) diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index 17236a976..21dee8f6a 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -27,7 +27,7 @@ from nova.network.quantum import melange_connection as melange from nova import utils -LOG = logging.getLogger("quantum_nova_ipam_lib") +LOG = logging.getLogger("nova.network.quantum.nova_ipam_lib") FLAGS = flags.FLAGS @@ -37,15 +37,15 @@ def get_ipam_lib(net_man): class QuantumNovaIPAMLib(object): - """ Implements Quantum IP Address Management (IPAM) interface - using the local Nova database. This implementation is inline - with how IPAM is used by other NetworkManagers. + """Implements Quantum IP Address Management (IPAM) interface + using the local Nova database. This implementation is inline + with how IPAM is used by other NetworkManagers. """ def __init__(self, net_manager): - """ Holds a reference to the "parent" network manager, used - to take advantage of various FlatManager methods to avoid - code duplication. + """Holds a reference to the "parent" network manager, used + to take advantage of various FlatManager methods to avoid + code duplication. """ self.net_manager = net_manager @@ -53,11 +53,11 @@ class QuantumNovaIPAMLib(object): quantum_net_id, priority, cidr=None, gateway_v6=None, cidr_v6=None, dns1=None, dns2=None): - """ Re-use the basic FlatManager create_networks method to - initialize the networks and fixed_ips tables in Nova DB. + """Re-use the basic FlatManager create_networks method to + initialize the networks and fixed_ips tables in Nova DB. - Also stores a few more fields in the networks table that - are needed by Quantum but not the FlatManager. + Also stores a few more fields in the networks table that + are needed by Quantum but not the FlatManager. """ admin_context = context.elevated() subnet_size = len(netaddr.IPNetwork(cidr)) @@ -85,8 +85,8 @@ class QuantumNovaIPAMLib(object): return network['uuid'] def delete_subnets_by_net_id(self, context, net_id, project_id): - """ Deletes a network based on Quantum UUID. Uses FlatManager - delete_network to avoid duplication. + """Deletes a network based on Quantum UUID. Uses FlatManager + delete_network to avoid duplication. """ admin_context = context.elevated() network = db.network_get_by_uuid(admin_context, net_id) @@ -97,9 +97,9 @@ class QuantumNovaIPAMLib(object): require_disassociated=False) def get_project_and_global_net_ids(self, context, project_id): - """ Fetches all networks associated with this project, or - that are "global" (i.e., have no project set). - Returns list sorted by 'priority'. + """Fetches all networks associated with this project, or + that are "global" (i.e., have no project set). + Returns list sorted by 'priority'. """ admin_context = context.elevated() networks = db.project_get_networks(admin_context, project_id, False) @@ -113,7 +113,7 @@ class QuantumNovaIPAMLib(object): return sorted(net_list, key=lambda x: id_priority_map[x[0]]) def allocate_fixed_ip(self, context, tenant_id, quantum_net_id, vif_rec): - """ Allocates a single fixed IPv4 address for a virtual interface.""" + """Allocates a single fixed IPv4 address for a virtual interface.""" admin_context = context.elevated() network = db.network_get_by_uuid(admin_context, quantum_net_id) if network['cidr']: @@ -125,8 +125,8 @@ class QuantumNovaIPAMLib(object): db.fixed_ip_update(admin_context, address, values) def get_subnets_by_net_id(self, context, tenant_id, net_id): - """ Returns information about the IPv4 and IPv6 subnets - associated with a Quantum Network UUID. + """Returns information about the IPv4 and IPv6 subnets + associated with a Quantum Network UUID. """ n = db.network_get_by_uuid(context.elevated(), net_id) subnet_data_v4 = { @@ -148,8 +148,8 @@ class QuantumNovaIPAMLib(object): return (subnet_data_v4, subnet_data_v6) def get_v4_ips_by_interface(self, context, net_id, vif_id, project_id): - """ Returns a list of IPv4 address strings associated with - the specified virtual interface, based on the fixed_ips table. + """Returns a list of IPv4 address strings associated with + the specified virtual interface, based on the fixed_ips table. """ vif_rec = db.virtual_interface_get_by_uuid(context, vif_id) fixed_ips = db.fixed_ip_get_by_virtual_interface(context, @@ -157,8 +157,8 @@ class QuantumNovaIPAMLib(object): return [fixed_ip['address'] for fixed_ip in fixed_ips] def get_v6_ips_by_interface(self, context, net_id, vif_id, project_id): - """ Returns a list containing a single IPv6 address strings - associated with the specified virtual interface. + """Returns a list containing a single IPv6 address strings + associated with the specified virtual interface. """ admin_context = context.elevated() network = db.network_get_by_uuid(admin_context, net_id) @@ -171,16 +171,16 @@ class QuantumNovaIPAMLib(object): return [] def verify_subnet_exists(self, context, tenant_id, quantum_net_id): - """ Confirms that a subnet exists that is associated with the - specified Quantum Network UUID. Raises an exception if no - such subnet exists. + """Confirms that a subnet exists that is associated with the + specified Quantum Network UUID. Raises an exception if no + such subnet exists. """ admin_context = context.elevated() db.network_get_by_uuid(admin_context, quantum_net_id) def deallocate_ips_by_vif(self, context, tenant_id, net_id, vif_ref): - """ Deallocate all fixed IPs associated with the specified - virtual interface. + """Deallocate all fixed IPs associated with the specified + virtual interface. """ try: admin_context = context.elevated() diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index 93892a843..21917653c 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -21,7 +21,7 @@ from nova.network.quantum import client as quantum_client from nova import utils -LOG = logging.getLogger("nova.network.quantum") +LOG = logging.getLogger("nova.network.quantum.quantum_connection") FLAGS = flags.FLAGS flags.DEFINE_string('quantum_connection_host', @@ -38,35 +38,35 @@ flags.DEFINE_string('quantum_default_tenant_id', class QuantumClientConnection(object): - """ Abstracts connection to Quantum service into higher level - operations performed by the QuantumManager. + """Abstracts connection to Quantum service into higher level + operations performed by the QuantumManager. - Separating this out as a class also let's us create a 'fake' - version of this class for unit tests. + Separating this out as a class also let's us create a 'fake' + version of this class for unit tests. """ def __init__(self): - """ Initialize Quantum client class based on flags. """ + """Initialize Quantum client class based on flags.""" self.client = quantum_client.Client(FLAGS.quantum_connection_host, FLAGS.quantum_connection_port, format="json", logger=LOG) def create_network(self, tenant_id, network_name): - """ Create network using specified name, return Quantum - network UUID. + """Create network using specified name, return Quantum + network UUID. """ data = {'network': {'name': network_name}} resdict = self.client.create_network(data, tenant=tenant_id) return resdict["network"]["id"] def delete_network(self, tenant_id, net_id): - """ Deletes Quantum network with specified UUID. """ + """Deletes Quantum network with specified UUID.""" self.client.delete_network(net_id, tenant=tenant_id) def network_exists(self, tenant_id, net_id): - """ Determine if a Quantum network exists for the - specified tenant. + """Determine if a Quantum network exists for the + specified tenant. """ try: self.client.show_network_details(net_id, tenant=tenant_id) @@ -76,9 +76,9 @@ class QuantumClientConnection(object): return False def create_and_attach_port(self, tenant_id, net_id, interface_id): - """ Creates a Quantum port on the specified network, sets - status to ACTIVE to enable traffic, and attaches the - vNIC with the specified interface-id. + """Creates a Quantum port on the specified network, sets + status to ACTIVE to enable traffic, and attaches the + vNIC with the specified interface-id. """ LOG.debug(_("Connecting interface %(interface_id)s to " "net %(net_id)s for %(tenant_id)s" % locals())) @@ -91,7 +91,7 @@ class QuantumClientConnection(object): tenant=tenant_id) def detach_and_delete_port(self, tenant_id, net_id, port_id): - """ Detach and delete the specified Quantum port. """ + """Detach and delete the specified Quantum port.""" LOG.debug(_("Deleting port %(port_id)s on net %(net_id)s" " for %(tenant_id)s" % locals())) @@ -99,8 +99,8 @@ class QuantumClientConnection(object): self.client.delete_port(net_id, port_id, tenant=tenant_id) def get_port_by_attachment(self, tenant_id, attachment_id): - """ Given a tenant, search for the Quantum network and port - UUID that has the specified interface-id attachment. + """Given a tenant, search for the Quantum network and port + UUID that has the specified interface-id attachment. """ # FIXME(danwent): this will be inefficient until the Quantum # API implements querying a port by the interface-id -- cgit From e5e3b306985a3b1fdd8a971f48b76eaf8f923f21 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 2 Sep 2011 13:24:38 -0700 Subject: fix pep8 violation --- nova/network/quantum/melange_connection.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index 0c744f080..71ac9b5f1 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -35,6 +35,7 @@ flags.DEFINE_string('melange_port', json_content_type = {'Content-type': "application/json"} + # FIXME(danwent): talk to the Melange folks about creating a # client lib that we can import as a library, instead of # have to have all of the client code in here. -- cgit From fc0ee0d01320d81b5bb6cd1bc6cb23c90c8246a7 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 2 Sep 2011 13:24:40 -0700 Subject: remove extra description stuff --- .../api/openstack/contrib/test_createserverext.py | 1 - nova/tests/api/openstack/test_servers.py | 19 +------------------ 2 files changed, 1 insertion(+), 19 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/contrib/test_createserverext.py b/nova/tests/api/openstack/contrib/test_createserverext.py index a6da9abfd..0881efcfe 100644 --- a/nova/tests/api/openstack/contrib/test_createserverext.py +++ b/nova/tests/api/openstack/contrib/test_createserverext.py @@ -98,7 +98,6 @@ class CreateserverextTest(test.TestCase): 'uuid': FAKE_UUID, 'user_id': 'fake', 'project_id': 'fake', - 'display_description': 'fakedescription', 'created_at': "", 'updated_at': ""}] diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 45ad6e5a8..a716af0e5 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -156,7 +156,6 @@ def stub_instance(id, user_id='fake', project_id='fake', private_address=None, vm_state=None, task_state=None, reservation_id="", uuid=FAKE_UUID, image_ref="10", flavor_id="1", interfaces=None, name=None, key_name='', - description='fakedescription', access_ipv4=None, access_ipv6=None): metadata = [] metadata.append(InstanceMetadata(key='seq', value=id)) @@ -211,7 +210,7 @@ def stub_instance(id, user_id='fake', project_id='fake', private_address=None, "terminated_at": utils.utcnow(), "availability_zone": "", "display_name": server_name, - "display_description": description, + "display_description": "", "locked": False, "metadata": metadata, "access_ip_v4": access_ipv4, @@ -354,7 +353,6 @@ class ServersTest(test.TestCase): "created": "2010-10-10T12:00:00Z", "progress": 0, "name": "server1", - "description": "fakedescription", "status": "BUILD", "accessIPv4": "", "accessIPv6": "", @@ -455,7 +453,6 @@ class ServersTest(test.TestCase): xmlns="http://docs.openstack.org/compute/api/v1.1" xmlns:atom="http://www.w3.org/2005/Atom" name="server1" - description="fakedescription" updated="%(expected_updated)s" created="%(expected_created)s" hostId="" @@ -528,7 +525,6 @@ class ServersTest(test.TestCase): "created": "2010-10-10T12:00:00Z", "progress": 100, "name": "server1", - "description": "fakedescription", "status": "ACTIVE", "accessIPv4": "", "accessIPv6": "", @@ -626,7 +622,6 @@ class ServersTest(test.TestCase): "created": "2010-10-10T12:00:00Z", "progress": 100, "name": "server1", - "description": "fakedescription", "status": "ACTIVE", "accessIPv4": "", "accessIPv6": "", @@ -1488,7 +1483,6 @@ class ServersTest(test.TestCase): 'access_ip_v4': '1.2.3.4', 'access_ip_v6': 'fead::1234', 'image_ref': image_ref, - 'display_description': 'fakedescription', 'user_id': 'fake', 'project_id': 'fake', "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), @@ -3368,7 +3362,6 @@ class TestServerInstanceCreation(test.TestCase): return [{'id': '1234', 'display_name': 'fakeinstance', 'user_id': 'fake', 'project_id': 'fake', - 'display_description': 'fakedescription', 'uuid': FAKE_UUID}] def set_admin_password(self, *args, **kwargs): @@ -3686,7 +3679,6 @@ class ServersViewBuilderV11Test(test.TestCase): "terminated_at": utils.utcnow(), "availability_zone": "", "display_name": "test_server", - "display_description": "fakedescription", "locked": False, "metadata": [], "accessIPv4": "1.2.3.4", @@ -3775,7 +3767,6 @@ class ServersViewBuilderV11Test(test.TestCase): "created": "2010-10-10T12:00:00Z", "progress": 0, "name": "test_server", - "description": "fakedescription", "status": "BUILD", "accessIPv4": "", "accessIPv6": "", @@ -3833,7 +3824,6 @@ class ServersViewBuilderV11Test(test.TestCase): "created": "2010-10-10T12:00:00Z", "progress": 100, "name": "test_server", - "description": "fakedescription", "status": "ACTIVE", "accessIPv4": "", "accessIPv6": "", @@ -4011,7 +4001,6 @@ class ServersViewBuilderV11Test(test.TestCase): "created": "2010-10-10T12:00:00Z", "progress": 0, "name": "test_server", - "description": "fakedescription", "status": "BUILD", "accessIPv4": "", "accessIPv6": "", @@ -4083,7 +4072,6 @@ class ServerXMLSerializationTest(test.TestCase): 'updated': self.TIMESTAMP, "progress": 0, "name": "test_server", - "description": "fakedescription", "status": "BUILD", "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', "key_name": '', @@ -4222,7 +4210,6 @@ class ServerXMLSerializationTest(test.TestCase): 'updated': self.TIMESTAMP, "progress": 0, "name": "test_server", - "description": "fakedescription", "status": "BUILD", "accessIPv4": "1.2.3.4", "accessIPv6": "fead::1234", @@ -4425,7 +4412,6 @@ class ServerXMLSerializationTest(test.TestCase): 'updated': self.TIMESTAMP, "progress": 0, "name": "test_server", - "description": "fakedescription", "status": "BUILD", "accessIPv4": "1.2.3.4", "accessIPv6": "fead::1234", @@ -4483,7 +4469,6 @@ class ServerXMLSerializationTest(test.TestCase): 'updated': self.TIMESTAMP, "progress": 100, "name": "test_server_2", - "description": "fakedescription", "status": "ACTIVE", "accessIPv4": "1.2.3.4", "accessIPv6": "fead::1234", @@ -4606,7 +4591,6 @@ class ServerXMLSerializationTest(test.TestCase): 'updated': self.TIMESTAMP, "progress": 0, "name": "test_server", - "description": "fakedescription", "status": "BUILD", "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', "accessIPv4": "1.2.3.4", @@ -4744,7 +4728,6 @@ class ServerXMLSerializationTest(test.TestCase): 'updated': self.TIMESTAMP, "progress": 0, "name": "test_server", - "description": "fakedescription", "status": "BUILD", "accessIPv4": "1.2.3.4", "accessIPv6": "fead::1234", -- cgit From dd7aa0234080d5c3512d0e9bab831a621aac10aa Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Fri, 2 Sep 2011 16:30:52 -0400 Subject: use 'qemu-image resize' rather than 'truncate' to grow image files qcow-image is capable of growing qcow formated disks in addition to being able to grow a qcow formated one. (LP: #836759) --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 52b2881e8..50c7c40e9 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -58,7 +58,7 @@ def extend(image, size): file_size = os.path.getsize(image) if file_size >= size: return - utils.execute('truncate', '-s', size, image) + utils.execute('qemu-img', 'resize', image, size) # NOTE(vish): attempts to resize filesystem utils.execute('e2fsck', '-fp', image, check_exit_code=False) utils.execute('resize2fs', image, check_exit_code=False) -- cgit From 6eb28b5748a829d058fd35888f03f2ee1f26f5b5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 2 Sep 2011 13:31:19 -0700 Subject: default description to name --- nova/api/openstack/create_instance_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 289f87921..9b2928bc8 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -164,7 +164,7 @@ class CreateInstanceHelper(object): # NOTE(vish): This is solely for compatibility with anything # that is using the display description field. metadata = server_dict.get('metadata') or {} - display_description = metadata.get('description') or '' + display_description = metadata.get('description') or name return (extra_values, create_method(context, inst_type, -- cgit From 53f1aafc546b165cfdd74aa6620fe4c288a9359a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 2 Sep 2011 17:28:58 -0500 Subject: alex meade issues --- nova/tests/fake_network.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 2e8cf60ec..36aebe6b0 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -34,31 +34,31 @@ class FakeModel(dict): return self[name] -def fake_network(n, ipv6=None): +def fake_network(network_id, ipv6=None): if ipv6 == None: ipv6 = FLAGS.use_ipv6 - rval = {'id': n, - 'label': 'test%d' % n, + rval = {'id': network_id, + 'label': 'test%d' % network_id, 'injected': False, 'multi_host': False, - 'cidr': '192.168.%d.0/24' % n, + 'cidr': '192.168.%d.0/24' % network_id, 'cidr_v6': None, 'netmask': '255.255.255.0', 'netmask_v6': None, - 'bridge': 'fake_br%d' % n, - 'bridge_interface': 'fake_eth%d' % n, - 'gateway': '192.168.%d.1' % n, + 'bridge': 'fake_br%d' % network_id, + 'bridge_interface': 'fake_eth%d' % network_id, + 'gateway': '192.168.%d.1' % network_id, 'gateway_v6': None, - 'broadcast': '192.168.%d.255' % n, - 'dns1': '192.168.%d.3' % n, - 'dns2': '192.168.%d.4' % n, + 'broadcast': '192.168.%d.255' % network_id, + 'dns1': '192.168.%d.3' % network_id, + 'dns2': '192.168.%d.4' % network_id, 'vlan': None, 'host': None, 'project_id': 'fake_project', - 'vpn_public_address': '192.168.%d.2' % n} + 'vpn_public_address': '192.168.%d.2' % network_id} if ipv6: - rval['cidr_v6'] = '2001:db8:0:%x::/64' % n - rval['gateway_v6'] = '2001:db8:0:%x::1' % n + rval['cidr_v6'] = '2001:db8:0:%x::/64' % network_id + rval['gateway_v6'] = '2001:db8:0:%x::1' % network_id rval['netmask_v6'] = '64' return rval @@ -109,11 +109,11 @@ def vifs(n): 'instance_id': 0} -def ipv4_like(ip, s): +def ipv4_like(ip, match_string): ip = ip.split('.') - s = s.split('.') + match_octets = match_string.split('.') - for i, octet in enumerate(s): + for i, octet in enumerate(match_octets): if octet == '*': continue if octet != ip[i]: @@ -121,7 +121,7 @@ def ipv4_like(ip, s): return True -def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): +def fake_get_instance_nw_info(stubs, num_networks=1, ips_per_vif=2): # stubs is the self.stubs from the test # ips_per_vif is the number of ips each vif will have # num_floating_ips is number of float ips for each fixed ip @@ -129,10 +129,10 @@ def fake_get_instance_nw_info(stubs, n=1, ips_per_vif=2): network.db = db def fixed_ips_fake(*args, **kwargs): - return list(fixed_ips(n, ips_per_vif)) + return list(fixed_ips(num_networks, ips_per_vif)) def virtual_interfaces_fake(*args, **kwargs): - return [vif for vif in vifs(n)] + return [vif for vif in vifs(num_networks)] def instance_type_fake(*args, **kwargs): return flavor -- cgit From cfdc4642bfa3d96e2335079d187945b9ca4c0141 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 2 Sep 2011 18:00:34 -0500 Subject: rick nits --- nova/tests/fake_network.py | 68 ++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 33 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 36aebe6b0..10565f83c 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -14,6 +14,7 @@ # 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 itertools from nova import db from nova import flags @@ -35,48 +36,49 @@ class FakeModel(dict): def fake_network(network_id, ipv6=None): - if ipv6 == None: + if ipv6 is None: ipv6 = FLAGS.use_ipv6 - rval = {'id': network_id, - 'label': 'test%d' % network_id, - 'injected': False, - 'multi_host': False, - 'cidr': '192.168.%d.0/24' % network_id, - 'cidr_v6': None, - 'netmask': '255.255.255.0', - 'netmask_v6': None, - 'bridge': 'fake_br%d' % network_id, - 'bridge_interface': 'fake_eth%d' % network_id, - 'gateway': '192.168.%d.1' % network_id, - 'gateway_v6': None, - 'broadcast': '192.168.%d.255' % network_id, - 'dns1': '192.168.%d.3' % network_id, - 'dns2': '192.168.%d.4' % network_id, - 'vlan': None, - 'host': None, - 'project_id': 'fake_project', - 'vpn_public_address': '192.168.%d.2' % network_id} + fake_network = {'id': network_id, + 'label': 'test%d' % network_id, + 'injected': False, + 'multi_host': False, + 'cidr': '192.168.%d.0/24' % network_id, + 'cidr_v6': None, + 'netmask': '255.255.255.0', + 'netmask_v6': None, + 'bridge': 'fake_br%d' % network_id, + 'bridge_interface': 'fake_eth%d' % network_id, + 'gateway': '192.168.%d.1' % network_id, + 'gateway_v6': None, + 'broadcast': '192.168.%d.255' % network_id, + 'dns1': '192.168.%d.3' % network_id, + 'dns2': '192.168.%d.4' % network_id, + 'vlan': None, + 'host': None, + 'project_id': 'fake_project', + 'vpn_public_address': '192.168.%d.2' % network_id} if ipv6: - rval['cidr_v6'] = '2001:db8:0:%x::/64' % network_id - rval['gateway_v6'] = '2001:db8:0:%x::1' % network_id - rval['netmask_v6'] = '64' + fake_network['cidr_v6'] = '2001:db8:0:%x::/64' % network_id + fake_network['gateway_v6'] = '2001:db8:0:%x::1' % network_id + fake_network['netmask_v6'] = '64' - return rval + return fake_network def fixed_ips(num_networks, num_ips, num_floating_ips=0): - for network in xrange(num_networks): - for ip in xrange(num_ips): - id = network * num_ips + ip - f_ips = [floating_ips(id).next() for i in xrange(num_floating_ips)] - yield {'id': id, - 'network_id': network, - 'address': '192.168.%d.1%02d' % (network, ip), + for network_index in xrange(num_networks): + for ip_index in xrange(num_ips): + fixed_ip_id = network_index * num_ips + ip_index + f_ips = [FakeModel(**floating_ips(fixed_ip_id).next()) + for i in xrange(num_floating_ips)] + yield {'id': fixed_ip_id, + 'network_id': network_index, + 'address': '192.168.%d.1%02d' % (network_index, ip_index), 'instance_id': 0, 'allocated': False, # and since network_id and vif_id happen to be equivalent - 'virtual_interface_id': network, - 'floating_ips': [FakeModel(**ip) for ip in f_ips]} + 'virtual_interface_id': network_index, + 'floating_ips': f_ips} flavor = {'id': 0, -- cgit From f970ce0fbb3d4de560f73a01b508d8f0f7ac9117 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Fri, 2 Sep 2011 18:02:41 -0500 Subject: removed unneeded import --- nova/tests/fake_network.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 10565f83c..73a117c33 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -14,7 +14,6 @@ # 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 itertools from nova import db from nova import flags -- cgit From 80059b3e87f6ce7ab2ba18a135e5c469d2be8f88 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Sun, 4 Sep 2011 01:19:21 -0500 Subject: correct floating ip id to increment in fake_network --- nova/tests/fake_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 73a117c33..99b027cf3 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -93,7 +93,7 @@ flavor = {'id': 0, def floating_ips(fixed_ip_id): for i in xrange(154): - yield {'id': 0, + yield {'id': i, 'address': '10.10.10.%d' % (i + 100), 'fixed_ip_id': fixed_ip_id, 'project_id': None, -- cgit From 6b61a6be4a14444723e3728fb0fcdd77bac8fe74 Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Mon, 5 Sep 2011 14:45:41 +0900 Subject: Fix bug #835919 that output a option file for dnsmasq not to offer a default gateway on second vif. --- nova/db/sqlalchemy/api.py | 15 +++++++++++++ nova/network/linux_net.py | 55 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 2 deletions(-) mode change 100644 => 100755 nova/db/sqlalchemy/api.py mode change 100644 => 100755 nova/network/linux_net.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py old mode 100644 new mode 100755 index b99667afc..aee1c2a55 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1415,6 +1415,21 @@ def instance_get_all_by_reservation(context, reservation_id): filter_by(project_id=context.project_id).\ filter_by(deleted=False).\ all() + +@require_admin_context +def instance_get_all_by_network(context, network_id): + session = get_session() + return session.query(models.Instance).\ + options(joinedload_all('fixed_ips.floating_ips')).\ + options(joinedload('virtual_interfaces')).\ + options(joinedload('security_groups')).\ + options(joinedload_all('fixed_ips.network')).\ + options(joinedload('metadata')).\ + options(joinedload('instance_type')).\ + filter_by(deleted=can_read_deleted(context)).\ + filter_by(network_id=network_id).\ + all() + @require_context diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py old mode 100644 new mode 100755 index 57c1d0c28..8d5a0bbbd --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -511,6 +511,35 @@ def get_dhcp_hosts(context, network_ref): return '\n'.join(hosts) +def get_dhcp_opts(context, network_ref): + """Get network's hosts config in dhcp-opts format.""" + # create a decision dictionary for default gateway for eache instance + default_gateway_network_node = dict() + network_id = network_ref['id'] + instance_refs = db.instance_get_all_by_network(context, network_id) + ips_ref = db.network_get_associated_fixed_ips(context, network_id) + + for instance_ref in instance_refs: + instance_id = instance_ref['id'] + # nic number is decided by ‡”Ô from this function in xxx function + vifs = db.virtual_interface_get_by_instance(context, instance_id) + if not vifs: + continue + # offer a default gateway to the first virtual interface of instance + first_vif = vifs[0] + default_gateway_network_node[instance_id] = first_vif['network_id'] + + for fixed_ip_ref in ips_ref: + instance_id = fixed_ip_ref['instance_id'] + target_network_id = default_gateway_network_node[instance_id] + if target_network_id == fixed_ip_ref['network_id']: + hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=True)) + else: + hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=None)) + + return '\n'.join(hosts) + + # NOTE(ja): Sending a HUP only reloads the hostfile, so any # configuration options (like dchp-range, vlan, ...) # aren't reloaded. @@ -526,8 +555,13 @@ def update_dhcp(context, dev, network_ref): with open(conffile, 'w') as f: f.write(get_dhcp_hosts(context, network_ref)) + optsfile = _dhcp_file(dev, 'opts') + with open(optsfile, 'w') as f: + f.write(get_dhcp_opts(context, network_ref)) + # Make sure dnsmasq can actually read it (it setuid()s to "nobody") os.chmod(conffile, 0644) + os.chmod(optsfile, 0644) pid = _dnsmasq_pid_for(dev) @@ -559,6 +593,7 @@ def update_dhcp(context, dev, network_ref): '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(network_ref['cidr'])), '--dhcp-hostsfile=%s' % _dhcp_file(dev, 'conf'), '--dhcp-script=%s' % FLAGS.dhcpbridge, + '--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts'), '--leasefile-ro'] if FLAGS.dns_server: cmd += ['-h', '-R', '--server=%s' % FLAGS.dns_server] @@ -625,13 +660,29 @@ def _host_lease(fixed_ip_ref): instance_ref['hostname'] or '*') +def _host_dhcp_network(fixed_ip_ref): + instance_ref = fixed_ip_ref['instance'] + return 'NW-i%08d-%s' % (instance_ref['id'], + fixed_ip_ref['network_id']) + + def _host_dhcp(fixed_ip_ref): """Return a host string for an address in dhcp-host format.""" instance_ref = fixed_ip_ref['instance'] - return '%s,%s.%s,%s' % (fixed_ip_ref['virtual_interface']['address'], + return '%s,%s.%s,%s,%s' % (fixed_ip_ref['virtual_interface']['address'], instance_ref['hostname'], FLAGS.dhcp_domain, - fixed_ip_ref['address']) + fixed_ip_ref['address'], + "net:" + _host_dhcp_network(fixed_ip_ref)) + + +def _host_dhcp_opts(fixed_ip_ref, gw): + """Return a host string for an address in dhcp-host format.""" + if not gw: + return '%s,%s' % (_host_dhcp_network(fixed_ip_ref), + 3) + else: + return '' def _execute(*cmd, **kwargs): -- cgit From d6f1a31d56de84398246498d0f2676d9741cdccf Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Mon, 5 Sep 2011 20:23:28 +0900 Subject: implement unit test for linux_net --- nova/db/api.py | 5 + nova/network/linux_net.py | 17 ++-- nova/tests/test_linux_net.py | 232 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+), 7 deletions(-) mode change 100644 => 100755 nova/db/api.py create mode 100755 nova/tests/test_linux_net.py (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py old mode 100644 new mode 100755 index 148887635..85e9c7669 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -532,6 +532,11 @@ def instance_get_all_by_reservation(context, reservation_id): return IMPL.instance_get_all_by_reservation(context, reservation_id) +def instance_get_all_by_network(context, network_id): + """Get all instances belonging to a network.""" + return IMPL.instance_get_all_by_network(context, network_id) + + def instance_get_by_fixed_ip(context, address): """Get an instance for a fixed ip by address.""" return IMPL.instance_get_by_fixed_ip(context, address) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 8d5a0bbbd..50d9ccf19 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -514,6 +514,7 @@ def get_dhcp_hosts(context, network_ref): def get_dhcp_opts(context, network_ref): """Get network's hosts config in dhcp-opts format.""" # create a decision dictionary for default gateway for eache instance + hosts = [] default_gateway_network_node = dict() network_id = network_ref['id'] instance_refs = db.instance_get_all_by_network(context, network_id) @@ -521,9 +522,10 @@ def get_dhcp_opts(context, network_ref): for instance_ref in instance_refs: instance_id = instance_ref['id'] - # nic number is decided by ‡”Ô from this function in xxx function + # nic number is decided by xxx from this function in xxx function vifs = db.virtual_interface_get_by_instance(context, instance_id) - if not vifs: + if vifs == None: + default_gateway_network_node[instance_id] = None continue # offer a default gateway to the first virtual interface of instance first_vif = vifs[0] @@ -531,11 +533,12 @@ def get_dhcp_opts(context, network_ref): for fixed_ip_ref in ips_ref: instance_id = fixed_ip_ref['instance_id'] - target_network_id = default_gateway_network_node[instance_id] - if target_network_id == fixed_ip_ref['network_id']: - hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=True)) - else: - hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=None)) + if default_gateway_network_node.has_key(instance_id): + target_network_id = default_gateway_network_node[instance_id] + if target_network_id == fixed_ip_ref['network_id']: + hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=True)) + else: + hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=None)) return '\n'.join(hosts) diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py new file mode 100755 index 000000000..3c3cdd0d9 --- /dev/null +++ b/nova/tests/test_linux_net.py @@ -0,0 +1,232 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 NTT +# 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. + +from nova import context +from nova import db +from nova import exception +from nova import log as logging +from nova import test +from nova import utils +from nova import flags +from nova.network import manager as network_manager + +import mox + +FLAGS = flags.FLAGS + +LOG = logging.getLogger('nova.tests.network') + + +HOST = "testhost" + +instances = [{'id': 0, + 'host': 'fake_instance00', + 'hostname': 'fake_instance00'}, + {'id': 1, + 'host': 'fake_instance01', + 'hostname': 'fake_instance01'}] + + +addresses = [{"address" : "10.0.0.1" }, + {"address" : "10.0.0.2" }, + {"address" : "10.0.0.3" }, + {"address" : "10.0.0.4" }] + + +networks = [{'id': 0, + 'uuid': "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + 'label': 'test0', + 'injected': False, + 'multi_host': False, + 'cidr': '192.168.0.0/24', + 'cidr_v6': '2001:db8::/64', + 'gateway_v6': '2001:db8::1', + 'netmask_v6': '64', + 'netmask': '255.255.255.0', + 'bridge': 'fa0', + 'bridge_interface': 'fake_fa0', + 'gateway': '192.168.0.1', + 'broadcast': '192.168.0.255', + 'dns1': '192.168.0.1', + 'dns2': '192.168.0.2', + 'dhcp_server' : '0.0.0.0', + 'dhcp_start' : '192.168.100.1', + 'vlan': None, + 'host': None, + 'project_id': 'fake_project', + 'vpn_public_address': '192.168.0.2'}, + {'id': 1, + 'uuid': "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", + 'label': 'test1', + 'injected': False, + 'multi_host': False, + 'cidr': '192.168.1.0/24', + 'cidr_v6': '2001:db9::/64', + 'gateway_v6': '2001:db9::1', + 'netmask_v6': '64', + 'netmask': '255.255.255.0', + 'bridge': 'fa1', + 'bridge_interface': 'fake_fa1', + 'gateway': '192.168.1.1', + 'broadcast': '192.168.1.255', + 'dns1': '192.168.0.1', + 'dns2': '192.168.0.2', + 'dhcp_server' : '0.0.0.0', + 'dhcp_start' : '192.168.100.1', + 'vlan': None, + 'host': None, + 'project_id': 'fake_project', + 'vpn_public_address': '192.168.1.2'}] + + +fixed_ips = [{'id': 0, + 'network_id': 0, + 'address': '192.168.0.100', + 'instance_id': 0, + 'allocated': True, + 'virtual_interface_id': 0, + 'virtual_interface' : addresses[0], + 'instance': instances[0], + 'floating_ips': []}, + {'id': 1, + 'network_id': 1, + 'address': '192.168.1.100', + 'instance_id': 0, + 'allocated': True, + 'virtual_interface_id': 1, + 'virtual_interface' : addresses[1], + 'instance': instances[0], + 'floating_ips': []}, + {'id': 2, + 'network_id': 0, + 'address': '192.168.0.101', + 'instance_id': 1, + 'allocated': True, + 'virtual_interface_id': 2, + 'virtual_interface' : addresses[2], + 'instance': instances[1], + 'floating_ips': []}, + {'id': 3, + 'network_id': 1, + 'address': '192.168.1.101', + 'instance_id': 1, + 'allocated': True, + 'virtual_interface_id': 3, + 'virtual_interface' : addresses[3], + 'instance': instances[1], + 'floating_ips': []}] + + + + +vifs = [{'id': 0, + 'address': 'DE:AD:BE:EF:00:00', + 'uuid': '00000000-0000-0000-0000-0000000000000000', + 'network_id': 0, + 'network': networks[0], + 'instance_id': 0}, + {'id': 1, + 'address': 'DE:AD:BE:EF:00:01', + 'uuid': '00000000-0000-0000-0000-0000000000000001', + 'network_id': 1, + 'network': networks[1], + 'instance_id': 0}, + {'id': 2, + 'address': 'DE:AD:BE:EF:00:02', + 'uuid': '00000000-0000-0000-0000-0000000000000002', + 'network_id': 1, + 'network': networks[1], + 'instance_id': 1}, + {'id': 3, + 'address': 'DE:AD:BE:EF:00:03', + 'uuid': '00000000-0000-0000-0000-0000000000000003', + 'network_id': 0, + 'network': networks[0], + 'instance_id': 1}] + + +class LinuxNetworkTestCase(test.TestCase): + + def setUp(self): + super(LinuxNetworkTestCase, self).setUp() + network_driver = FLAGS.network_driver + self.driver = utils.import_object(network_driver) + self.driver.db = db + + def test_update_dhcp(self): + self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') + self.mox.StubOutWithMock(db, 'instance_get_all_by_network') + self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') + + fixed_ips[1]['instance'] = instances[0] + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips) + + db.instance_get_all_by_network(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(instances) + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn([vifs[0],vifs[1]]) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn([vifs[2],vifs[3]]) + + self.mox.ReplayAll() + self.driver.update_dhcp(None, "eth0", networks[0]) + + + def test_get_dhcp_hosts(self): + self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') + + fixed_ips[1]['instance'] = instances[0] + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips) + + self.mox.ReplayAll() + + hosts = self.driver.get_dhcp_hosts(None, networks[0]) + + self.assertEquals(hosts, + "10.0.0.1,fake_instance00.novalocal,192.168.0.100,net:NW-i00000000-0\n" \ + "10.0.0.2,fake_instance00.novalocal,192.168.1.100,net:NW-i00000000-1\n" \ + "10.0.0.3,fake_instance01.novalocal,192.168.0.101,net:NW-i00000001-0\n" \ + "10.0.0.4,fake_instance01.novalocal,192.168.1.101,net:NW-i00000001-1") + + + def test_get_dhcp_opts(self): + self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') + self.mox.StubOutWithMock(db, 'instance_get_all_by_network') + self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') + + fixed_ips[1]['instance'] = instances[0] + + db.instance_get_all_by_network(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(instances) + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(fixed_ips) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn([vifs[0],vifs[1]]) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn([vifs[2],vifs[3]]) + + self.mox.ReplayAll() + + opts = self.driver.get_dhcp_opts(None, networks[0]) + self.assertEquals(opts, '\nNW-i00000000-1,3\nNW-i00000001-0,3\n') + + + -- cgit From 418923c385a34265dd33a0c2ada3aa97a5749a06 Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Mon, 5 Sep 2011 21:13:00 +0900 Subject: format for pep8 --- nova/db/api.py | 4 +- nova/db/sqlalchemy/api.py | 5 +- nova/network/linux_net.py | 6 +- nova/tests/test_linux_net.py | 177 ++++++++++++++++++++++++++++++++----------- 4 files changed, 141 insertions(+), 51 deletions(-) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index 85e9c7669..faac13966 100755 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -535,8 +535,8 @@ def instance_get_all_by_reservation(context, reservation_id): def instance_get_all_by_network(context, network_id): """Get all instances belonging to a network.""" return IMPL.instance_get_all_by_network(context, network_id) - - + + def instance_get_by_fixed_ip(context, address): """Get an instance for a fixed ip by address.""" return IMPL.instance_get_by_fixed_ip(context, address) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index aee1c2a55..3194a5231 100755 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1415,8 +1415,8 @@ def instance_get_all_by_reservation(context, reservation_id): filter_by(project_id=context.project_id).\ filter_by(deleted=False).\ all() - -@require_admin_context + + def instance_get_all_by_network(context, network_id): session = get_session() return session.query(models.Instance).\ @@ -1431,7 +1431,6 @@ def instance_get_all_by_network(context, network_id): all() - @require_context def instance_get_by_fixed_ip(context, address): """Return instance ref by exact match of FixedIP""" diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 50d9ccf19..4ebc3df23 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -438,7 +438,7 @@ def ensure_vpn_forward(public_ip, port, private_ip): def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule.""" - for chain, rule in floating_forward_rules(floating_ip, fixed_ip): + for chain, rule in floating_forward_rules(floaing_ip, fixed_ip): iptables_manager.ipv4['nat'].add_rule(chain, rule) iptables_manager.apply() @@ -518,7 +518,7 @@ def get_dhcp_opts(context, network_ref): default_gateway_network_node = dict() network_id = network_ref['id'] instance_refs = db.instance_get_all_by_network(context, network_id) - ips_ref = db.network_get_associated_fixed_ips(context, network_id) + ips_ref = db.network_get_associated_fixed_ips(context, network_id) for instance_ref in instance_refs: instance_id = instance_ref['id'] @@ -533,7 +533,7 @@ def get_dhcp_opts(context, network_ref): for fixed_ip_ref in ips_ref: instance_id = fixed_ip_ref['instance_id'] - if default_gateway_network_node.has_key(instance_id): + if instance_id in default_gateway_network_node: target_network_id = default_gateway_network_node[instance_id] if target_network_id == fixed_ip_ref['network_id']: hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=True)) diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index 3c3cdd0d9..94c53817b 100755 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -41,10 +41,10 @@ instances = [{'id': 0, 'hostname': 'fake_instance01'}] -addresses = [{"address" : "10.0.0.1" }, - {"address" : "10.0.0.2" }, - {"address" : "10.0.0.3" }, - {"address" : "10.0.0.4" }] +addresses = [{"address": "10.0.0.1"}, + {"address": "10.0.0.2"}, + {"address": "10.0.0.3"}, + {"address": "10.0.0.4"}] networks = [{'id': 0, @@ -63,8 +63,8 @@ networks = [{'id': 0, 'broadcast': '192.168.0.255', 'dns1': '192.168.0.1', 'dns2': '192.168.0.2', - 'dhcp_server' : '0.0.0.0', - 'dhcp_start' : '192.168.100.1', + 'dhcp_server': '0.0.0.0', + 'dhcp_start': '192.168.100.1', 'vlan': None, 'host': None, 'project_id': 'fake_project', @@ -85,8 +85,8 @@ networks = [{'id': 0, 'broadcast': '192.168.1.255', 'dns1': '192.168.0.1', 'dns2': '192.168.0.2', - 'dhcp_server' : '0.0.0.0', - 'dhcp_start' : '192.168.100.1', + 'dhcp_server': '0.0.0.0', + 'dhcp_start': '192.168.100.1', 'vlan': None, 'host': None, 'project_id': 'fake_project', @@ -99,7 +99,7 @@ fixed_ips = [{'id': 0, 'instance_id': 0, 'allocated': True, 'virtual_interface_id': 0, - 'virtual_interface' : addresses[0], + 'virtual_interface': addresses[0], 'instance': instances[0], 'floating_ips': []}, {'id': 1, @@ -108,7 +108,7 @@ fixed_ips = [{'id': 0, 'instance_id': 0, 'allocated': True, 'virtual_interface_id': 1, - 'virtual_interface' : addresses[1], + 'virtual_interface': addresses[1], 'instance': instances[0], 'floating_ips': []}, {'id': 2, @@ -117,7 +117,7 @@ fixed_ips = [{'id': 0, 'instance_id': 1, 'allocated': True, 'virtual_interface_id': 2, - 'virtual_interface' : addresses[2], + 'virtual_interface': addresses[2], 'instance': instances[1], 'floating_ips': []}, {'id': 3, @@ -126,13 +126,11 @@ fixed_ips = [{'id': 0, 'instance_id': 1, 'allocated': True, 'virtual_interface_id': 3, - 'virtual_interface' : addresses[3], + 'virtual_interface': addresses[3], 'instance': instances[1], 'floating_ips': []}] - - vifs = [{'id': 0, 'address': 'DE:AD:BE:EF:00:00', 'uuid': '00000000-0000-0000-0000-0000000000000000', @@ -167,66 +165,159 @@ class LinuxNetworkTestCase(test.TestCase): self.driver = utils.import_object(network_driver) self.driver.db = db - def test_update_dhcp(self): + def test_update_dhcp_for_nw00(self): self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'instance_get_all_by_network') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') - fixed_ips[1]['instance'] = instances[0] db.network_get_associated_fixed_ips(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips) + mox.IgnoreArg())\ + .AndReturn([fixed_ips[0], + fixed_ips[3]]) db.instance_get_all_by_network(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(instances) + mox.IgnoreArg())\ + .AndReturn(instances) db.network_get_associated_fixed_ips(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips) + mox.IgnoreArg())\ + .AndReturn([fixed_ips[0], + fixed_ips[3]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn([vifs[0],vifs[1]]) + mox.IgnoreArg())\ + .AndReturn([vifs[0], vifs[1]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn([vifs[2],vifs[3]]) - + mox.IgnoreArg())\ + .AndReturn([vifs[2], vifs[3]]) self.mox.ReplayAll() + self.driver.update_dhcp(None, "eth0", networks[0]) + def test_update_dhcp_for_nw01(self): + self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') + self.mox.StubOutWithMock(db, 'instance_get_all_by_network') + self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') + + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([fixed_ips[1], + fixed_ips[2]]) - def test_get_dhcp_hosts(self): + db.instance_get_all_by_network(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn(instances) + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([fixed_ips[1], + fixed_ips[2]]) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([vifs[0], vifs[1]]) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([vifs[2], vifs[3]]) + self.mox.ReplayAll() + + self.driver.update_dhcp(None, "eth0", networks[0]) + + def test_get_dhcp_hosts_for_nw00(self): self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') - fixed_ips[1]['instance'] = instances[0] db.network_get_associated_fixed_ips(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips) + mox.IgnoreArg())\ + .AndReturn([fixed_ips[0], + fixed_ips[3]]) + self.mox.ReplayAll() + + expected = \ + "10.0.0.1,fake_instance00.novalocal,"\ + "192.168.0.100,net:NW-i00000000-0\n"\ + "10.0.0.4,fake_instance01.novalocal,"\ + "192.168.1.101,net:NW-i00000001-1" + actual_hosts = self.driver.get_dhcp_hosts(None, networks[1]) + self.assertEquals(actual_hosts, expected) + + def test_get_dhcp_hosts_for_nw01(self): + self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') + + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([fixed_ips[1], + fixed_ips[2]]) self.mox.ReplayAll() - hosts = self.driver.get_dhcp_hosts(None, networks[0]) - - self.assertEquals(hosts, - "10.0.0.1,fake_instance00.novalocal,192.168.0.100,net:NW-i00000000-0\n" \ - "10.0.0.2,fake_instance00.novalocal,192.168.1.100,net:NW-i00000000-1\n" \ - "10.0.0.3,fake_instance01.novalocal,192.168.0.101,net:NW-i00000001-0\n" \ - "10.0.0.4,fake_instance01.novalocal,192.168.1.101,net:NW-i00000001-1") + expected = \ + "10.0.0.2,fake_instance00.novalocal,"\ + "192.168.1.100,net:NW-i00000000-1\n"\ + "10.0.0.3,fake_instance01.novalocal,"\ + "192.168.0.101,net:NW-i00000001-0" + actual_hosts = self.driver.get_dhcp_hosts(None, networks[0]) + + self.assertEquals(actual_hosts, expected) - - def test_get_dhcp_opts(self): + def test_get_dhcp_opts_for_nw00(self): self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'instance_get_all_by_network') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') - fixed_ips[1]['instance'] = instances[0] - db.instance_get_all_by_network(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(instances) + mox.IgnoreArg())\ + .AndReturn(instances) db.network_get_associated_fixed_ips(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(fixed_ips) + mox.IgnoreArg())\ + .AndReturn([fixed_ips[0], + fixed_ips[3]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn([vifs[0],vifs[1]]) + mox.IgnoreArg())\ + .AndReturn([vifs[0], + vifs[1]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn([vifs[2],vifs[3]]) + mox.IgnoreArg())\ + .AndReturn([vifs[2], + vifs[3]]) + self.mox.ReplayAll() + + expected_opts = '\n'\ + '' + actual_opts = self.driver.get_dhcp_opts(None, networks[0]) + self.assertEquals(actual_opts, expected_opts) + + def test_get_dhcp_opts_for_nw01(self): + self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') + self.mox.StubOutWithMock(db, 'instance_get_all_by_network') + self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') + + db.instance_get_all_by_network(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn(instances) + db.network_get_associated_fixed_ips(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([fixed_ips[1], + fixed_ips[2]]) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([vifs[0], + vifs[1]]) + db.virtual_interface_get_by_instance(mox.IgnoreArg(), + mox.IgnoreArg())\ + .AndReturn([vifs[2], + vifs[3]]) self.mox.ReplayAll() - opts = self.driver.get_dhcp_opts(None, networks[0]) - self.assertEquals(opts, '\nNW-i00000000-1,3\nNW-i00000001-0,3\n') + expected_opts = 'NW-i00000000-1,3\n'\ + 'NW-i00000001-0,3' + actual_opts = self.driver.get_dhcp_opts(None, networks[1]) + + self.assertEquals(actual_opts, expected_opts) + + def test_dhcp_opts_default_gateway_network(self): + expected = "" + actual = self.driver._host_dhcp_opts(fixed_ips[0], True) + self.assertEquals(actual, expected) - + def test_dhcp_opts_not_default_gateway_network(self): + expected = "NW-i00000000-0,3" + actual = self.driver._host_dhcp_opts(fixed_ips[0], False) + self.assertEquals(actual, expected) -- cgit From 14314701bac27ce0e913f8e0587c354819a4bd9e Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Mon, 5 Sep 2011 21:32:52 +0900 Subject: format for pep8 --- nova/tests/test_linux_net.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index 94c53817b..93d9b02c9 100755 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -316,7 +316,6 @@ class LinuxNetworkTestCase(test.TestCase): actual = self.driver._host_dhcp_opts(fixed_ips[0], True) self.assertEquals(actual, expected) - def test_dhcp_opts_not_default_gateway_network(self): expected = "NW-i00000000-0,3" actual = self.driver._host_dhcp_opts(fixed_ips[0], False) -- cgit From b922e08a6c15eeaab1f7ec342c00673b610d0e76 Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Tue, 6 Sep 2011 10:14:27 +0900 Subject: correct a method to collect instances from db add interface data to test --- nova/db/api.py | 5 --- nova/db/sqlalchemy/api.py | 14 --------- nova/network/linux_net.py | 8 +++-- nova/tests/test_linux_net.py | 74 +++++++++++++++++++++++++++++--------------- 4 files changed, 54 insertions(+), 47 deletions(-) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index faac13966..148887635 100755 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -532,11 +532,6 @@ def instance_get_all_by_reservation(context, reservation_id): return IMPL.instance_get_all_by_reservation(context, reservation_id) -def instance_get_all_by_network(context, network_id): - """Get all instances belonging to a network.""" - return IMPL.instance_get_all_by_network(context, network_id) - - def instance_get_by_fixed_ip(context, address): """Get an instance for a fixed ip by address.""" return IMPL.instance_get_by_fixed_ip(context, address) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 3194a5231..b99667afc 100755 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1417,20 +1417,6 @@ def instance_get_all_by_reservation(context, reservation_id): all() -def instance_get_all_by_network(context, network_id): - session = get_session() - return session.query(models.Instance).\ - options(joinedload_all('fixed_ips.floating_ips')).\ - options(joinedload('virtual_interfaces')).\ - options(joinedload('security_groups')).\ - options(joinedload_all('fixed_ips.network')).\ - options(joinedload('metadata')).\ - options(joinedload('instance_type')).\ - filter_by(deleted=can_read_deleted(context)).\ - filter_by(network_id=network_id).\ - all() - - @require_context def instance_get_by_fixed_ip(context, address): """Return instance ref by exact match of FixedIP""" diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 4ebc3df23..c26e3574e 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -517,11 +517,13 @@ def get_dhcp_opts(context, network_ref): hosts = [] default_gateway_network_node = dict() network_id = network_ref['id'] - instance_refs = db.instance_get_all_by_network(context, network_id) + instance_set = set() ips_ref = db.network_get_associated_fixed_ips(context, network_id) - for instance_ref in instance_refs: - instance_id = instance_ref['id'] + for fixed_ip_ref in ips_ref: + instance_set.add(fixed_ip_ref['instance_id']) + + for instance_id in instance_set: # nic number is decided by xxx from this function in xxx function vifs = db.virtual_interface_get_by_instance(context, instance_id) if vifs == None: diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index 93d9b02c9..3f44fbd05 100755 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -18,10 +18,10 @@ from nova import context from nova import db from nova import exception +from nova import flags from nova import log as logging from nova import test from nova import utils -from nova import flags from nova.network import manager as network_manager import mox @@ -44,7 +44,9 @@ instances = [{'id': 0, addresses = [{"address": "10.0.0.1"}, {"address": "10.0.0.2"}, {"address": "10.0.0.3"}, - {"address": "10.0.0.4"}] + {"address": "10.0.0.4"}, + {"address": "10.0.0.5"}, + {"address": "10.0.0.6"}] networks = [{'id': 0, @@ -128,6 +130,24 @@ fixed_ips = [{'id': 0, 'virtual_interface_id': 3, 'virtual_interface': addresses[3], 'instance': instances[1], + 'floating_ips': []}, + {'id': 4, + 'network_id': 0, + 'address': '192.168.0.102', + 'instance_id': 0, + 'allocated': True, + 'virtual_interface_id': 4, + 'virtual_interface': addresses[4], + 'instance': instances[0], + 'floating_ips': []}, + {'id': 5, + 'network_id': 1, + 'address': '192.168.1.102', + 'instance_id': 1, + 'allocated': True, + 'virtual_interface_id': 5, + 'virtual_interface': addresses[5], + 'instance': instances[1], 'floating_ips': []}] @@ -154,6 +174,18 @@ vifs = [{'id': 0, 'uuid': '00000000-0000-0000-0000-0000000000000003', 'network_id': 0, 'network': networks[0], + 'instance_id': 1}, + {'id': 4, + 'address': 'DE:AD:BE:EF:00:04', + 'uuid': '00000000-0000-0000-0000-0000000000000004', + 'network_id': 0, + 'network': networks[0], + 'instance_id': 0}, + {'id': 5, + 'address': 'DE:AD:BE:EF:00:05', + 'uuid': '00000000-0000-0000-0000-0000000000000005', + 'network_id': 1, + 'network': networks[1], 'instance_id': 1}] @@ -167,7 +199,6 @@ class LinuxNetworkTestCase(test.TestCase): def test_update_dhcp_for_nw00(self): self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') - self.mox.StubOutWithMock(db, 'instance_get_all_by_network') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') db.network_get_associated_fixed_ips(mox.IgnoreArg(), @@ -175,9 +206,6 @@ class LinuxNetworkTestCase(test.TestCase): .AndReturn([fixed_ips[0], fixed_ips[3]]) - db.instance_get_all_by_network(mox.IgnoreArg(), - mox.IgnoreArg())\ - .AndReturn(instances) db.network_get_associated_fixed_ips(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([fixed_ips[0], @@ -194,7 +222,6 @@ class LinuxNetworkTestCase(test.TestCase): def test_update_dhcp_for_nw01(self): self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') - self.mox.StubOutWithMock(db, 'instance_get_all_by_network') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') db.network_get_associated_fixed_ips(mox.IgnoreArg(), @@ -202,9 +229,6 @@ class LinuxNetworkTestCase(test.TestCase): .AndReturn([fixed_ips[1], fixed_ips[2]]) - db.instance_get_all_by_network(mox.IgnoreArg(), - mox.IgnoreArg())\ - .AndReturn(instances) db.network_get_associated_fixed_ips(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([fixed_ips[1], @@ -257,27 +281,27 @@ class LinuxNetworkTestCase(test.TestCase): def test_get_dhcp_opts_for_nw00(self): self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') - self.mox.StubOutWithMock(db, 'instance_get_all_by_network') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') - db.instance_get_all_by_network(mox.IgnoreArg(), - mox.IgnoreArg())\ - .AndReturn(instances) db.network_get_associated_fixed_ips(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([fixed_ips[0], - fixed_ips[3]]) + fixed_ips[3], + fixed_ips[4]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([vifs[0], - vifs[1]]) + vifs[1], + vifs[4]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([vifs[2], - vifs[3]]) + vifs[3], + vifs[5]]) self.mox.ReplayAll() expected_opts = '\n'\ + '\n'\ '' actual_opts = self.driver.get_dhcp_opts(None, networks[0]) @@ -285,28 +309,28 @@ class LinuxNetworkTestCase(test.TestCase): def test_get_dhcp_opts_for_nw01(self): self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') - self.mox.StubOutWithMock(db, 'instance_get_all_by_network') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') - db.instance_get_all_by_network(mox.IgnoreArg(), - mox.IgnoreArg())\ - .AndReturn(instances) db.network_get_associated_fixed_ips(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([fixed_ips[1], - fixed_ips[2]]) + fixed_ips[2], + fixed_ips[5]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([vifs[0], - vifs[1]]) + vifs[1], + vifs[4]]) db.virtual_interface_get_by_instance(mox.IgnoreArg(), mox.IgnoreArg())\ .AndReturn([vifs[2], - vifs[3]]) + vifs[3], + vifs[5]]) self.mox.ReplayAll() expected_opts = 'NW-i00000000-1,3\n'\ - 'NW-i00000001-0,3' + 'NW-i00000001-0,3\n'\ + 'NW-i00000002-1,3' actual_opts = self.driver.get_dhcp_opts(None, networks[1]) self.assertEquals(actual_opts, expected_opts) -- cgit From 00c3cbe8621918fdd60f9068df2a41db52a7f76f Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Tue, 6 Sep 2011 10:20:28 +0900 Subject: revert codes for db --- nova/db/api.py | 0 nova/db/sqlalchemy/api.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 nova/db/api.py mode change 100755 => 100644 nova/db/sqlalchemy/api.py (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py old mode 100755 new mode 100644 diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py old mode 100755 new mode 100644 -- cgit From d4668c7351fab83930a6fe5ee8f67dabe8027b48 Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Tue, 6 Sep 2011 13:57:14 +0900 Subject: fix a mistaking of deletion in ensure_floating_forward --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index c26e3574e..48438760a 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -438,7 +438,7 @@ def ensure_vpn_forward(public_ip, port, private_ip): def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule.""" - for chain, rule in floating_forward_rules(floaing_ip, fixed_ip): + for chain, rule in floating_forward_rules(floating_ip, fixed_ip): iptables_manager.ipv4['nat'].add_rule(chain, rule) iptables_manager.apply() -- cgit From 6c55199d3ec900cea771e4f8077c87efa968c2f0 Mon Sep 17 00:00:00 2001 From: Keisuke Tagami Date: Tue, 6 Sep 2011 17:20:10 +0900 Subject: fix a mistaking of dataset and expected values on small test. --- nova/tests/test_linux_net.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index 3f44fbd05..be6faa07b 100755 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -114,7 +114,7 @@ fixed_ips = [{'id': 0, 'instance': instances[0], 'floating_ips': []}, {'id': 2, - 'network_id': 0, + 'network_id': 1, 'address': '192.168.0.101', 'instance_id': 1, 'allocated': True, @@ -123,7 +123,7 @@ fixed_ips = [{'id': 0, 'instance': instances[1], 'floating_ips': []}, {'id': 3, - 'network_id': 1, + 'network_id': 0, 'address': '192.168.1.101', 'instance_id': 1, 'allocated': True, @@ -256,7 +256,7 @@ class LinuxNetworkTestCase(test.TestCase): "10.0.0.1,fake_instance00.novalocal,"\ "192.168.0.100,net:NW-i00000000-0\n"\ "10.0.0.4,fake_instance01.novalocal,"\ - "192.168.1.101,net:NW-i00000001-1" + "192.168.1.101,net:NW-i00000001-0" actual_hosts = self.driver.get_dhcp_hosts(None, networks[1]) self.assertEquals(actual_hosts, expected) @@ -274,7 +274,7 @@ class LinuxNetworkTestCase(test.TestCase): "10.0.0.2,fake_instance00.novalocal,"\ "192.168.1.100,net:NW-i00000000-1\n"\ "10.0.0.3,fake_instance01.novalocal,"\ - "192.168.0.101,net:NW-i00000001-0" + "192.168.0.101,net:NW-i00000001-1" actual_hosts = self.driver.get_dhcp_hosts(None, networks[0]) self.assertEquals(actual_hosts, expected) @@ -301,7 +301,7 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() expected_opts = '\n'\ - '\n'\ + 'NW-i00000001-0,3\n'\ '' actual_opts = self.driver.get_dhcp_opts(None, networks[0]) @@ -329,8 +329,8 @@ class LinuxNetworkTestCase(test.TestCase): self.mox.ReplayAll() expected_opts = 'NW-i00000000-1,3\n'\ - 'NW-i00000001-0,3\n'\ - 'NW-i00000002-1,3' + '\n'\ + '' actual_opts = self.driver.get_dhcp_opts(None, networks[1]) self.assertEquals(actual_opts, expected_opts) -- cgit From 0589ecebb5e610ba6a6787fada14e96af92361c2 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 6 Sep 2011 14:26:02 -0500 Subject: Set flat_injected to False by default. --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index e6b30d1a0..6120137ce 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -74,7 +74,7 @@ flags.DEFINE_string('flat_network_bridge', None, 'Bridge for simple network instances') flags.DEFINE_string('flat_network_dns', '8.8.4.4', 'Dns for simple network') -flags.DEFINE_bool('flat_injected', True, +flags.DEFINE_bool('flat_injected', False, 'Whether to attempt to inject network setup into guest') flags.DEFINE_string('flat_interface', None, 'FlatDhcp will bridge into this interface if set') -- cgit From 27b052c1aa2b5ae191c2e2986788c152eef9c221 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Tue, 6 Sep 2011 14:36:58 -0500 Subject: novaclient v1_0 has an ipgroups argument, but novaclient v1_1 doesn't --- nova/scheduler/abstract_scheduler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py index 7f17b642f..be6267d3a 100644 --- a/nova/scheduler/abstract_scheduler.py +++ b/nova/scheduler/abstract_scheduler.py @@ -110,7 +110,6 @@ class AbstractScheduler(driver.Scheduler): flavor_id = instance_type['flavorid'] reservation_id = instance_properties['reservation_id'] files = kwargs['injected_files'] - ipgroup = None # Not supported in OS API ... yet child_zone = zone_info['child_zone'] child_blob = zone_info['child_blob'] zone = db.zone_get(context, child_zone) @@ -124,8 +123,9 @@ class AbstractScheduler(driver.Scheduler): except novaclient_exceptions.BadRequest, e: raise exception.NotAuthorized(_("Bad credentials attempting " "to talk to zone at %(url)s.") % locals()) - nova.servers.create(name, image_ref, flavor_id, ipgroup, meta, files, - child_blob, reservation_id=reservation_id) + nova.servers.create(name, image_ref, flavor_id, + meta=meta, files=files, zone_blob=child_blob, + reservation_id=reservation_id) def _provision_resource_from_blob(self, context, build_plan_item, instance_id, request_spec, kwargs): -- cgit From 1f3856ffb92ab690b1d630deb6fa025ae74348f7 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 6 Sep 2011 12:48:41 -0700 Subject: revert changes to display description --- nova/api/openstack/create_instance_helper.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 9b2928bc8..29e071609 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -161,10 +161,6 @@ class CreateInstanceHelper(object): 'config_drive': config_drive, 'password': password} - # NOTE(vish): This is solely for compatibility with anything - # that is using the display description field. - metadata = server_dict.get('metadata') or {} - display_description = metadata.get('description') or name return (extra_values, create_method(context, inst_type, @@ -172,9 +168,9 @@ class CreateInstanceHelper(object): kernel_id=kernel_id, ramdisk_id=ramdisk_id, display_name=name, - display_description=display_description, + display_description=name, key_name=key_name, - metadata=metadata, + metadata=server_dict.get('metadata', {}), access_ip_v4=server_dict.get('accessIPv4'), access_ip_v6=server_dict.get('accessIPv6'), injected_files=injected_files, -- cgit From 9b2885076d2ed438fb3189b8528f5bec6a2cda4d Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Tue, 6 Sep 2011 15:02:04 -0500 Subject: Fix a misspelling of "exception" --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index e6b30d1a0..050cec250 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -448,7 +448,7 @@ class NetworkManager(manager.SchedulerDependentManager): try: fixed_ips = kwargs.get('fixed_ips') or \ self.db.fixed_ip_get_by_instance(context, instance_id) - except exceptions.FixedIpNotFoundForInstance: + except exception.FixedIpNotFoundForInstance: fixed_ips = [] LOG.debug(_("network deallocation for instance |%s|"), instance_id, context=context) -- cgit From 41f3d157c917255683ae23313704f357e061911c Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 6 Sep 2011 16:41:35 -0500 Subject: Fixed unit test. --- nova/tests/test_xenapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 45dad3516..91b4161b0 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -494,6 +494,7 @@ class XenAPIVMTestCase(test.TestCase): self.check_vm_params_for_linux_with_external_kernel() def test_spawn_netinject_file(self): + self.flags(flat_injected=True) db_fakes.stub_out_db_instance_api(self.stubs, injected=True) self._tee_executed = False @@ -611,7 +612,6 @@ class XenAPIVMTestCase(test.TestCase): str(3 * 1024)) def test_rescue(self): - self.flags(flat_injected=False) instance = self._create_instance() conn = xenapi_conn.get_connection(False) conn.rescue(self.context, instance, None, []) -- cgit From 53357516226a1c00217c742eb512b7efc0f574b2 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Tue, 6 Sep 2011 18:01:57 -0700 Subject: Added use_single_default_gateway to switch from multiple default gateways to single default gateway --- nova/network/linux_net.py | 72 ++++++++++++++++++++------------------------ nova/tests/test_linux_net.py | 17 +++-------- 2 files changed, 38 insertions(+), 51 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 48438760a..affdde701 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -68,6 +68,9 @@ flags.DEFINE_string('linuxnet_interface_driver', 'Driver used to create ethernet devices.') flags.DEFINE_string('linuxnet_ovs_integration_bridge', 'br-int', 'Name of Open vSwitch bridge used with linuxnet') +flags.DEFINE_bool('use_single_default_gateway', + False, 'Use single default gateway. Only first nic of vm' + ' will get default gateway from dhcp server') binary_name = os.path.basename(inspect.stack()[-1][1]) @@ -513,35 +516,27 @@ def get_dhcp_hosts(context, network_ref): def get_dhcp_opts(context, network_ref): """Get network's hosts config in dhcp-opts format.""" - # create a decision dictionary for default gateway for eache instance hosts = [] - default_gateway_network_node = dict() - network_id = network_ref['id'] - instance_set = set() - ips_ref = db.network_get_associated_fixed_ips(context, network_id) - - for fixed_ip_ref in ips_ref: - instance_set.add(fixed_ip_ref['instance_id']) - - for instance_id in instance_set: - # nic number is decided by xxx from this function in xxx function - vifs = db.virtual_interface_get_by_instance(context, instance_id) - if vifs == None: - default_gateway_network_node[instance_id] = None - continue - # offer a default gateway to the first virtual interface of instance - first_vif = vifs[0] - default_gateway_network_node[instance_id] = first_vif['network_id'] - - for fixed_ip_ref in ips_ref: - instance_id = fixed_ip_ref['instance_id'] - if instance_id in default_gateway_network_node: - target_network_id = default_gateway_network_node[instance_id] - if target_network_id == fixed_ip_ref['network_id']: - hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=True)) - else: - hosts.append(_host_dhcp_opts(fixed_ip_ref, gw=None)) - + ips_ref = db.network_get_associated_fixed_ips(context, network_ref['id']) + + if ips_ref: + #set of instance ids + instance_set = set([fixed_ip_ref['instance_id'] + for fixed_ip_ref in ips_ref]) + default_gw_network_node = {} + for instance_id in instance_set: + vifs = db.virtual_interface_get_by_instance(context, instance_id) + if vifs: + #offer a default gateway to the first virtual interface + default_gw_network_node[instance_id] = vifs[0]['network_id'] + + for fixed_ip_ref in ips_ref: + instance_id = fixed_ip_ref['instance_id'] + if instance_id in default_gw_network_node: + target_network_id = default_gw_network_node[instance_id] + # we don't want default gateway for this fixed ip + if target_network_id != fixed_ip_ref['network_id']: + hosts.append(_host_dhcp_opts(fixed_ip_ref)) return '\n'.join(hosts) @@ -560,13 +555,14 @@ def update_dhcp(context, dev, network_ref): with open(conffile, 'w') as f: f.write(get_dhcp_hosts(context, network_ref)) - optsfile = _dhcp_file(dev, 'opts') - with open(optsfile, 'w') as f: - f.write(get_dhcp_opts(context, network_ref)) + if FLAGS.use_single_default_gateway: + optsfile = _dhcp_file(dev, 'opts') + with open(optsfile, 'w') as f: + f.write(get_dhcp_opts(context, network_ref)) + os.chmod(optsfile, 0644) # Make sure dnsmasq can actually read it (it setuid()s to "nobody") os.chmod(conffile, 0644) - os.chmod(optsfile, 0644) pid = _dnsmasq_pid_for(dev) @@ -598,11 +594,13 @@ def update_dhcp(context, dev, network_ref): '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(network_ref['cidr'])), '--dhcp-hostsfile=%s' % _dhcp_file(dev, 'conf'), '--dhcp-script=%s' % FLAGS.dhcpbridge, - '--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts'), '--leasefile-ro'] if FLAGS.dns_server: cmd += ['-h', '-R', '--server=%s' % FLAGS.dns_server] + if FLAGS.use_single_default_gateway: + cmd += ['--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts')] + _execute(*cmd, run_as_root=True) @@ -681,13 +679,9 @@ def _host_dhcp(fixed_ip_ref): "net:" + _host_dhcp_network(fixed_ip_ref)) -def _host_dhcp_opts(fixed_ip_ref, gw): +def _host_dhcp_opts(fixed_ip_ref): """Return a host string for an address in dhcp-host format.""" - if not gw: - return '%s,%s' % (_host_dhcp_network(fixed_ip_ref), - 3) - else: - return '' + return '%s,%s' % (_host_dhcp_network(fixed_ip_ref), 3) def _execute(*cmd, **kwargs): diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index be6faa07b..4c72f1e0f 100755 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -23,6 +23,7 @@ from nova import log as logging from nova import test from nova import utils from nova.network import manager as network_manager +from nova.network import linux_net import mox @@ -194,6 +195,7 @@ class LinuxNetworkTestCase(test.TestCase): def setUp(self): super(LinuxNetworkTestCase, self).setUp() network_driver = FLAGS.network_driver + self.flags(use_single_default_gateway=True) self.driver = utils.import_object(network_driver) self.driver.db = db @@ -300,9 +302,7 @@ class LinuxNetworkTestCase(test.TestCase): vifs[5]]) self.mox.ReplayAll() - expected_opts = '\n'\ - 'NW-i00000001-0,3\n'\ - '' + expected_opts = 'NW-i00000001-0,3' actual_opts = self.driver.get_dhcp_opts(None, networks[0]) self.assertEquals(actual_opts, expected_opts) @@ -328,19 +328,12 @@ class LinuxNetworkTestCase(test.TestCase): vifs[5]]) self.mox.ReplayAll() - expected_opts = 'NW-i00000000-1,3\n'\ - '\n'\ - '' + expected_opts = "NW-i00000000-1,3" actual_opts = self.driver.get_dhcp_opts(None, networks[1]) self.assertEquals(actual_opts, expected_opts) - def test_dhcp_opts_default_gateway_network(self): - expected = "" - actual = self.driver._host_dhcp_opts(fixed_ips[0], True) - self.assertEquals(actual, expected) - def test_dhcp_opts_not_default_gateway_network(self): expected = "NW-i00000000-0,3" - actual = self.driver._host_dhcp_opts(fixed_ips[0], False) + actual = self.driver._host_dhcp_opts(fixed_ips[0]) self.assertEquals(actual, expected) -- cgit From c1763deb23dc6dcf7ca4f32aafde47501a87083f Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 7 Sep 2011 11:11:19 -0500 Subject: updated floating_ip generation --- nova/tests/fake_network.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 99b027cf3..19be83bc9 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -14,6 +14,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from itertools import islice from nova import db from nova import flags @@ -68,8 +69,8 @@ def fixed_ips(num_networks, num_ips, num_floating_ips=0): for network_index in xrange(num_networks): for ip_index in xrange(num_ips): fixed_ip_id = network_index * num_ips + ip_index - f_ips = [FakeModel(**floating_ips(fixed_ip_id).next()) - for i in xrange(num_floating_ips)] + f_ips = [FakeModel(**floating_ip_dict) for floating_ip_dict + in islice(floating_ips(fixed_ip_id), num_floating_ips)] yield {'id': fixed_ip_id, 'network_id': network_index, 'address': '192.168.%d.1%02d' % (network_index, ip_index), @@ -94,7 +95,7 @@ flavor = {'id': 0, def floating_ips(fixed_ip_id): for i in xrange(154): yield {'id': i, - 'address': '10.10.10.%d' % (i + 100), + 'address': '10.10.%d.%d' % (fixed_ip_id, i + 100), 'fixed_ip_id': fixed_ip_id, 'project_id': None, 'auto_assigned': False} -- cgit From e3cb2c82224fad59c16010c0842ebcfa1ac0dc95 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 7 Sep 2011 12:41:16 -0500 Subject: changed the fixed_ip_generator --- nova/tests/fake_network.py | 110 +++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 39 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 19be83bc9..ee1a4a7c7 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -14,7 +14,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from itertools import islice from nova import db from nova import flags @@ -35,6 +34,17 @@ class FakeModel(dict): return self[name] +flavor = {'id': 0, + 'name': 'fake_flavor', + 'memory_mb': 2048, + 'vcpus': 2, + 'local_gb': 10, + 'flavor_id': 0, + 'swap': 0, + 'rxtx_quota': 0, + 'rxtx_cap': 3} + + def fake_network(network_id, ipv6=None): if ipv6 is None: ipv6 = FLAGS.use_ipv6 @@ -65,42 +75,6 @@ def fake_network(network_id, ipv6=None): return fake_network -def fixed_ips(num_networks, num_ips, num_floating_ips=0): - for network_index in xrange(num_networks): - for ip_index in xrange(num_ips): - fixed_ip_id = network_index * num_ips + ip_index - f_ips = [FakeModel(**floating_ip_dict) for floating_ip_dict - in islice(floating_ips(fixed_ip_id), num_floating_ips)] - yield {'id': fixed_ip_id, - 'network_id': network_index, - 'address': '192.168.%d.1%02d' % (network_index, ip_index), - 'instance_id': 0, - 'allocated': False, - # and since network_id and vif_id happen to be equivalent - 'virtual_interface_id': network_index, - 'floating_ips': f_ips} - - -flavor = {'id': 0, - 'name': 'fake_flavor', - 'memory_mb': 2048, - 'vcpus': 2, - 'local_gb': 10, - 'flavor_id': 0, - 'swap': 0, - 'rxtx_quota': 0, - 'rxtx_cap': 3} - - -def floating_ips(fixed_ip_id): - for i in xrange(154): - yield {'id': i, - 'address': '10.10.%d.%d' % (fixed_ip_id, i + 100), - 'fixed_ip_id': fixed_ip_id, - 'project_id': None, - 'auto_assigned': False} - - def vifs(n): for x in xrange(n): yield {'id': x, @@ -111,6 +85,58 @@ def vifs(n): 'instance_id': 0} +#def fixed_ips(num_networks, num_ips, num_floating_ips=0): +# for network_index in xrange(num_networks): +# for ip_index in xrange(num_ips): +# fixed_ip_id = network_index * num_ips + ip_index +# islice = itertools.islice +# yield {'id': fixed_ip_id, +# 'network_id': network_index, +# 'address': '192.168.%d.1%02d' % (network_index, ip_index), +# 'instance_id': 0, +# 'allocated': False, +# # and since network_id and vif_id happen to be equivalent +# 'virtual_interface_id': network_index, +# 'floating_ips': f_ips} + + +def floating_ip_ids(): + for i in xrange(99): + yield i + + +def fixed_ip_ids(): + for i in xrange(99): + yield i + + +floating_ip_id = floating_ip_ids() +fixed_ip_id = fixed_ip_ids() + + +def next_fixed_ip(network_id, num_floating_ips=0): + next_id = fixed_ip_id.next() + f_ips = [FakeModel(**next_floating_ip(fixed_ip_id)) + for i in xrange(num_floating_ips)] + return {'id': next, + 'network_id': network_id, + 'address': '192.168.%d.1%02d' % (network_id, next_id), + 'instance_id': 0, + 'allocated': False, + # and since network_id and vif_id happen to be equivalent + 'virtual_interface_id': network_id, + 'floating_ips': f_ips} + + +def next_floating_ip(fixed_ip_id): + next_id = floating_ip_id.next() + return {'id': next_id, + 'address': '10.10.10.1%02d' % next_id, + 'fixed_ip_id': fixed_ip_id, + 'project_id': None, + 'auto_assigned': False} + + def ipv4_like(ip, match_string): ip = ip.split('.') match_octets = match_string.split('.') @@ -123,15 +149,21 @@ def ipv4_like(ip, match_string): return True -def fake_get_instance_nw_info(stubs, num_networks=1, ips_per_vif=2): +def fake_get_instance_nw_info(stubs, num_networks=1, ips_per_vif=2, + floating_ips_per_fixed_ip=0): # stubs is the self.stubs from the test # ips_per_vif is the number of ips each vif will have # num_floating_ips is number of float ips for each fixed ip network = network_manager.FlatManager(host=HOST) network.db = db + # reset the fixed and floating ip generators + floating_ip_id = floating_ip_ids() + fixed_ip_id = fixed_ip_ids() + def fixed_ips_fake(*args, **kwargs): - return list(fixed_ips(num_networks, ips_per_vif)) + return [next_fixed_ip(i, floating_ips_per_fixed_ip) + for i in xrange(num_networks) for j in xrange(ips_per_vif)] def virtual_interfaces_fake(*args, **kwargs): return [vif for vif in vifs(num_networks)] -- cgit From 262b5cf6e8bd577d2b08fb92e6da56f8bcdecd57 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Wed, 7 Sep 2011 13:37:29 -0500 Subject: weigh_hosts() needs to return a list of hosts for the instances, not just a list of hosts --- nova/scheduler/base_scheduler.py | 16 +++++++++++-- nova/tests/scheduler/test_abstract_scheduler.py | 32 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/base_scheduler.py b/nova/scheduler/base_scheduler.py index 35e5af035..e9c078b81 100644 --- a/nova/scheduler/base_scheduler.py +++ b/nova/scheduler/base_scheduler.py @@ -55,5 +55,17 @@ class BaseScheduler(abstract_scheduler.AbstractScheduler): scheduling objectives """ # NOTE(sirp): The default logic is the same as the NoopCostFunction - return [dict(weight=1, hostname=hostname, capabilities=capabilities) - for hostname, capabilities in hosts] + hosts = [dict(weight=1, hostname=hostname, capabilities=capabilities) + for hostname, capabilities in hosts] + + # NOTE(Vek): What we actually need to return is enough hosts + # for all the instances! + num_instances = request_spec.get('num_instances', 1) + instances = [] + while num_instances > len(hosts): + instances.extend(hosts) + num_instances -= len(hosts) + if num_instances > 0: + instances.extend(hosts[:num_instances]) + + return instances diff --git a/nova/tests/scheduler/test_abstract_scheduler.py b/nova/tests/scheduler/test_abstract_scheduler.py index aa97e2344..f47699048 100644 --- a/nova/tests/scheduler/test_abstract_scheduler.py +++ b/nova/tests/scheduler/test_abstract_scheduler.py @@ -65,6 +65,11 @@ class FakeAbstractScheduler(abstract_scheduler.AbstractScheduler): pass +class FakeBaseScheduler(base_scheduler.BaseScheduler): + # No need to stub anything at the moment + pass + + class FakeZoneManager(zone_manager.ZoneManager): def __init__(self): self.service_states = { @@ -365,3 +370,30 @@ class AbstractSchedulerTestCase(test.TestCase): self.assertEqual(fixture._decrypt_blob(test_data), json.dumps(test_data)) + + +class BaseSchedulerTestCase(test.TestCase): + """Test case for Base Scheduler.""" + + def test_weigh_hosts(self): + """ + Try to weigh a short list of hosts and make sure enough + entries for a larger number instances are returned. + """ + + sched = FakeBaseScheduler() + + # Fake out a list of hosts + zm = FakeZoneManager() + hostlist = [(host, services['compute']) + for host, services in zm.service_states + if 'compute' in services] + + # Call weigh_hosts() + num_instances = len(hostlist) * 2 + len(hostlist) / 2 + instlist = sched.weigh_hosts('compute', + dict(num_instances=num_instances), + hostlist) + + # Should be enough entries to cover all instances + self.assertEqual(len(instlist), num_instances) -- cgit From 34baac0f11ff2084caa46a533aad411988e1541e Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 7 Sep 2011 11:41:33 -0700 Subject: Fix for LP Bug #837867 --- nova/network/manager.py | 6 ++--- nova/tests/test_network.py | 58 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 050cec250..d1791f777 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -128,8 +128,8 @@ class RPCAllocateFixedIP(object): """Calls allocate_fixed_ip once for each network.""" green_pool = greenpool.GreenPool() - vpn = kwargs.pop('vpn') - requested_networks = kwargs.pop('requested_networks') + vpn = kwargs.get('vpn') + requested_networks = kwargs.get('requested_networks') for network in networks: address = None @@ -890,7 +890,7 @@ class FlatManager(NetworkManager): def _allocate_fixed_ips(self, context, instance_id, host, networks, **kwargs): """Calls allocate_fixed_ip once for each network.""" - requested_networks = kwargs.pop('requested_networks') + requested_networks = kwargs.get('requested_networks') for network in networks: address = None if requested_networks is not None: diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 25ff940f0..05fca7bc5 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -58,7 +58,7 @@ networks = [{'id': 0, 'dns1': '192.168.0.1', 'dns2': '192.168.0.2', 'vlan': None, - 'host': None, + 'host': HOST, 'project_id': 'fake_project', 'vpn_public_address': '192.168.0.2'}, {'id': 1, @@ -78,7 +78,7 @@ networks = [{'id': 0, 'dns1': '192.168.0.1', 'dns2': '192.168.0.2', 'vlan': None, - 'host': None, + 'host': HOST, 'project_id': 'fake_project', 'vpn_public_address': '192.168.1.2'}] @@ -247,6 +247,34 @@ class FlatNetworkTestCase(test.TestCase): self.network.validate_networks(None, requested_networks) + def test_add_fixed_ip_instance_without_vpn_requested_networks(self): + self.mox.StubOutWithMock(db, 'network_get') + self.mox.StubOutWithMock(db, 'network_update') + self.mox.StubOutWithMock(db, 'fixed_ip_associate_pool') + self.mox.StubOutWithMock(db, 'instance_get') + self.mox.StubOutWithMock(db, + 'virtual_interface_get_by_instance_and_network') + self.mox.StubOutWithMock(db, 'fixed_ip_update') + + db.fixed_ip_update(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + db.virtual_interface_get_by_instance_and_network(mox.IgnoreArg(), + mox.IgnoreArg(), mox.IgnoreArg()).AndReturn({'id': 0}) + + db.instance_get(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn({'security_groups': + [{'id': 0}]}) + db.fixed_ip_associate_pool(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn('192.168.0.101') + db.network_get(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks[0]) + db.network_update(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()) + self.mox.ReplayAll() + self.network.add_fixed_ip_to_instance(self.context, 1, HOST, + networks[0]['id']) + class VlanNetworkTestCase(test.TestCase): def setUp(self): @@ -387,6 +415,32 @@ class VlanNetworkTestCase(test.TestCase): mox.IgnoreArg(), mox.IgnoreArg()) + def test_add_fixed_ip_instance_without_vpn_requested_networks(self): + self.mox.StubOutWithMock(db, 'network_get') + self.mox.StubOutWithMock(db, 'fixed_ip_associate_pool') + self.mox.StubOutWithMock(db, 'instance_get') + self.mox.StubOutWithMock(db, + 'virtual_interface_get_by_instance_and_network') + self.mox.StubOutWithMock(db, 'fixed_ip_update') + + db.fixed_ip_update(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()) + db.virtual_interface_get_by_instance_and_network(mox.IgnoreArg(), + mox.IgnoreArg(), mox.IgnoreArg()).AndReturn({'id': 0}) + + db.instance_get(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn({'security_groups': + [{'id': 0}]}) + db.fixed_ip_associate_pool(mox.IgnoreArg(), + mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn('192.168.0.101') + db.network_get(mox.IgnoreArg(), + mox.IgnoreArg()).AndReturn(networks[0]) + self.mox.ReplayAll() + self.network.add_fixed_ip_to_instance(self.context, 1, HOST, + networks[0]['id']) + class CommonNetworkTestCase(test.TestCase): -- cgit From 3e61268a350ba0ec43d28dfbac6e82503a174dba Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 7 Sep 2011 14:33:43 -0500 Subject: had used wrong variable --- nova/tests/fake_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index ee1a4a7c7..76e1b991d 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -116,7 +116,7 @@ fixed_ip_id = fixed_ip_ids() def next_fixed_ip(network_id, num_floating_ips=0): next_id = fixed_ip_id.next() - f_ips = [FakeModel(**next_floating_ip(fixed_ip_id)) + f_ips = [FakeModel(**next_floating_ip(next_id)) for i in xrange(num_floating_ips)] return {'id': next, 'network_id': network_id, -- cgit From 31ae07f06c71968deb67c5aa1c111fe9e14fb5d8 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 7 Sep 2011 14:36:22 -0500 Subject: forgot _id --- nova/tests/fake_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 76e1b991d..97231bfa1 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -118,7 +118,7 @@ def next_fixed_ip(network_id, num_floating_ips=0): next_id = fixed_ip_id.next() f_ips = [FakeModel(**next_floating_ip(next_id)) for i in xrange(num_floating_ips)] - return {'id': next, + return {'id': next_id, 'network_id': network_id, 'address': '192.168.%d.1%02d' % (network_id, next_id), 'instance_id': 0, -- cgit From a158d5721c18902b290a42bb3874b34e54dbcd7b Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 7 Sep 2011 13:10:05 -0700 Subject: exclude net tag from host_dhcp if use_single_default_gateway flag is set to false --- nova/network/linux_net.py | 17 ++++++++++++----- nova/tests/test_linux_net.py | 6 ++++++ 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index affdde701..7d89b2bcc 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -672,11 +672,18 @@ def _host_dhcp_network(fixed_ip_ref): def _host_dhcp(fixed_ip_ref): """Return a host string for an address in dhcp-host format.""" instance_ref = fixed_ip_ref['instance'] - return '%s,%s.%s,%s,%s' % (fixed_ip_ref['virtual_interface']['address'], - instance_ref['hostname'], - FLAGS.dhcp_domain, - fixed_ip_ref['address'], - "net:" + _host_dhcp_network(fixed_ip_ref)) + vif = fixed_ip_ref['virtual_interface'] + if FLAGS.use_single_default_gateway: + return '%s,%s.%s,%s,%s' % (vif['address'], + instance_ref['hostname'], + FLAGS.dhcp_domain, + fixed_ip_ref['address'], + "net:" + _host_dhcp_network(fixed_ip_ref)) + else: + return '%s,%s.%s,%s' % (vif['address'], + instance_ref['hostname'], + FLAGS.dhcp_domain, + fixed_ip_ref['address']) def _host_dhcp_opts(fixed_ip_ref): diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index 4c72f1e0f..6d0a2b6bd 100755 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -337,3 +337,9 @@ class LinuxNetworkTestCase(test.TestCase): expected = "NW-i00000000-0,3" actual = self.driver._host_dhcp_opts(fixed_ips[0]) self.assertEquals(actual, expected) + + def test_host_dhcp_without_default_gateway_network(self): + self.flags(use_single_default_gateway=False) + expected = ("10.0.0.1,fake_instance00.novalocal,192.168.0.100") + actual = self.driver._host_dhcp(fixed_ips[0]) + self.assertEquals(actual, expected) -- cgit From 4d98408d4ec605ad9a591d5166f2b8ea6e723ecb Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Wed, 7 Sep 2011 13:37:58 -0700 Subject: modified unit tests, set use_single_default_gateway flag to True whereever needed instead of setting it in the init method --- nova/tests/test_linux_net.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_linux_net.py b/nova/tests/test_linux_net.py index 6d0a2b6bd..99577b88e 100755 --- a/nova/tests/test_linux_net.py +++ b/nova/tests/test_linux_net.py @@ -195,11 +195,11 @@ class LinuxNetworkTestCase(test.TestCase): def setUp(self): super(LinuxNetworkTestCase, self).setUp() network_driver = FLAGS.network_driver - self.flags(use_single_default_gateway=True) self.driver = utils.import_object(network_driver) self.driver.db = db def test_update_dhcp_for_nw00(self): + self.flags(use_single_default_gateway=True) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') @@ -223,6 +223,7 @@ class LinuxNetworkTestCase(test.TestCase): self.driver.update_dhcp(None, "eth0", networks[0]) def test_update_dhcp_for_nw01(self): + self.flags(use_single_default_gateway=True) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') self.mox.StubOutWithMock(db, 'virtual_interface_get_by_instance') @@ -246,6 +247,7 @@ class LinuxNetworkTestCase(test.TestCase): self.driver.update_dhcp(None, "eth0", networks[0]) def test_get_dhcp_hosts_for_nw00(self): + self.flags(use_single_default_gateway=True) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') db.network_get_associated_fixed_ips(mox.IgnoreArg(), @@ -264,6 +266,7 @@ class LinuxNetworkTestCase(test.TestCase): self.assertEquals(actual_hosts, expected) def test_get_dhcp_hosts_for_nw01(self): + self.flags(use_single_default_gateway=True) self.mox.StubOutWithMock(db, 'network_get_associated_fixed_ips') db.network_get_associated_fixed_ips(mox.IgnoreArg(), @@ -339,7 +342,6 @@ class LinuxNetworkTestCase(test.TestCase): self.assertEquals(actual, expected) def test_host_dhcp_without_default_gateway_network(self): - self.flags(use_single_default_gateway=False) expected = ("10.0.0.1,fake_instance00.novalocal,192.168.0.100") actual = self.driver._host_dhcp(fixed_ips[0]) self.assertEquals(actual, expected) -- cgit From 591997a76a8395a72c7316207983e1225c9c4a62 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Wed, 7 Sep 2011 20:40:48 +0000 Subject: fix a couple of typos in the added unit test --- nova/tests/scheduler/test_abstract_scheduler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/scheduler/test_abstract_scheduler.py b/nova/tests/scheduler/test_abstract_scheduler.py index f47699048..9bf128b13 100644 --- a/nova/tests/scheduler/test_abstract_scheduler.py +++ b/nova/tests/scheduler/test_abstract_scheduler.py @@ -26,6 +26,7 @@ from nova import test from nova.compute import api as compute_api from nova.scheduler import driver from nova.scheduler import abstract_scheduler +from nova.scheduler import base_scheduler from nova.scheduler import zone_manager @@ -386,7 +387,7 @@ class BaseSchedulerTestCase(test.TestCase): # Fake out a list of hosts zm = FakeZoneManager() hostlist = [(host, services['compute']) - for host, services in zm.service_states + for host, services in zm.service_states.items() if 'compute' in services] # Call weigh_hosts() -- cgit From 3334fabe55c862531e3ced21b211710857c1e087 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 7 Sep 2011 15:48:31 -0500 Subject: removed vestige --- nova/tests/fake_network.py | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 97231bfa1..5e536a6ee 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -85,21 +85,6 @@ def vifs(n): 'instance_id': 0} -#def fixed_ips(num_networks, num_ips, num_floating_ips=0): -# for network_index in xrange(num_networks): -# for ip_index in xrange(num_ips): -# fixed_ip_id = network_index * num_ips + ip_index -# islice = itertools.islice -# yield {'id': fixed_ip_id, -# 'network_id': network_index, -# 'address': '192.168.%d.1%02d' % (network_index, ip_index), -# 'instance_id': 0, -# 'allocated': False, -# # and since network_id and vif_id happen to be equivalent -# 'virtual_interface_id': network_index, -# 'floating_ips': f_ips} - - def floating_ip_ids(): for i in xrange(99): yield i -- cgit From 2bed69e61aefdc8e2aa7eeb31fe9f338e912a01d Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 7 Sep 2011 16:06:35 -0500 Subject: properly handle the id resetters --- nova/tests/fake_network.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 5e536a6ee..1ecb99b31 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -143,6 +143,7 @@ def fake_get_instance_nw_info(stubs, num_networks=1, ips_per_vif=2, network.db = db # reset the fixed and floating ip generators + global floating_ip_id, fixed_ip_id floating_ip_id = floating_ip_ids() fixed_ip_id = fixed_ip_ids() -- cgit From d2b9299408f07f995fffc8b8559f52ee6adeeaad Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Wed, 7 Sep 2011 18:01:52 -0500 Subject: spread-first strategy --- nova/scheduler/abstract_scheduler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py index 7f17b642f..a81fa53cd 100644 --- a/nova/scheduler/abstract_scheduler.py +++ b/nova/scheduler/abstract_scheduler.py @@ -20,8 +20,9 @@ customize the behavior: filter_hosts() and weigh_hosts(). The default behavior is to simply select all hosts and weight them the same. """ -import operator import json +import operator +import random import M2Crypto @@ -40,6 +41,8 @@ from nova.scheduler import api from nova.scheduler import driver FLAGS = flags.FLAGS +flags.DEFINE_boolean('spread_first', False, + 'Use a spread-first zone scheduler strategy') LOG = logging.getLogger('nova.scheduler.abstract_scheduler') -- cgit From ad25b7aa2ad744607b20432d635f70cc645cc6f6 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Wed, 7 Sep 2011 18:08:39 -0500 Subject: actually shuffle the weighted_hosts list... --- nova/scheduler/abstract_scheduler.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py index a81fa53cd..b4c2bf4f1 100644 --- a/nova/scheduler/abstract_scheduler.py +++ b/nova/scheduler/abstract_scheduler.py @@ -295,6 +295,8 @@ class AbstractScheduler(driver.Scheduler): "child_zone": child_zone, "child_blob": weighting["blob"]} weighted_hosts.append(host_dict) + if FLAGS.spread_first: + random.shuffle(weighted_hosts) weighted_hosts.sort(key=operator.itemgetter('weight')) return weighted_hosts -- cgit From b6d454762d7fdf9b202d8a580dd9bfdf069a5e80 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Wed, 7 Sep 2011 16:38:41 -0700 Subject: fix for lp844364: improve check for fixed_ip association --- nova/api/openstack/contrib/floating_ips.py | 4 +- .../api/openstack/contrib/test_floating_ips.py | 78 +++++++++++++++++++++- 2 files changed, 77 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index d1add8f83..9ceb5858f 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -107,7 +107,7 @@ class FloatingIPController(object): context = req.environ['nova.context'] floating_ip = self.network_api.get_floating_ip(context, id) - if 'fixed_ip' in floating_ip: + if floating_ip['fixed_ip']: self.network_api.disassociate_floating_ip(context, floating_ip['address']) @@ -161,7 +161,7 @@ class Floating_ips(extensions.ExtensionDescriptor): raise webob.exc.HTTPBadRequest(explanation=msg) floating_ip = self.network_api.get_floating_ip_by_ip(context, address) - if 'fixed_ip' in floating_ip: + if floating_ip['fixed_ip']: self.network_api.disassociate_floating_ip(context, address) return webob.Response(status_int=202) diff --git a/nova/tests/api/openstack/contrib/test_floating_ips.py b/nova/tests/api/openstack/contrib/test_floating_ips.py index 642f2b841..0744f0a11 100644 --- a/nova/tests/api/openstack/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/contrib/test_floating_ips.py @@ -212,11 +212,45 @@ class FloatingIpTest(test.TestCase): "fixed_ip": None} self.assertEqual(ip, expected) - def test_floating_ip_release(self): + def test_floating_ip_release_associated(self): + self.disassociated = False + + def get_floating_ip(ignore, context, id): + return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': {'id': 1}} + + def disassociate(ignore, context, floating_address): + self.disassociated = True + + self.stubs.Set(network.api.API, "get_floating_ip", + get_floating_ip) + self.stubs.Set(network.api.API, "disassociate_floating_ip", + disassociate) + req = webob.Request.blank('/v1.1/123/os-floating-ips/1') + req.method = 'DELETE' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) + self.assertTrue(self.disassociated) + + def test_floating_ip_release_disassociated(self): + self.disassociated = False + + def fake_get_floating_ip(ignore, context, id): + return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': None} + + def fake_disassociate(ignore, context, floating_address): + self.disassociated = True + + self.stubs.Set(network.api.API, "get_floating_ip", + fake_get_floating_ip) + self.stubs.Set(network.api.API, "disassociate_floating_ip", + fake_disassociate) req = webob.Request.blank('/v1.1/123/os-floating-ips/1') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + self.assertFalse(self.disassociated) def test_add_floating_ip_to_instance(self): self.stubs.Set(network.api.API, "associate_floating_ip", @@ -289,8 +323,45 @@ class FloatingIpTest(test.TestCase): self.assertEqual(resp.status_int, 202) self.assertTrue(self.disassociated) - def test_remove_floating_ip_from_instance(self): - body = dict(removeFloatingIp=dict(address='11.0.0.1')) + def test_remove_associated_floating_ip_from_instance(self): + self.disassociated = False + + def fake_get_floating_ip_by_ip(ignore, context, ip): + return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': {'id': 1}} + + def fake_disassociate(ignore, context, floating_address): + self.disassociated = True + + self.stubs.Set(network.api.API, "get_floating_ip_by_ip", + fake_get_floating_ip_by_ip) + self.stubs.Set(network.api.API, "disassociate_floating_ip", + fake_disassociate) + body = dict(removeFloatingIp=dict(address='10.10.10.10')) + req = webob.Request.blank('/v1.1/123/servers/test_inst/action') + req.method = "POST" + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) + self.assertTrue(self.disassociated) + + def test_remove_disassociated_floating_ip_from_instance(self): + self.disassociated = False + + def fake_get_floating_ip_by_ip(ignore, context, ip): + return {'id': 1, 'address': '10.10.10.10', + 'fixed_ip': None} + + def fake_disassociate(ignore, context, floating_address): + self.disassociated = True + + self.stubs.Set(network.api.API, "get_floating_ip_by_ip", + fake_get_floating_ip_by_ip) + self.stubs.Set(network.api.API, "disassociate_floating_ip", + fake_disassociate) + body = dict(removeFloatingIp=dict(address='10.10.10.10')) req = webob.Request.blank('/v1.1/123/servers/test_inst/action') req.method = "POST" req.body = json.dumps(body) @@ -298,6 +369,7 @@ class FloatingIpTest(test.TestCase): resp = req.get_response(fakes.wsgi_app()) self.assertEqual(resp.status_int, 202) + self.assertFalse(self.disassociated) def test_bad_address_param_in_remove_floating_ip(self): body = dict(removeFloatingIp=dict(badparam='11.0.0.1')) -- cgit From b3bc07d4dcb43b6e070a136eb5532def6b623e6e Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 7 Sep 2011 20:40:47 -0700 Subject: remove the short circuit in abstract scheduler when no local hosts are available --- nova/scheduler/abstract_scheduler.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py index 7f17b642f..cb8db599f 100644 --- a/nova/scheduler/abstract_scheduler.py +++ b/nova/scheduler/abstract_scheduler.py @@ -269,9 +269,6 @@ class AbstractScheduler(driver.Scheduler): # Filter local hosts based on requirements ... filtered_hosts = self.filter_hosts(topic, request_spec, unfiltered_hosts) - if not filtered_hosts: - LOG.warn(_("No hosts available")) - return [] # weigh the selected hosts. # weighted_hosts = [{weight=weight, hostname=hostname, -- cgit From 5426687825cd64adf0524de2808eed1cca15f521 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 7 Sep 2011 20:49:42 -0700 Subject: added test to cover case where no local hosts are available but child hosts are --- nova/tests/scheduler/test_abstract_scheduler.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'nova') diff --git a/nova/tests/scheduler/test_abstract_scheduler.py b/nova/tests/scheduler/test_abstract_scheduler.py index aa97e2344..a65d15e43 100644 --- a/nova/tests/scheduler/test_abstract_scheduler.py +++ b/nova/tests/scheduler/test_abstract_scheduler.py @@ -365,3 +365,25 @@ class AbstractSchedulerTestCase(test.TestCase): self.assertEqual(fixture._decrypt_blob(test_data), json.dumps(test_data)) + + def test_empty_local_hosts(self): + """ + Create a nested set of FakeZones, try to build multiple instances + and ensure that a select call returns the appropriate build plan. + """ + sched = FakeAbstractScheduler() + self.stubs.Set(sched, '_call_zone_method', fake_call_zone_method) + self.stubs.Set(nova.db, 'zone_get_all', fake_zone_get_all) + + zm = FakeZoneManager() + # patch this to have no local hosts + zm.service_states = {} + sched.set_zone_manager(zm) + + fake_context = {} + build_plan = sched.select(fake_context, + {'instance_type': {'memory_mb': 512}, + 'num_instances': 4}) + + # 0 from local zones, 12 from remotes + self.assertEqual(12, len(build_plan)) -- cgit From b13ca667bdd3303bbfcd4e58cc6d773cea09661d Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 7 Sep 2011 21:48:11 -0700 Subject: catch exceptions from novaclient when talking to child zones. store them and re-raise if no other child zones return any results. If no exceptions are raised but no results are returned, raise a NotFound exception. --- nova/scheduler/api.py | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 55cea5f8f..a5124678d 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -103,22 +103,6 @@ def update_service_capabilities(context, service_name, host, capabilities): return rpc.fanout_cast(context, 'scheduler', kwargs) -def _wrap_method(function, self): - """Wrap method to supply self.""" - def _wrap(*args, **kwargs): - return function(self, *args, **kwargs) - return _wrap - - -def _process(func, zone): - """Worker stub for green thread pool. Give the worker - an authenticated nova client and zone info.""" - nova = novaclient.Client(zone.username, zone.password, None, - zone.api_url) - nova.authenticate() - return func(nova, zone) - - def call_zone_method(context, method_name, errors_to_ignore=None, novaclient_collection_name='zones', zones=None, *args, **kwargs): @@ -166,6 +150,28 @@ def child_zone_helper(zone_list, func): For example, if you are calling server.pause(), the list will be whatever the response from server.pause() is. One entry per child zone called.""" + + def _wrap_method(function, arg1): + """Wrap method to supply an argument.""" + def _wrap(*args, **kwargs): + return function(arg1, *args, **kwargs) + return _wrap + + def _process(func, zone): + """Worker stub for green thread pool. Give the worker + an authenticated nova client and zone info.""" + try: + nova = novaclient.Client(zone.username, zone.password, None, + zone.api_url) + nova.authenticate() + except novaclient_exceptions.BadRequest, e: + url = zone.api_url + LOG.warn(_("Failed request to zone; URL=%(url)s: %(e)s") + % locals()) + return e + else: + return func(nova, zone) + green_pool = greenpool.GreenPool() return [result for result in green_pool.imap( _wrap_method(_process, func), zone_list)] @@ -260,6 +266,8 @@ class reroute_compute(object): if not FLAGS.enable_zone_routing: raise exception.InstanceNotFound(instance_id=item_uuid) + self.item_uuid = item_uuid + zones = db.zone_get_all(context) if not zones: raise exception.InstanceNotFound(instance_id=item_uuid) @@ -342,9 +350,13 @@ class reroute_compute(object): dict {'server':{k:v}}. Others may return a list of them, like {'servers':[{k,v}]}""" reduced_response = [] + found_exception = None for zone_response in zone_responses: if not zone_response: continue + if isinstance(zone_response, BaseException): + found_exception = zone_response + continue server = zone_response.__dict__ @@ -355,7 +367,9 @@ class reroute_compute(object): reduced_response.append(dict(server=server)) if reduced_response: return reduced_response[0] # first for now. - return {} + elif found_exception: + raise found_exception + exception.InstanceNotFound(instance_id=self.item_uuid) def redirect_handler(f): -- cgit From bc84c1306e9334d4082cadee4dcb5cd14a905afe Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 7 Sep 2011 22:57:36 -0700 Subject: pep8 fix for tests/api/openstack/test_servers.py which is an issue in trunk --- nova/tests/api/openstack/test_servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 2ef687709..d063a60c2 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3615,7 +3615,7 @@ class TestGetKernelRamdiskFromImage(test.TestCase): self.assertRaises(exception.NotFound, self._get_k_r, image_meta) def test_ami_no_ramdisk(self): - """If an ami is missing a ramdisk, return kernel ID and None for + """If an ami is missing a ramdisk, return kernel ID and None for ramdisk ID """ image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami', -- cgit From be645283c85d69e2d3cf4f4eabdbb545aaf139bf Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 7 Sep 2011 23:24:07 -0700 Subject: create a new exception ZoneRequestError to use for returning errors when zone requests couldn't complete --- nova/exception.py | 7 +++++++ nova/scheduler/api.py | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/exception.py b/nova/exception.py index 95d8229b5..aa6609461 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -806,3 +806,10 @@ class CannotResizeToSmallerSize(NovaException): class ImageTooLarge(NovaException): message = _("Image is larger than instance type allows") + + +class ZoneRequestError(Error): + def __init__(self, message=None): + if message is None: + message = _("1 or more Zones could not complete the request") + super(ZoneRequestError, self).__init__(message=message) diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index a5124678d..05685fc15 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -168,7 +168,10 @@ def child_zone_helper(zone_list, func): url = zone.api_url LOG.warn(_("Failed request to zone; URL=%(url)s: %(e)s") % locals()) - return e + # This is being returned instead of raised, so that when results are + # processed in unmarshal_result() after the greenpool.imap completes, + # the exception can be raised there if no other zones had a response. + return exception.ZoneRequestError() else: return func(nova, zone) -- cgit From d1b1f301583fd67050c45f4c863733f251620a9c Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 7 Sep 2011 23:39:39 -0700 Subject: typo trying to raise InstanceNotFound when all zones returned nothing --- nova/scheduler/api.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 05685fc15..6081c3f02 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -168,9 +168,10 @@ def child_zone_helper(zone_list, func): url = zone.api_url LOG.warn(_("Failed request to zone; URL=%(url)s: %(e)s") % locals()) - # This is being returned instead of raised, so that when results are - # processed in unmarshal_result() after the greenpool.imap completes, - # the exception can be raised there if no other zones had a response. + # This is being returned instead of raised, so that when + # results are # processed in unmarshal_result() after the + # greenpool.imap completes, # the exception can be raised + # there if no other zones had a response. return exception.ZoneRequestError() else: return func(nova, zone) @@ -372,7 +373,7 @@ class reroute_compute(object): return reduced_response[0] # first for now. elif found_exception: raise found_exception - exception.InstanceNotFound(instance_id=self.item_uuid) + raise exception.InstanceNotFound(instance_id=self.item_uuid) def redirect_handler(f): -- cgit From be7a081976d37b84d93028673d08ab78bc9d8a73 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 7 Sep 2011 23:45:11 -0700 Subject: comment fix --- nova/scheduler/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/api.py b/nova/scheduler/api.py index 6081c3f02..719437b73 100644 --- a/nova/scheduler/api.py +++ b/nova/scheduler/api.py @@ -169,8 +169,8 @@ def child_zone_helper(zone_list, func): LOG.warn(_("Failed request to zone; URL=%(url)s: %(e)s") % locals()) # This is being returned instead of raised, so that when - # results are # processed in unmarshal_result() after the - # greenpool.imap completes, # the exception can be raised + # results are processed in unmarshal_result() after the + # greenpool.imap completes, the exception can be raised # there if no other zones had a response. return exception.ZoneRequestError() else: -- cgit From 973870c82445d4c1ebbd46f2ba7c3817ae5e7f87 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Thu, 8 Sep 2011 01:09:22 -0700 Subject: added tests for failure cases talking with zones --- nova/tests/scheduler/test_scheduler.py | 102 +++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py index a52dd041a..890348192 100644 --- a/nova/tests/scheduler/test_scheduler.py +++ b/nova/tests/scheduler/test_scheduler.py @@ -963,9 +963,14 @@ class FakeZone(object): self.password = password +ZONE_API_URL1 = "http://1.example.com" +ZONE_API_URL2 = "http://2.example.com" + + def zone_get_all(context): return [ - FakeZone(1, 'http://example.com', 'bob', 'xxx'), + FakeZone(1, ZONE_API_URL1, 'bob', 'xxx'), + FakeZone(2, ZONE_API_URL2, 'bob', 'xxx'), ] @@ -1065,7 +1070,9 @@ class ZoneRedirectTest(test.TestCase): def test_unmarshal_single_server(self): decorator = api.reroute_compute("foo") - self.assertEquals(decorator.unmarshall_result([]), {}) + decorator.item_uuid = 'fake_uuid' + self.assertRaises(exception.InstanceNotFound, + decorator.unmarshall_result, []) self.assertEquals(decorator.unmarshall_result( [FakeResource(dict(a=1, b=2)), ]), dict(server=dict(a=1, b=2))) @@ -1079,6 +1086,90 @@ class ZoneRedirectTest(test.TestCase): [FakeResource(dict(_a=1, manager=2)), ]), dict(server={})) + def test_one_zone_down_no_instances(self): + + def _fake_issue_novaclient_command(nova, zone, *args, **kwargs): + return None + + class FakeNovaClientWithFailure(object): + def __init__(self, username, password, method, api_url): + self.api_url = api_url + + def authenticate(self): + if self.api_url == ZONE_API_URL2: + raise novaclient_exceptions.BadRequest('foo') + + self.stubs.Set(api, '_issue_novaclient_command', + _fake_issue_novaclient_command) + self.stubs.Set(api.novaclient, 'Client', FakeNovaClientWithFailure) + + @api.reroute_compute("get") + def do_get(self, context, uuid): + pass + + self.assertRaises(exception.ZoneRequestError, + do_get, None, {}, FAKE_UUID) + + def test_one_zone_down_got_instance(self): + + def _fake_issue_novaclient_command(nova, zone, *args, **kwargs): + class FakeServer(object): + def __init__(self): + self.id = FAKE_UUID + self.test = '1234' + return FakeServer() + + class FakeNovaClientWithFailure(object): + def __init__(self, username, password, method, api_url): + self.api_url = api_url + + def authenticate(self): + if self.api_url == ZONE_API_URL2: + raise novaclient_exceptions.BadRequest('foo') + + self.stubs.Set(api, '_issue_novaclient_command', + _fake_issue_novaclient_command) + self.stubs.Set(api.novaclient, 'Client', FakeNovaClientWithFailure) + + @api.reroute_compute("get") + def do_get(self, context, uuid): + pass + + try: + do_get(None, {}, FAKE_UUID) + except api.RedirectResult, e: + results = e.results + self.assertIn('server', results) + self.assertEqual(results['server']['id'], FAKE_UUID) + self.assertEqual(results['server']['test'], '1234') + except Exception, e: + self.fail(_("RedirectResult should have been raised")) + else: + self.fail(_("RedirectResult should have been raised")) + + def test_zones_up_no_instances(self): + + def _fake_issue_novaclient_command(nova, zone, *args, **kwargs): + return None + + class FakeNovaClientNoFailure(object): + def __init__(self, username, password, method, api_url): + pass + + def authenticate(self): + return + + self.stubs.Set(api, '_issue_novaclient_command', + _fake_issue_novaclient_command) + self.stubs.Set(api.novaclient, 'Client', FakeNovaClientNoFailure) + + @api.reroute_compute("get") + def do_get(self, context, uuid): + pass + + self.assertRaises(exception.InstanceNotFound, + do_get, None, {}, FAKE_UUID) + class FakeServerCollection(object): def get(self, instance_id): @@ -1097,7 +1188,7 @@ class FakeEmptyServerCollection(object): class FakeNovaClient(object): - def __init__(self, collection): + def __init__(self, collection, *args, **kwargs): self.servers = collection @@ -1162,8 +1253,9 @@ class CallZoneMethodTest(test.TestCase): context = {} method = 'do_something' results = api.call_zone_method(context, method) - expected = [(1, 42)] - self.assertEqual(expected, results) + self.assertEqual(len(results), 2) + self.assertIn((1, 42), results) + self.assertIn((2, 42), results) def test_call_zone_method_not_present(self): context = {} -- cgit From 212ec3af4b11a770d7e4cf7869b1865b0f067e5c Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 8 Sep 2011 10:52:29 -0400 Subject: Clean up shutdown of lxc containers --- nova/virt/disk.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 52b2881e8..12a3a64ca 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -148,16 +148,17 @@ def destroy_container(target, instance, nbd=False): LXC does not support qcow2 images yet. """ + out, err = utils.execute('mount', run_as_root=True) + for loop in out.splitlines(): + if instance['name'] in loop: + device = loop.split()[0] + try: container_dir = '%s/rootfs' % target utils.execute('umount', container_dir, run_as_root=True) - finally: - out, err = utils.execute('losetup', '-a', run_as_root=True) - for loop in out.splitlines(): - if instance['name'] in loop: - device = loop.split(loop, ':') - _unlink_device(device, nbd) - + _unlink_device(device, nbd) + except Exception, exn: + LOG.exception(_('Failed to remove container: %s'), exn) def _link_device(image, nbd): """Link image to device using loopback or nbd""" -- cgit From 6cbbdb909443a33c2af8ddd73b861cd41201fa0b Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 8 Sep 2011 11:32:11 -0400 Subject: adding can_read_deleted back to db api --- nova/db/sqlalchemy/api.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 523258841..1730b4ddb 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1277,6 +1277,10 @@ def instance_get_all_by_filters(context, filters): changes_since = filters['changes-since'] query_prefix = query_prefix.\ filter(models.Instance.updated_at > changes_since) + else: + # filter out deleted instances if no changes-since filter provided + query_prefix = query_prefix.\ + filter_by(deleted=can_read_deleted(context)) if not context.is_admin: # If we're not admin context, add appropriate filter.. -- cgit From 763bf3f1282e3d9723a356d4014a9599601637eb Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 8 Sep 2011 11:43:43 -0500 Subject: Do not attempt to mount the swap VDI for file injection. --- nova/tests/api/openstack/test_servers.py | 2 +- nova/virt/xenapi/vmops.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 2ef687709..d063a60c2 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3615,7 +3615,7 @@ class TestGetKernelRamdiskFromImage(test.TestCase): self.assertRaises(exception.NotFound, self._get_k_r, image_meta) def test_ami_no_ramdisk(self): - """If an ami is missing a ramdisk, return kernel ID and None for + """If an ami is missing a ramdisk, return kernel ID and None for ramdisk ID """ image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami', diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c5f105f40..bb53a52bc 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -188,9 +188,16 @@ class VMOps(object): ramdisk = VMHelper.fetch_image(context, self._session, instance, instance.ramdisk_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']) + + # NOTE(jk0): Since vdi_type may contain either 'os' or 'swap', we + # need to ensure that the 'swap' VDI is not chosen as the mount + # point for file injection. + first_vdi_ref = None + for vdi in vdis: + if vdi['vdi_type'] != 'swap': + # Create the VM ref and attach the first disk + first_vdi_ref = self._session.call_xenapi( + 'VDI.get_by_uuid', vdi['vdi_uuid']) vm_mode = instance.vm_mode and instance.vm_mode.lower() if vm_mode == 'pv': -- cgit From 3f6738b9f07640b0950793975cfc55e62aa3e1ad Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 8 Sep 2011 12:11:39 -0500 Subject: Use .get instead. --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index bb53a52bc..9c138ee41 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -194,7 +194,7 @@ class VMOps(object): # point for file injection. first_vdi_ref = None for vdi in vdis: - if vdi['vdi_type'] != 'swap': + if vdi.get('vdi_type') != 'swap': # Create the VM ref and attach the first disk first_vdi_ref = self._session.call_xenapi( 'VDI.get_by_uuid', vdi['vdi_uuid']) -- cgit From c8a48eec1fb9f205af5cef2b882fc171bcca4d57 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Thu, 8 Sep 2011 19:08:46 +0000 Subject: Add a NOTE() --- nova/scheduler/abstract_scheduler.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'nova') diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py index be6267d3a..1ba10c3a9 100644 --- a/nova/scheduler/abstract_scheduler.py +++ b/nova/scheduler/abstract_scheduler.py @@ -123,6 +123,14 @@ class AbstractScheduler(driver.Scheduler): except novaclient_exceptions.BadRequest, e: raise exception.NotAuthorized(_("Bad credentials attempting " "to talk to zone at %(url)s.") % locals()) + # NOTE(Vek): Novaclient has two different calling conventions + # for this call, depending on whether you're using + # 1.0 or 1.1 API: in 1.0, there's an ipgroups + # argument after flavor_id which isn't present in + # 1.1. To work around this, all the extra + # arguments are passed as keyword arguments + # (there's a reasonable default for ipgroups in the + # novaclient call). nova.servers.create(name, image_ref, flavor_id, meta=meta, files=files, zone_blob=child_blob, reservation_id=reservation_id) -- cgit From 4bf6508a026c62a7aa2423b1910c871ddc3f0916 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Thu, 8 Sep 2011 15:26:44 -0400 Subject: converting fix to just address ec2; updating test --- nova/api/ec2/cloud.py | 4 +++- nova/db/sqlalchemy/api.py | 4 ---- nova/tests/test_cloud.py | 4 +--- 3 files changed, 4 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 049ca6f93..4f7030a5a 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1200,8 +1200,10 @@ class CloudController(object): instances.append(instance) else: try: + # always filter out deleted instances + search_opts['deleted'] = False instances = self.compute_api.get_all(context, - search_opts=search_opts) + search_opts=search_opts) except exception.NotFound: instances = [] for instance in instances: diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 1730b4ddb..523258841 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1277,10 +1277,6 @@ def instance_get_all_by_filters(context, filters): changes_since = filters['changes-since'] query_prefix = query_prefix.\ filter(models.Instance.updated_at > changes_since) - else: - # filter out deleted instances if no changes-since filter provided - query_prefix = query_prefix.\ - filter_by(deleted=can_read_deleted(context)) if not context.is_admin: # If we're not admin context, add appropriate filter.. diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 3fe6a9b42..7fe353b3d 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -486,11 +486,9 @@ class CloudTestCase(test.TestCase): inst2 = db.instance_create(self.context, args2) db.instance_destroy(self.context, inst1.id) result = self.cloud.describe_instances(self.context) + self.assertEqual(len(result['reservationSet']), 1) result1 = result['reservationSet'][0]['instancesSet'] self.assertEqual(result1[0]['instanceId'], - ec2utils.id_to_ec2_id(inst1.id)) - result2 = result['reservationSet'][1]['instancesSet'] - self.assertEqual(result2[0]['instanceId'], ec2utils.id_to_ec2_id(inst2.id)) def _block_device_mapping_create(self, instance_id, mappings): -- cgit From fe355a10ad0a215eb5295e46e6c106221972e7ed Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Thu, 8 Sep 2011 13:18:08 -0700 Subject: make check for fixed_ip association more defensive --- nova/api/openstack/contrib/floating_ips.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 9ceb5858f..d078b26c6 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -107,7 +107,7 @@ class FloatingIPController(object): context = req.environ['nova.context'] floating_ip = self.network_api.get_floating_ip(context, id) - if floating_ip['fixed_ip']: + if floating_ip.get('fixed_ip'): self.network_api.disassociate_floating_ip(context, floating_ip['address']) @@ -161,7 +161,7 @@ class Floating_ips(extensions.ExtensionDescriptor): raise webob.exc.HTTPBadRequest(explanation=msg) floating_ip = self.network_api.get_floating_ip_by_ip(context, address) - if floating_ip['fixed_ip']: + if floating_ip.get('fixed_ip'): self.network_api.disassociate_floating_ip(context, address) return webob.Response(status_int=202) -- cgit From aec647b3b42c4cd56a9509c2d1ac25ff12b0664e Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 8 Sep 2011 16:10:03 -0500 Subject: First pass at adding reboot_type to reboot codepath. --- nova/api/openstack/servers.py | 5 ++--- nova/compute/api.py | 5 +++-- nova/compute/manager.py | 4 ++-- nova/tests/api/openstack/test_servers.py | 2 +- nova/tests/test_compute.py | 15 ++++++++++++--- nova/tests/test_virt_drivers.py | 3 ++- nova/tests/test_vmwareapi.py | 3 ++- nova/virt/driver.py | 3 ++- nova/virt/fake.py | 2 +- nova/virt/hyperv.py | 2 +- nova/virt/vmwareapi_conn.py | 2 +- nova/virt/xenapi/vmops.py | 9 +++++++-- nova/virt/xenapi_conn.py | 4 ++-- 13 files changed, 38 insertions(+), 21 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index d084ac360..f5447edc5 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -334,9 +334,8 @@ class Controller(object): LOG.exception(msg) raise exc.HTTPBadRequest(explanation=msg) try: - # TODO(gundlach): pass reboot_type, support soft reboot in - # virt driver - self.compute_api.reboot(req.environ['nova.context'], id) + self.compute_api.reboot(req.environ['nova.context'], id, + reboot_type) except Exception, e: LOG.exception(_("Error in reboot %s"), e) raise exc.HTTPUnprocessableEntity() diff --git a/nova/compute/api.py b/nova/compute/api.py index 4e2944bb7..b0ea044c5 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1042,13 +1042,14 @@ class API(base.Base): return recv_meta @scheduler_api.reroute_compute("reboot") - def reboot(self, context, instance_id): + def reboot(self, context, instance_id, reboot_type): """Reboot the given instance.""" self.update(context, instance_id, vm_state=vm_states.ACTIVE, task_state=task_states.REBOOTING) - self._cast_compute_message('reboot_instance', context, instance_id) + self._cast_compute_message('reboot_instance', context, instance_id, + reboot_type) @scheduler_api.reroute_compute("rebuild") def rebuild(self, context, instance_id, image_href, admin_password, diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 0477db745..0be12297f 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -579,7 +579,7 @@ class ComputeManager(manager.SchedulerDependentManager): @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @checks_instance_lock - def reboot_instance(self, context, instance_id): + def reboot_instance(self, context, instance_id, reboot_type="SOFT"): """Reboot an instance on this host.""" LOG.audit(_("Rebooting instance %s"), instance_id, context=context) context = context.elevated() @@ -601,7 +601,7 @@ class ComputeManager(manager.SchedulerDependentManager): context=context) network_info = self._get_instance_nw_info(context, instance_ref) - self.driver.reboot(instance_ref, network_info) + self.driver.reboot(instance_ref, network_info, reboot_type) current_power_state = self._get_power_state(context, instance_ref) self._instance_update(context, diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 2ef687709..d063a60c2 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3615,7 +3615,7 @@ class TestGetKernelRamdiskFromImage(test.TestCase): self.assertRaises(exception.NotFound, self._get_k_r, image_meta) def test_ami_no_ramdisk(self): - """If an ami is missing a ramdisk, return kernel ID and None for + """If an ami is missing a ramdisk, return kernel ID and None for ramdisk ID """ image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami', diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 65fdffbd6..4d463572b 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -300,11 +300,20 @@ class ComputeTestCase(test.TestCase): self.compute.resume_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id) - def test_reboot(self): - """Ensure instance can be rebooted""" + def test_soft_reboot(self): + """Ensure instance can be soft rebooted""" instance_id = self._create_instance() + reboot_type = "SOFT" self.compute.run_instance(self.context, instance_id) - self.compute.reboot_instance(self.context, instance_id) + self.compute.reboot_instance(self.context, instance_id, reboot_type) + self.compute.terminate_instance(self.context, instance_id) + + def test_hard_reboot(self): + """Ensure instance can be hard rebooted""" + instance_id = self._create_instance() + reboot_type = "HARD" + self.compute.run_instance(self.context, instance_id) + self.compute.reboot_instance(self.context, instance_id, reboot_type) self.compute.terminate_instance(self.context, instance_id) def test_set_admin_password(self): diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index 480247c91..440d3401b 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -103,8 +103,9 @@ class _VirtDriverTestCase(test.TestCase): def test_reboot(self): instance_ref = test_utils.get_test_instance() network_info = test_utils.get_test_network_info() + reboot_type = "SOFT" self.connection.spawn(self.ctxt, instance_ref, network_info) - self.connection.reboot(instance_ref, network_info) + self.connection.reboot(instance_ref, network_info, reboot_type) @catch_notimplementederror def test_get_host_ip_addr(self): diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py index 06daf46e8..e6da1690f 100644 --- a/nova/tests/test_vmwareapi.py +++ b/nova/tests/test_vmwareapi.py @@ -170,7 +170,8 @@ class VMWareAPIVMTestCase(test.TestCase): self._create_vm() info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - self.conn.reboot(self.instance, self.network_info) + reboot_type = "SOFT" + self.conn.reboot(self.instance, self.network_info, reboot_type) info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index d05b51bd9..301346c6b 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -165,12 +165,13 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() - def reboot(self, instance, network_info): + def reboot(self, instance, network_info, reboot_type): """Reboot the specified instance. :param instance: Instance object as returned by DB layer. :param network_info: :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` + :param reboot_type: Either a HARD or SOFT reboot """ # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index d5e2bf31b..3596d8353 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -103,7 +103,7 @@ class FakeConnection(driver.ComputeDriver): if not instance['name'] in self.instances: raise exception.InstanceNotRunning() - def reboot(self, instance, network_info): + def reboot(self, instance, network_info, reboot_type): pass def get_host_ip_addr(self): diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 03a78db1f..76925b405 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -367,7 +367,7 @@ class HyperVConnection(driver.ComputeDriver): wmi_obj.Properties_.Item(prop).Value return newinst - def reboot(self, instance, network_info): + def reboot(self, instance, network_info, reboot_type): """Reboot the specified instance.""" vm = self._lookup(instance.name) if vm is None: diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 243ee64f5..fa89a8f45 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -133,7 +133,7 @@ class VMWareESXConnection(driver.ComputeDriver): """Create snapshot from a running VM instance.""" self._vmops.snapshot(context, instance, name) - def reboot(self, instance, network_info): + def reboot(self, instance, network_info, reboot_type): """Reboot VM instance.""" self._vmops.reboot(instance, network_info) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c5f105f40..b7d6a40b4 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -617,10 +617,15 @@ class VMOps(object): str(new_disk_size)) LOG.debug(_("Resize instance %s complete") % (instance.name)) - def reboot(self, instance): + def reboot(self, instance, reboot_type): """Reboot VM instance.""" vm_ref = self._get_vm_opaque_ref(instance) - task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref) + + if reboot_type == "HARD": + task = self._session.call_xenapi('Async.VM.hard_reboot', vm_ref) + else: + task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref) + self._session.wait_for_task(task, instance.id) def get_agent_version(self, instance, timeout=None): diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0d23e7689..f6dbc19f8 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -203,9 +203,9 @@ class XenAPIConnection(driver.ComputeDriver): """ Create snapshot from a running VM instance """ self._vmops.snapshot(context, instance, image_id) - def reboot(self, instance, network_info): + def reboot(self, instance, network_info, reboot_type): """Reboot VM instance""" - self._vmops.reboot(instance) + self._vmops.reboot(instance, reboot_type) def set_admin_password(self, instance, new_pass): """Set the root/admin password on the VM instance""" -- cgit From 4ffe41dffaab64c96649bfc3236e5ba6bb9d8b37 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Fri, 9 Sep 2011 09:27:26 +0200 Subject: Open Essex (switch version to 2012.1) --- nova/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/version.py b/nova/version.py index 1f8d08e8c..06810df46 100644 --- a/nova/version.py +++ b/nova/version.py @@ -22,7 +22,7 @@ except ImportError: 'revno': 0} -NOVA_VERSION = ['2011', '3'] +NOVA_VERSION = ['2012', '1'] YEAR, COUNT = NOVA_VERSION FINAL = False # This becomes true at Release Candidate time -- cgit From 7352e3e1eb7a4d29b556492a208e80439828f211 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 9 Sep 2011 09:48:38 -0400 Subject: removing key_name and config_drive from non-detailed server entity --- nova/api/openstack/views/servers.py | 7 ++----- nova/tests/api/openstack/test_servers.py | 4 ---- 2 files changed, 2 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index ac09b5864..473dc9e7e 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -145,6 +145,8 @@ class ViewBuilderV11(ViewBuilder): response['server']['accessIPv4'] = inst.get('access_ip_v4') or "" response['server']['accessIPv6'] = inst.get('access_ip_v6') or "" + response['server']['key_name'] = inst.get('key_name', '') + response['server']['config_drive'] = inst.get('config_drive') return response @@ -185,8 +187,6 @@ class ViewBuilderV11(ViewBuilder): def _build_extra(self, response, inst): self._build_links(response, inst) response['uuid'] = inst['uuid'] - response['key_name'] = inst.get('key_name', '') - self._build_config_drive(response, inst) def _build_links(self, response, inst): href = self.generate_href(inst["id"]) @@ -205,9 +205,6 @@ class ViewBuilderV11(ViewBuilder): response["links"] = links - def _build_config_drive(self, response, inst): - response['config_drive'] = inst.get('config_drive') - def generate_href(self, server_id): """Create an url that refers to a specific server id.""" return os.path.join(self.base_url, self.project_id, diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index d063a60c2..f0a1c5ce5 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -3715,7 +3715,6 @@ class ServersViewBuilderV11Test(test.TestCase): "id": 1, "uuid": self.instance['uuid'], "name": "test_server", - "key_name": '', "links": [ { "rel": "self", @@ -3726,7 +3725,6 @@ class ServersViewBuilderV11Test(test.TestCase): "href": "http://localhost/servers/1", }, ], - "config_drive": None, } } @@ -3739,8 +3737,6 @@ class ServersViewBuilderV11Test(test.TestCase): "id": 1, "uuid": self.instance['uuid'], "name": "test_server", - "key_name": '', - "config_drive": None, "links": [ { "rel": "self", -- cgit From 9dd2d6c49a36c1834d0ef842c47d2ef400642ff2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 9 Sep 2011 08:59:02 -0700 Subject: remove sudo from qemu-img commands --- nova/virt/libvirt/connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 363a20ed0..19cef5ad7 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1696,7 +1696,7 @@ class LibvirtConnection(driver.ComputeDriver): base = os.path.basename(info['path']) # Get image type and create empty disk image. instance_disk = os.path.join(instance_dir, base) - utils.execute('sudo', 'qemu-img', 'create', '-f', info['type'], + utils.execute('qemu-img', 'create', '-f', info['type'], instance_disk, info['local_gb']) # if image has kernel and ramdisk, just download @@ -1788,7 +1788,7 @@ class LibvirtConnection(driver.ComputeDriver): if disk_type == 'raw': size = int(os.path.getsize(path)) else: - out, err = utils.execute('sudo', 'qemu-img', 'info', path) + out, err = utils.execute('qemu-img', 'info', path) size = [i.split('(')[1].split()[0] for i in out.split('\n') if i.strip().find('virtual size') >= 0] size = int(size[0]) -- cgit From d05d4e77df0bdfd2b802186762391d7f91361701 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Fri, 9 Sep 2011 14:35:38 -0500 Subject: Add comment to document why random.shuffle() works --- nova/scheduler/abstract_scheduler.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'nova') diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py index b4c2bf4f1..e4f615b94 100644 --- a/nova/scheduler/abstract_scheduler.py +++ b/nova/scheduler/abstract_scheduler.py @@ -296,6 +296,13 @@ class AbstractScheduler(driver.Scheduler): "child_blob": weighting["blob"]} weighted_hosts.append(host_dict) if FLAGS.spread_first: + # NOTE(Vek): If all the weights are unique, then the sort + # below undoes this shuffle; however, if + # several responses from several zones have the + # same weight, then this shuffle serves to + # break up the monolithic blocks and cause the + # instances to be uniformly spread across the + # zones. random.shuffle(weighted_hosts) weighted_hosts.sort(key=operator.itemgetter('weight')) return weighted_hosts -- cgit From c0700ea7bbb4d860610b71e635b8dbde19157e85 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Fri, 9 Sep 2011 20:27:22 +0000 Subject: don't need random in abstract_scheduler.py anymore... --- nova/scheduler/abstract_scheduler.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/scheduler/abstract_scheduler.py b/nova/scheduler/abstract_scheduler.py index 76329bbc6..6e8c7d715 100644 --- a/nova/scheduler/abstract_scheduler.py +++ b/nova/scheduler/abstract_scheduler.py @@ -22,7 +22,6 @@ behavior is to simply select all hosts and weight them the same. import json import operator -import random import M2Crypto -- cgit From 65a0cc41b1b9ead5acd3128a4a6202bb02e3a6e5 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 9 Sep 2011 17:25:45 -0400 Subject: fixing image status mapping --- nova/api/openstack/views/images.py | 15 ++++++++------- nova/tests/api/openstack/test_images.py | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 21f1b2d3e..8983b2957 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -37,17 +37,18 @@ class ViewBuilder(object): def _format_status(self, image): """Update the status field to standardize format.""" status_mapping = { - 'pending': 'QUEUED', - 'decrypting': 'PREPARING', - 'untarring': 'SAVING', - 'available': 'ACTIVE', - 'killed': 'FAILED', + 'active': 'ACTIVE', + 'queued': 'SAVING', + 'saving': 'SAVING', + 'deleted': 'DELETED', + 'pending_delete': 'DELETED', + 'killed': 'ERROR', } try: - image['status'] = status_mapping[image['status']].upper() + image['status'] = status_mapping[image['status']] except KeyError: - image['status'] = image['status'].upper() + image['status'] = 'UNKNOWN' def _build_server(self, image, image_obj): """Indicates that you must use a ViewBuilder subclass.""" diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 2a7cfc382..46f763d5e 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -407,7 +407,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): "name": "queued snapshot", "updated": self.NOW_API_FORMAT, "created": self.NOW_API_FORMAT, - "status": "QUEUED", + "status": "SAVING", "progress": 0, 'server': { 'id': 42, @@ -603,7 +603,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'name': 'queued snapshot', 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, - 'status': 'QUEUED', + 'status': 'SAVING', 'progress': 0, }, { @@ -627,7 +627,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'name': 'killed snapshot', 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, - 'status': 'FAILED', + 'status': 'ERROR', 'progress': 0, }, { @@ -676,7 +676,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, - 'status': 'QUEUED', + 'status': 'SAVING', 'progress': 0, 'server': { 'id': 42, @@ -769,7 +769,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, - 'status': 'FAILED', + 'status': 'ERROR', 'progress': 0, 'server': { 'id': 42, -- cgit From c3cb1d38ca4a6f3308503c79e13e3e8688143163 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Fri, 9 Sep 2011 20:47:37 -0400 Subject: Fix spelling mistake --- nova/virt/libvirt/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 6ae458537..7c1edc373 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -196,7 +196,7 @@ class LibvirtConnection(driver.ComputeDriver): def _test_connection(self): try: - self._wrapped_conn.geCapabilities() + self._wrapped_conn.getCapabilities() return True except libvirt.libvirtError as e: if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \ -- cgit From 9482275a60ab8caa546ec402f61c60b9f5e7e33f Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Sat, 10 Sep 2011 13:56:54 -0400 Subject: Update GlanceClient, GlanceImageService, and Glance Xen plugin to work with Glance keystone. --- nova/api/auth.py | 1 + nova/api/openstack/create_instance_helper.py | 3 +- nova/compute/api.py | 3 +- nova/compute/manager.py | 3 +- nova/context.py | 9 ++- nova/image/__init__.py | 56 +---------------- nova/image/glance.py | 91 +++++++++++++++++++++------- nova/tests/api/openstack/fakes.py | 2 +- nova/tests/glance/stubs.py | 6 +- nova/tests/integrated/integrated_helpers.py | 2 +- nova/tests/test_xenapi.py | 3 +- nova/virt/images.py | 3 +- nova/virt/libvirt/connection.py | 4 +- nova/virt/vmwareapi/fake.py | 6 +- nova/virt/vmwareapi/vmops.py | 4 +- nova/virt/vmwareapi/vmware_images.py | 69 +++------------------ nova/virt/xenapi/vm_utils.py | 17 +++--- nova/virt/xenapi/vmops.py | 4 +- 18 files changed, 119 insertions(+), 167 deletions(-) (limited to 'nova') diff --git a/nova/api/auth.py b/nova/api/auth.py index cd0d38b3f..f73cae01e 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -70,6 +70,7 @@ class KeystoneContext(wsgi.Middleware): project_id, roles=roles, auth_token=auth_token, + strategy='keystone', remote_address=remote_address) req.environ['nova.context'] = ctx diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 67e669c17..e27ddf78b 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -92,7 +92,8 @@ class CreateInstanceHelper(object): if str(image_href).startswith(req.application_url): image_href = image_href.split('/').pop() try: - image_service, image_id = nova.image.get_image_service(image_href) + image_service, image_id = nova.image.get_image_service(context, + image_href) kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image( req, image_service, image_id) images = set([str(x['id']) for x in image_service.index(context)]) diff --git a/nova/compute/api.py b/nova/compute/api.py index 4e2944bb7..95b4f5dea 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -202,7 +202,8 @@ class API(base.Base): self._check_injected_file_quota(context, injected_files) self._check_requested_networks(context, requested_networks) - (image_service, image_id) = nova.image.get_image_service(image_href) + (image_service, image_id) = nova.image.get_image_service(context, + image_href) image = image_service.show(context, image_id) config_drive_id = None diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 0477db745..25d44e502 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -322,7 +322,8 @@ class ComputeManager(manager.SchedulerDependentManager): # used by the image service. This should be refactored to be # consistent. image_href = instance['image_ref'] - image_service, image_id = nova.image.get_image_service(image_href) + image_service, image_id = nova.image.get_image_service(context, + image_href) image_meta = image_service.show(context, image_id) try: diff --git a/nova/context.py b/nova/context.py index 5c22641a0..de5b791c4 100644 --- a/nova/context.py +++ b/nova/context.py @@ -32,7 +32,7 @@ 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, auth_token=None): + request_id=None, auth_token=None, strategy='noauth'): self.user_id = user_id self.project_id = project_id self.roles = roles or [] @@ -50,6 +50,7 @@ class RequestContext(object): request_id = unicode(uuid.uuid4()) self.request_id = request_id self.auth_token = auth_token + self.strategy = strategy def to_dict(self): return {'user_id': self.user_id, @@ -60,7 +61,8 @@ class RequestContext(object): 'remote_address': self.remote_address, 'timestamp': utils.strtime(self.timestamp), 'request_id': self.request_id, - 'auth_token': self.auth_token} + 'auth_token': self.auth_token, + 'strategy': self.strategy} @classmethod def from_dict(cls, values): @@ -77,7 +79,8 @@ class RequestContext(object): remote_address=self.remote_address, timestamp=self.timestamp, request_id=self.request_id, - auth_token=self.auth_token) + auth_token=self.auth_token, + strategy=self.strategy) def get_admin_context(read_deleted=False): diff --git a/nova/image/__init__.py b/nova/image/__init__.py index 5447c8a3a..307b73f01 100644 --- a/nova/image/__init__.py +++ b/nova/image/__init__.py @@ -16,70 +16,20 @@ # under the License. -from urlparse import urlparse - import nova -from nova import exception from nova import utils from nova import flags -from nova.image import glance as glance_image_service +from nova.image import glance FLAGS = flags.FLAGS -GlanceClient = utils.import_class('glance.client.Client') - - -def _parse_image_ref(image_href): - """Parse an image href into composite parts. - - :param image_href: href of an image - :returns: a tuple of the form (image_id, host, port) - :raises ValueError - - """ - o = urlparse(image_href) - port = o.port or 80 - host = o.netloc.split(':', 1)[0] - image_id = int(o.path.split('/')[-1]) - return (image_id, host, port) - - def get_default_image_service(): ImageService = utils.import_class(FLAGS.image_service) return ImageService() -# FIXME(sirp): perhaps this should be moved to nova/images/glance so that we -# keep Glance specific code together for the most part -def get_glance_client(image_href): - """Get the correct glance client and id for the given image_href. - - The image_href param can be an href of the form - http://myglanceserver:9292/images/42, or just an int such as 42. If the - image_href is an int, then flags are used to create the default - glance client. - - :param image_href: image ref/id for an image - :returns: a tuple of the form (glance_client, image_id) - - """ - image_href = image_href or 0 - if str(image_href).isdigit(): - glance_host, glance_port = \ - glance_image_service.pick_glance_api_server() - glance_client = GlanceClient(glance_host, glance_port) - return (glance_client, int(image_href)) - - try: - (image_id, host, port) = _parse_image_ref(image_href) - except ValueError: - raise exception.InvalidImageRef(image_href=image_href) - glance_client = GlanceClient(host, port) - return (glance_client, image_id) - - -def get_image_service(image_href): +def get_image_service(context, image_href): """Get the proper image_service and id for the given image_href. The image_href param can be an href of the form @@ -94,6 +44,6 @@ def get_image_service(image_href): if str(image_href).isdigit(): return (get_default_image_service(), int(image_href)) - (glance_client, image_id) = get_glance_client(image_href) + (glance_client, image_id) = glance.get_glance_client(context, image_href) image_service = nova.image.glance.GlanceImageService(glance_client) return (image_service, image_id) diff --git a/nova/image/glance.py b/nova/image/glance.py index 80abc7384..e735f4082 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -23,6 +23,7 @@ import copy import datetime import json import random +from urlparse import urlparse from glance.common import exception as glance_exception @@ -42,6 +43,35 @@ FLAGS = flags.FLAGS GlanceClient = utils.import_class('glance.client.Client') +def _parse_image_ref(image_href): + """Parse an image href into composite parts. + + :param image_href: href of an image + :returns: a tuple of the form (image_id, host, port) + :raises ValueError + + """ + o = urlparse(image_href) + port = o.port or 80 + host = o.netloc.split(':', 1)[0] + image_id = int(o.path.split('/')[-1]) + return (image_id, host, port) + + +def _create_glance_client(context, host, port): + if context.strategy == 'keystone': + # NOTE(dprince): Glance client just needs auth_tok right? Should we + # add username and tenant to the creds below? + creds={'strategy': 'keystone', + 'username': context.user_id, + 'tenant': context.project_id} + glance_client = GlanceClient(host, port, auth_tok=context.auth_token, + creds=creds) + else: + glance_client = GlanceClient(host, port) + return glance_client + + def pick_glance_api_server(): """Return which Glance API server to use for the request @@ -57,6 +87,33 @@ def pick_glance_api_server(): return host, port +def get_glance_client(context, image_href): + """Get the correct glance client and id for the given image_href. + + The image_href param can be an href of the form + http://myglanceserver:9292/images/42, or just an int such as 42. If the + image_href is an int, then flags are used to create the default + glance client. + + :param image_href: image ref/id for an image + :returns: a tuple of the form (glance_client, image_id) + + """ + image_href = image_href or 0 + if str(image_href).isdigit(): + glance_host, glance_port = pick_glance_api_server() + glance_client = _create_glance_client(context, glance_host, + glance_port) + return (glance_client, int(image_href)) + + try: + (image_id, host, port) = _parse_image_ref(image_href) + except ValueError: + raise exception.InvalidImageRef(image_href=image_href) + glance_client = _create_glance_client(context, glance_host, glance_port) + return (glance_client, image_id) + + class GlanceImageService(service.BaseImageService): """Provides storage and retrieval of disk image objects within Glance.""" @@ -71,23 +128,14 @@ class GlanceImageService(service.BaseImageService): def __init__(self, client=None): self._client = client - def _get_client(self): + def _get_client(self, context): # NOTE(sirp): we want to load balance each request across glance # servers. Since GlanceImageService is a long-lived object, `client` # is made to choose a new server each time via this property. if self._client is not None: return self._client glance_host, glance_port = pick_glance_api_server() - return GlanceClient(glance_host, glance_port) - - def _set_client(self, client): - self._client = client - - client = property(_get_client, _set_client) - - def _set_client_context(self, context): - """Sets the client's auth token.""" - self.client.set_auth_token(context.auth_token) + return _create_glance_client(context, glance_host, glance_port) def index(self, context, **kwargs): """Calls out to Glance for a list of images available.""" @@ -128,14 +176,14 @@ class GlanceImageService(service.BaseImageService): def _get_images(self, context, **kwargs): """Get image entitites from images service""" - self._set_client_context(context) # ensure filters is a dict kwargs['filters'] = kwargs.get('filters') or {} # NOTE(vish): don't filter out private images kwargs['filters'].setdefault('is_public', 'none') - return self._fetch_images(self.client.get_images_detailed, **kwargs) + client = self._get_client(context) + return self._fetch_images(client.get_images_detailed, **kwargs) def _fetch_images(self, fetch_func, **kwargs): """Paginate through results from glance server""" @@ -168,9 +216,8 @@ class GlanceImageService(service.BaseImageService): def show(self, context, image_id): """Returns a dict with image data for the given opaque image id.""" - self._set_client_context(context) try: - image_meta = self.client.get_image_meta(image_id) + image_meta = self._get_client(context).get_image_meta(image_id) except glance_exception.NotFound: raise exception.ImageNotFound(image_id=image_id) @@ -192,9 +239,9 @@ class GlanceImageService(service.BaseImageService): def get(self, context, image_id, data): """Calls out to Glance for metadata and data and writes data.""" - self._set_client_context(context) try: - image_meta, image_chunks = self.client.get_image(image_id) + client = self._get_client(context) + image_meta, image_chunks = client.get_image(image_id) except glance_exception.NotFound: raise exception.ImageNotFound(image_id=image_id) @@ -210,7 +257,6 @@ class GlanceImageService(service.BaseImageService): :raises: AlreadyExists if the image already exist. """ - self._set_client_context(context) # Translate Base -> Service LOG.debug(_('Creating image in Glance. Metadata passed in %s'), image_meta) @@ -218,7 +264,7 @@ class GlanceImageService(service.BaseImageService): LOG.debug(_('Metadata after formatting for Glance %s'), sent_service_image_meta) - recv_service_image_meta = self.client.add_image( + recv_service_image_meta = self._get_client(context).add_image( sent_service_image_meta, data) # Translate Service -> Base @@ -233,12 +279,12 @@ class GlanceImageService(service.BaseImageService): :raises: ImageNotFound if the image does not exist. """ - self._set_client_context(context) # NOTE(vish): show is to check if image is available self.show(context, image_id) image_meta = _convert_to_string(image_meta) try: - image_meta = self.client.update_image(image_id, image_meta, data) + client = self._get_client(context) + image_meta = client.update_image(image_id, image_meta, data) except glance_exception.NotFound: raise exception.ImageNotFound(image_id=image_id) @@ -251,11 +297,10 @@ class GlanceImageService(service.BaseImageService): :raises: ImageNotFound if the image does not exist. """ - self._set_client_context(context) # NOTE(vish): show is to check if image is available self.show(context, image_id) try: - result = self.client.delete_image(image_id) + result = self._get_client(context).delete_image(image_id) except glance_exception.NotFound: raise exception.ImageNotFound(image_id=image_id) return result diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 44681d395..098b1e284 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -124,7 +124,7 @@ def stub_out_key_pair_funcs(stubs, have_key_pair=True): def stub_out_image_service(stubs): - def fake_get_image_service(image_href): + def fake_get_image_service(context, image_href): return (nova.image.fake.FakeImageService(), image_href) stubs.Set(nova.image, 'get_image_service', fake_get_image_service) stubs.Set(nova.image, 'get_default_image_service', diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index f2a19f22d..6b74e671c 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -16,14 +16,14 @@ import StringIO -import nova.image +from nova.image import glance def stubout_glance_client(stubs): - def fake_get_glance_client(image_href): + def fake_get_glance_client(context, image_href): image_id = int(str(image_href).split('/')[-1]) return (FakeGlance('foo'), image_id) - stubs.Set(nova.image, 'get_glance_client', fake_get_glance_client) + stubs.Set(glance, 'get_glance_client', fake_get_glance_client) class FakeGlance(object): diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index 343190427..49de9c854 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -64,7 +64,7 @@ class _IntegratedTestBase(test.TestCase): self.flags(**f) self.flags(verbose=True) - def fake_get_image_service(image_href): + def fake_get_image_service(context, image_href): image_id = int(str(image_href).split('/')[-1]) return (nova.image.fake.FakeImageService(), image_id) self.stubs.Set(nova.image, 'get_image_service', fake_get_image_service) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 91b4161b0..4a83d139e 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -932,8 +932,9 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase): self.fake_instance.architecture = 'x86-64' def assert_disk_type(self, disk_type): + ctx = context.RequestContext('fake', 'fake') dt = vm_utils.VMHelper.determine_disk_image_type( - self.fake_instance) + self.fake_instance, ctx) self.assertEqual(disk_type, dt) def test_instance_disk(self): diff --git a/nova/virt/images.py b/nova/virt/images.py index 54c691a40..810b359d9 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -37,7 +37,8 @@ def fetch(context, image_href, path, _user_id, _project_id): # when it is added to glance. Right now there is no # auth checking in glance, so we assume that access was # checked before we got here. - (image_service, image_id) = nova.image.get_image_service(image_href) + (image_service, image_id) = nova.image.get_image_service(context, + image_href) with open(path, "wb") as image_file: metadata = image_service.get(context, image_id, image_file) return metadata diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 19cef5ad7..fd902ca4b 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -398,10 +398,10 @@ class LibvirtConnection(driver.ComputeDriver): virt_dom = self._lookup_by_name(instance['name']) (image_service, image_id) = nova.image.get_image_service( - instance['image_ref']) + context, instance['image_ref']) base = image_service.show(context, image_id) (snapshot_image_service, snapshot_image_id) = \ - nova.image.get_image_service(image_href) + nova.image.get_image_service(context, image_href) snapshot = snapshot_image_service.show(context, snapshot_image_id) metadata = {'is_public': False, diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 4c62d18bb..0dea13aba 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -412,7 +412,7 @@ def fake_get_network(*args, **kwargs): return [{'type': 'fake'}] -def fake_fetch_image(image, instance, **kwargs): +def fake_fetch_image(context, image, instance, **kwargs): """Fakes fetch image call. Just adds a reference to the db for the file.""" ds_name = kwargs.get("datastore_name") file_path = kwargs.get("file_path") @@ -420,12 +420,12 @@ def fake_fetch_image(image, instance, **kwargs): _add_file(ds_file_path) -def fake_upload_image(image, instance, **kwargs): +def fake_upload_image(context, image, instance, **kwargs): """Fakes the upload of an image.""" pass -def fake_get_vmdk_size_and_properties(image_id, instance): +def fake_get_vmdk_size_and_properties(context, image_id, instance): """Fakes the file size and properties fetch for the image file.""" props = {"vmware_ostype": "otherGuest", "vmware_adaptertype": "lsiLogic"} diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 07a6ba6ab..6bdc2f23a 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -157,7 +157,7 @@ class VMWareVMOps(object): repository. """ image_size, image_properties = \ - vmware_images.get_vmdk_size_and_properties( + vmware_images.get_vmdk_size_and_properties(context, instance.image_ref, instance) vmdk_file_size_in_kb = int(image_size) / 1024 os_type = image_properties.get("vmware_ostype", "otherGuest") @@ -282,6 +282,7 @@ class VMWareVMOps(object): # Upload the -flat.vmdk file whose meta-data file we just created # above vmware_images.fetch_image( + context, instance.image_ref, instance, host=self._session._host_ip, @@ -448,6 +449,7 @@ class VMWareVMOps(object): # Upload the contents of -flat.vmdk file which has the disk data. LOG.debug(_("Uploading image %s") % snapshot_name) vmware_images.upload_image( + context, snapshot_name, instance, os_type=os_type, diff --git a/nova/virt/vmwareapi/vmware_images.py b/nova/virt/vmwareapi/vmware_images.py index f5f75dae2..53f2d372e 100644 --- a/nova/virt/vmwareapi/vmware_images.py +++ b/nova/virt/vmwareapi/vmware_images.py @@ -20,15 +20,13 @@ Utility functions for Image transfer. from nova import exception from nova import flags -import nova.image +from nova.image import glance from nova import log as logging from nova.virt.vmwareapi import io_util from nova.virt.vmwareapi import read_write_util LOG = logging.getLogger("nova.virt.vmwareapi.vmware_images") -FLAGS = flags.FLAGS - QUEUE_BUFFER_SIZE = 10 @@ -87,36 +85,10 @@ def start_transfer(read_file_handle, data_size, write_file_handle=None, write_file_handle.close() -def fetch_image(image, instance, **kwargs): - """Fetch an image for attaching to the newly created VM.""" - # Depending upon the image service, make appropriate image service call - if FLAGS.image_service == "nova.image.glance.GlanceImageService": - func = _get_glance_image - elif FLAGS.image_service == "nova.image.s3.S3ImageService": - func = _get_s3_image - else: - raise NotImplementedError(_("The Image Service %s is not implemented") - % FLAGS.image_service) - return func(image, instance, **kwargs) - - -def upload_image(image, instance, **kwargs): - """Upload the newly snapshotted VM disk file.""" - # Depending upon the image service, make appropriate image service call - if FLAGS.image_service == "nova.image.glance.GlanceImageService": - func = _put_glance_image - elif FLAGS.image_service == "nova.image.s3.S3ImageService": - func = _put_s3_image - else: - raise NotImplementedError(_("The Image Service %s is not implemented") - % FLAGS.image_service) - return func(image, instance, **kwargs) - - -def _get_glance_image(image, instance, **kwargs): +def fetch_image(context, image, instance, **kwargs): """Download image from the glance image server.""" LOG.debug(_("Downloading image %s from glance image server") % image) - (glance_client, image_id) = nova.image.get_glance_client(image) + (glance_client, image_id) = glance.get_glance_client(context, image) metadata, read_iter = glance_client.get_image(image_id) read_file_handle = read_write_util.GlanceFileRead(read_iter) file_size = int(metadata['size']) @@ -132,17 +104,7 @@ def _get_glance_image(image, instance, **kwargs): LOG.debug(_("Downloaded image %s from glance image server") % image) -def _get_s3_image(image, instance, **kwargs): - """Download image from the S3 image server.""" - raise NotImplementedError - - -def _get_local_image(image, instance, **kwargs): - """Download image from the local nova compute node.""" - raise NotImplementedError - - -def _put_glance_image(image, instance, **kwargs): +def upload_image(context, image, instance, **kwargs): """Upload the snapshotted vm disk file to Glance image server.""" LOG.debug(_("Uploading image %s to the Glance image server") % image) read_file_handle = read_write_util.VmWareHTTPReadFile( @@ -152,7 +114,7 @@ def _put_glance_image(image, instance, **kwargs): kwargs.get("cookies"), kwargs.get("file_path")) file_size = read_file_handle.get_size() - (glance_client, image_id) = nova.image.get_glance_client(image) + (glance_client, image_id) = glance.get_glance_client(context, image) # The properties and other fields that we need to set for the image. image_metadata = {"is_public": True, "disk_format": "vmdk", @@ -168,17 +130,7 @@ def _put_glance_image(image, instance, **kwargs): LOG.debug(_("Uploaded image %s to the Glance image server") % image) -def _put_local_image(image, instance, **kwargs): - """Upload the snapshotted vm disk file to the local nova compute node.""" - raise NotImplementedError - - -def _put_s3_image(image, instance, **kwargs): - """Upload the snapshotted vm disk file to S3 image server.""" - raise NotImplementedError - - -def get_vmdk_size_and_properties(image, instance): +def get_vmdk_size_and_properties(context, image, instance): """ Get size of the vmdk file that is to be downloaded for attach in spawn. Need this to create the dummy virtual disk for the meta-data file. The @@ -186,12 +138,9 @@ def get_vmdk_size_and_properties(image, instance): """ LOG.debug(_("Getting image size for the image %s") % image) - if FLAGS.image_service == "nova.image.glance.GlanceImageService": - (glance_client, image_id) = nova.image.get_glance_client(image) - meta_data = glance_client.get_image_meta(image_id) - size, properties = meta_data["size"], meta_data["properties"] - elif FLAGS.image_service == "nova.image.s3.S3ImageService": - raise NotImplementedError + (glance_client, image_id) = glance.get_glance_client(context, image) + meta_data = glance_client.get_image_meta(image_id) + size, properties = meta_data["size"], meta_data["properties"] LOG.debug(_("Got image size of %(size)s for the image %(image)s") % locals()) return size, properties diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index efbea7076..302238c98 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -31,12 +31,10 @@ import urllib import uuid from xml.dom import minidom -import glance.client from nova import db from nova import exception from nova import flags -import nova.image -from nova.image import glance as glance_image_service +from nova.image import glance from nova import log as logging from nova import utils from nova.compute import instance_types @@ -383,8 +381,7 @@ class VMHelper(HelperBase): os_type = instance.os_type or FLAGS.default_os_type - glance_host, glance_port = \ - glance_image_service.pick_glance_api_server() + glance_host, glance_port = glance.pick_glance_api_server() params = {'vdi_uuids': vdi_uuids, 'image_id': image_id, 'glance_host': glance_host, @@ -447,8 +444,7 @@ class VMHelper(HelperBase): # pass them as arguments uuid_stack = [str(uuid.uuid4()) for i in xrange(2)] - glance_host, glance_port = \ - glance_image_service.pick_glance_api_server() + glance_host, glance_port = glance.pick_glance_api_server() params = {'image_id': image, 'glance_host': glance_host, 'glance_port': glance_port, @@ -546,7 +542,7 @@ class VMHelper(HelperBase): else: sr_ref = safe_find_sr(session) - glance_client, image_id = nova.image.get_glance_client(image) + glance_client, image_id = glance.get_glance_client(context, image) glance_client.set_auth_token(getattr(context, 'auth_token', None)) meta, image_file = glance_client.get_image(image_id) virtual_size = int(meta['size']) @@ -606,7 +602,7 @@ class VMHelper(HelperBase): raise e @classmethod - def determine_disk_image_type(cls, instance): + def determine_disk_image_type(cls, instance, context): """Disk Image Types are used to determine where the kernel will reside within an image. To figure out which type we're dealing with, we use the following rules: @@ -639,7 +635,8 @@ class VMHelper(HelperBase): 'vhd': ImageType.DISK_VHD, 'iso': ImageType.DISK_ISO} image_ref = instance.image_ref - glance_client, image_id = nova.image.get_glance_client(image_ref) + glance_client, image_id = glance.get_glance_client(context, + image_ref) meta = glance_client.get_image_meta(image_id) disk_format = meta['disk_format'] try: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 9c138ee41..038c041c7 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -135,7 +135,7 @@ class VMOps(object): self._session.call_xenapi('VM.start', vm_ref, False, False) def _create_disks(self, context, instance): - disk_image_type = VMHelper.determine_disk_image_type(instance) + disk_image_type = VMHelper.determine_disk_image_type(instance, context) vdis = VMHelper.fetch_image(context, self._session, instance, instance.image_ref, instance.user_id, instance.project_id, @@ -176,7 +176,7 @@ class VMOps(object): power_state.SHUTDOWN) return - disk_image_type = VMHelper.determine_disk_image_type(instance) + disk_image_type = VMHelper.determine_disk_image_type(instance, context) kernel = None ramdisk = None try: -- cgit From cd5084f8a69b0e2a14f01aa9a4f3d8588a83c923 Mon Sep 17 00:00:00 2001 From: Thierry Carrez Date: Mon, 12 Sep 2011 14:30:56 +0200 Subject: Fix rogue usage of 'sudo' bypassing the run_as_root=True method --- nova/tests/test_libvirt.py | 4 ++-- nova/virt/disk.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 8c6775b29..fea2b7cd3 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -743,7 +743,7 @@ class LibvirtConnTestCase(test.TestCase): # qemu-img should be mockd since test environment might not have # large disk space. self.mox.StubOutWithMock(utils, "execute") - utils.execute('sudo', 'qemu-img', 'create', '-f', 'raw', + utils.execute('qemu-img', 'create', '-f', 'raw', '%s/%s/disk' % (tmpdir, instance_ref.name), '10G') self.mox.ReplayAll() @@ -795,7 +795,7 @@ class LibvirtConnTestCase(test.TestCase): os.path.getsize("/test/disk").AndReturn(10 * 1024 * 1024 * 1024) # another is qcow image, so qemu-img should be mocked. self.mox.StubOutWithMock(utils, "execute") - utils.execute('sudo', 'qemu-img', 'info', '/test/disk.local').\ + utils.execute('qemu-img', 'info', '/test/disk.local').\ AndReturn((ret, '')) self.mox.ReplayAll() diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 52b2881e8..e6cf5f5c4 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -228,8 +228,8 @@ def _inject_metadata_into_fs(metadata, fs, execute=None): metadata_path = os.path.join(fs, "meta.js") metadata = dict([(m.key, m.value) for m in metadata]) - utils.execute('sudo', 'tee', metadata_path, - process_input=json.dumps(metadata)) + utils.execute('tee', metadata_path, + process_input=json.dumps(metadata), run_as_root=True) def _inject_key_into_fs(key, fs, execute=None): -- cgit From 9b8e73d9ef1a5bd4efb460f3a0c033fc748ccdd9 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 12 Sep 2011 14:22:46 -0400 Subject: adding tests for deleted and pending_delete statuses --- nova/tests/api/openstack/test_images.py | 96 ++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 46f763d5e..c63d1203a 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -365,7 +365,9 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): {'id': 125, 'name': 'saving snapshot'}, {'id': 126, 'name': 'active snapshot'}, {'id': 127, 'name': 'killed snapshot'}, - {'id': 129, 'name': None}] + {'id': 128, 'name': 'deleted snapshot'}, + {'id': 129, 'name': 'pending_delete snapshot'}, + {'id': 131, 'name': None}] self.assertDictListMatch(response_list, expected) @@ -458,7 +460,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(expected_image.toxml(), actual_image.toxml()) def test_get_image_xml_no_name(self): - request = webob.Request.blank('/v1.0/images/129') + request = webob.Request.blank('/v1.0/images/131') request.accept = "application/xml" response = request.get_response(fakes.wsgi_app()) @@ -466,7 +468,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): expected_now = self.NOW_API_FORMAT expected_image = minidom.parseString(""" - Date: Mon, 12 Sep 2011 15:17:57 -0400 Subject: pep8 fix. --- nova/image/glance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/image/glance.py b/nova/image/glance.py index e735f4082..13c8ff843 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -62,9 +62,9 @@ def _create_glance_client(context, host, port): if context.strategy == 'keystone': # NOTE(dprince): Glance client just needs auth_tok right? Should we # add username and tenant to the creds below? - creds={'strategy': 'keystone', - 'username': context.user_id, - 'tenant': context.project_id} + creds = {'strategy': 'keystone', + 'username': context.user_id, + 'tenant': context.project_id} glance_client = GlanceClient(host, port, auth_tok=context.auth_token, creds=creds) else: -- cgit From 81fe8c89061fa15ebcea9d20f39cf79b63cf8522 Mon Sep 17 00:00:00 2001 From: Antony Messerli Date: Mon, 12 Sep 2011 14:43:15 -0500 Subject: pep8 fixes --- nova/db/sqlalchemy/api.py | 2 +- nova/virt/disk.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 40e2ca167..e5a661c7f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -672,7 +672,7 @@ def floating_ip_update(context, address, values): def fixed_ip_associate(context, address, instance_id, network_id=None, reserved=False): """Keyword arguments: - reserved -- should be a boolean value(True or False), exact value will be + reserved -- should be a boolean value(True or False), exact value will be used to filter on the fixed ip address """ session = get_session() diff --git a/nova/virt/disk.py b/nova/virt/disk.py index d0745c82d..cd3422829 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -160,6 +160,7 @@ def destroy_container(target, instance, nbd=False): except Exception, exn: LOG.exception(_('Failed to remove container: %s'), exn) + def _link_device(image, nbd): """Link image to device using loopback or nbd""" -- cgit