From ef222bfe6f50d5203f83fa9d2e9071969f814c29 Mon Sep 17 00:00:00 2001 From: Maru Newby Date: Tue, 18 Sep 2012 18:27:26 +0000 Subject: Add lookup by ip via Quantum for metadata service. * The Nova network API's get_fixed_ip_by_address() is used by the metadata service in determining which instance to return metadata for. * This change implements support for the function in the Quantum v2 implementation of the network API to ensure that the metadata service can return results when Nova is configured to use Quantum. * Overlapping network spaces are not supported at this time. * Addresses bug 1052196 Change-Id: Iaa5d78c4de92313e93ee9008a2c26052a4f60602 --- nova/exception.py | 5 +++++ nova/network/quantumv2/api.py | 28 +++++++++++++++++++------- nova/tests/network/test_quantumv2.py | 38 +++++++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/nova/exception.py b/nova/exception.py index c9f339e6c..4dfff73bd 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -605,6 +605,11 @@ class FixedIpAlreadyInUse(NovaException): "%(instance_uuid)s.") +class FixedIpAssociatedWithMultipleInstances(NovaException): + message = _("More than one instance is associated with fixed ip address " + "'%(address)s'.") + + class FixedIpInvalid(Invalid): message = _("Fixed IP address %(address)s is invalid.") diff --git a/nova/network/quantumv2/api.py b/nova/network/quantumv2/api.py index 5d657dc21..8a1259d3d 100644 --- a/nova/network/quantumv2/api.py +++ b/nova/network/quantumv2/api.py @@ -220,6 +220,18 @@ class API(base.Base): id_str = id_str and id_str + ', ' + _id or _id raise exception.NetworkNotFound(network_id=id_str) + def _get_instance_uuids_by_ip(self, context, address): + """Retrieve instance uuids associated with the given ip address. + + :returns: A list of dicts containing the uuids keyed by 'instance_uuid' + e.g. [{'instance_uuid': uuid}, ...] + """ + search_opts = {"fixed_ips": 'ip_address=%s' % address} + data = quantumv2.get_client(context).list_ports(**search_opts) + ports = data.get('ports', []) + return [{'instance_uuid': port['device_id']} for port in ports + if port['device_id']] + def get_instance_uuids_by_ip_filter(self, context, filters): """Return a list of dicts in the form of [{'instance_uuid': uuid}] that matched the ip filter. @@ -232,12 +244,7 @@ class API(base.Base): if ip[-1] == '$': ip = ip[:-1] ip = ip.replace('\\.', '.') - search_opts = {"fixed_ips": {'ip_address': ip}} - data = quantumv2.get_client(context).list_ports(**search_opts) - ports = data.get('ports', []) - - return [{'instance_uuid': port['device_id']} for port in ports - if port['device_id']] + return self._get_instance_uuids_by_ip(context, ip) def trigger_security_group_members_refresh(self, context, instance_ref): @@ -277,7 +284,14 @@ class API(base.Base): raise NotImplementedError() def get_fixed_ip_by_address(self, context, address): - raise NotImplementedError() + uuid_maps = self._get_instance_uuids_by_ip(context, address) + if len(uuid_maps) == 1: + return uuid_maps[0] + elif not uuid_maps: + raise exception.FixedIpNotFoundForAddress(address=address) + else: + raise exception.FixedIpAssociatedWithMultipleInstances( + address=address) def get_floating_ip(self, context, id): raise NotImplementedError() diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py index 19ba84edf..e52eedd12 100644 --- a/nova/tests/network/test_quantumv2.py +++ b/nova/tests/network/test_quantumv2.py @@ -161,11 +161,12 @@ class TestQuantumv2(test.TestCase): 'tenant_id': 'his_tenantid'}] self.nets = [self.nets1, self.nets2, self.nets3, self.nets4] + self.port_address = '10.0.1.2' self.port_data1 = [{'network_id': 'my_netid1', 'device_id': 'device_id1', 'device_owner': 'compute:nova', 'id': 'my_portid1', - 'fixed_ips': [{'ip_address': '10.0.1.2', + 'fixed_ips': [{'ip_address': self.port_address, 'subnet_id': 'my_subid1'}], 'mac_address': 'my_mac1', }] self.dhcp_port_data1 = [{'fixed_ips': [{'ip_address': '10.0.1.9', @@ -510,17 +511,44 @@ class TestQuantumv2(test.TestCase): except exception.NetworkNotFound as ex: self.assertTrue("my_netid2, my_netid3" in str(ex)) - def test_get_instance_uuids_by_ip_filter(self): - filters = {'ip': '^10\\.0\\.1\\.2$'} + def _mock_list_ports(self, port_data=None): + if port_data is None: + port_data = self.port_data2 + address = self.port_address self.moxed_client.list_ports( - fixed_ips=MyComparator({'ip_address': '10.0.1.2'})).AndReturn( - {'ports': self.port_data2}) + fixed_ips=MyComparator('ip_address=%s' % address)).AndReturn( + {'ports': port_data}) self.mox.ReplayAll() + return address + + def test_get_instance_uuids_by_ip_filter(self): + self._mock_list_ports() + filters = {'ip': '^10\\.0\\.1\\.2$'} api = quantumapi.API() result = api.get_instance_uuids_by_ip_filter(self.context, filters) self.assertEquals('device_id1', result[0]['instance_uuid']) self.assertEquals('device_id2', result[1]['instance_uuid']) + def test_get_fixed_ip_by_address_fails_for_no_ports(self): + address = self._mock_list_ports(port_data=[]) + api = quantumapi.API() + self.assertRaises(exception.FixedIpNotFoundForAddress, + api.get_fixed_ip_by_address, + self.context, address) + + def test_get_fixed_ip_by_address_succeeds_for_1_port(self): + address = self._mock_list_ports(port_data=self.port_data1) + api = quantumapi.API() + result = api.get_fixed_ip_by_address(self.context, address) + self.assertEquals('device_id1', result['instance_uuid']) + + def test_get_fixed_ip_by_address_fails_for_more_than_1_port(self): + address = self._mock_list_ports() + api = quantumapi.API() + self.assertRaises(exception.FixedIpAssociatedWithMultipleInstances, + api.get_fixed_ip_by_address, + self.context, address) + def _get_available_networks(self, prv_nets, pub_nets, req_ids=None): api = quantumapi.API() nets = prv_nets + pub_nets -- cgit