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
(cherry picked from commit 1dac7c4347)
This commit is contained in:
Xu Han Peng 2015-01-14 14:15:49 +08:00 committed by Ihar Hrachyshka
parent e27f00d949
commit 755f2ad747
2 changed files with 90 additions and 22 deletions

View File

@ -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)

View File

@ -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:
@ -4143,6 +4171,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.