From: Xu Han Peng Date: Wed, 14 Jan 2015 06:15:49 +0000 (+0800) Subject: Fix IP allocation for multiple slaac subnets X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=1dac7c43471f18ea83784986b5ffeab83e26323b;p=openstack-build%2Fneutron-build.git Fix IP allocation for multiple slaac subnets Currently when one network contains multiple IPv6 slaac subnets, the IP allocation for the port on this network is not correct. Only the first fixed ip is based on EUI64. This is caused by a wrong usage of removing element in list. Change-Id: I1b0a478997371afeae34c0536d5fed7a18223f63 Closes-Bug:1358709 --- diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index e68d90ecb..44a0e380f 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -552,32 +552,30 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, subnets = self.get_subnets(context, filters=filter) # Split into v4 and v6 subnets v4 = [] - v6 = [] + v6_stateful = [] + v6_stateless = [] for subnet in subnets: if subnet['ip_version'] == 4: v4.append(subnet) else: - v6.append(subnet) - for subnet in v6: - if ipv6_utils.is_auto_address_subnet(subnet): - #(dzyu) If true, calculate an IPv6 address - # by mac address and prefix, then remove this - # subnet from the array of subnets that will be passed - # to the _generate_ip() function call, since we just - # generated an IP. - prefix = subnet['cidr'] - ip_address = ipv6_utils.get_ipv6_addr_by_EUI64( - prefix, p['mac_address']) - if not self._check_unique_ip( - context, p['network_id'], - subnet['id'], ip_address.format()): - raise n_exc.IpAddressInUse( - net_id=p['network_id'], - ip_address=ip_address.format()) - ips.append({'ip_address': ip_address.format(), - 'subnet_id': subnet['id']}) - v6.remove(subnet) - version_subnets = [v4, v6] + if ipv6_utils.is_auto_address_subnet(subnet): + v6_stateless.append(subnet) + else: + v6_stateful.append(subnet) + + for subnet in v6_stateless: + prefix = subnet['cidr'] + ip_address = ipv6_utils.get_ipv6_addr_by_EUI64( + prefix, p['mac_address']) + if not self._check_unique_ip( + context, p['network_id'], + subnet['id'], ip_address.format()): + raise n_exc.IpAddressInUse( + net_id=p['network_id'], + ip_address=ip_address.format()) + ips.append({'ip_address': ip_address.format(), + 'subnet_id': subnet['id']}) + version_subnets = [v4, v6_stateful] for subnets in version_subnets: if subnets: result = NeutronDbPluginV2._generate_ip(context, subnets) diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py index ae67648f2..69a20ad3a 100644 --- a/neutron/tests/unit/test_db_plugin.py +++ b/neutron/tests/unit/test_db_plugin.py @@ -1492,6 +1492,34 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s port_mac)) self.assertEqual(port['port']['fixed_ips'][0]['ip_address'], eui_addr) + def test_ip_allocation_for_ipv6_2_subnet_slaac_mode(self): + res = self._create_network(fmt=self.fmt, name='net', + admin_state_up=True) + network = self.deserialize(self.fmt, res) + v6_subnet_1 = self._make_subnet(self.fmt, network, + gateway='2001:100::1', + cidr='2001:100::0/64', + ip_version=6, + ipv6_ra_mode=constants.IPV6_SLAAC) + v6_subnet_2 = self._make_subnet(self.fmt, network, + gateway='2001:200::1', + cidr='2001:200::0/64', + ip_version=6, + ipv6_ra_mode=constants.IPV6_SLAAC) + port = self._make_port(self.fmt, network['network']['id']) + self.assertEqual(len(port['port']['fixed_ips']), 2) + port_mac = port['port']['mac_address'] + cidr_1 = v6_subnet_1['subnet']['cidr'] + cidr_2 = v6_subnet_2['subnet']['cidr'] + eui_addr_1 = str(ipv6_utils.get_ipv6_addr_by_EUI64(cidr_1, + port_mac)) + eui_addr_2 = str(ipv6_utils.get_ipv6_addr_by_EUI64(cidr_2, + port_mac)) + self.assertEqual(port['port']['fixed_ips'][0]['ip_address'], + eui_addr_1) + self.assertEqual(port['port']['fixed_ips'][1]['ip_address'], + eui_addr_2) + def test_range_allocation(self): with self.subnet(gateway_ip='10.0.0.3', cidr='10.0.0.0/29') as subnet: @@ -4147,6 +4175,48 @@ class TestNeutronDbPluginV2(base.BaseTestCase): self._validate_rebuild_availability_ranges(pools, allocations, expected) + def _test__allocate_ips_for_port(self, subnets, port, expected): + plugin = db_base_plugin_v2.NeutronDbPluginV2() + with mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2, + 'get_subnets') as get_subnets: + with mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2, + '_check_unique_ip') as check_unique: + context = mock.Mock() + get_subnets.return_value = subnets + check_unique.return_value = True + actual = plugin._allocate_ips_for_port(context, port) + self.assertEqual(expected, actual) + + def test__allocate_ips_for_port_2_slaac_subnets(self): + subnets = [ + { + 'cidr': u'2001:100::/64', + 'enable_dhcp': True, + 'gateway_ip': u'2001:100::1', + 'id': u'd1a28edd-bd83-480a-bd40-93d036c89f13', + 'ip_version': 6, + 'ipv6_address_mode': None, + 'ipv6_ra_mode': u'slaac'}, + { + 'cidr': u'2001:200::/64', + 'enable_dhcp': True, + 'gateway_ip': u'2001:200::1', + 'id': u'dc813d3d-ed66-4184-8570-7325c8195e28', + 'ip_version': 6, + 'ipv6_address_mode': None, + 'ipv6_ra_mode': u'slaac'}] + port = {'port': { + 'network_id': 'fbb9b578-95eb-4b79-a116-78e5c4927176', + 'fixed_ips': attributes.ATTR_NOT_SPECIFIED, + 'mac_address': '12:34:56:78:44:ab'}} + expected = [] + for subnet in subnets: + addr = str(ipv6_utils.get_ipv6_addr_by_EUI64( + subnet['cidr'], port['port']['mac_address'])) + expected.append({'ip_address': addr, 'subnet_id': subnet['id']}) + + self._test__allocate_ips_for_port(subnets, port, expected) + class NeutronDbPluginV2AsMixinTestCase(testlib_api.SqlTestCase): """Tests for NeutronDbPluginV2 as Mixin.