]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Fix IP allocation for multiple slaac subnets
authorXu Han Peng <xuhanp@cn.ibm.com>
Wed, 14 Jan 2015 06:15:49 +0000 (14:15 +0800)
committerXu Han Peng <xuhanp@cn.ibm.com>
Wed, 14 Jan 2015 16:53:59 +0000 (00:53 +0800)
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

neutron/db/db_base_plugin_v2.py
neutron/tests/unit/test_db_plugin.py

index e68d90ecbf357d938047d645a0bad1e509055025..44a0e380fa27da930348524c7bc463c5b32b0082 100644 (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)
index ae67648f267b5c4ae7b4a6e4821a6ad66b0ff4f3..69a20ad3aa1e803dde2e4fb307981bada786bc38 100644 (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:
@@ -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.