]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Support Subnets that are configured by external RAs
authorSean M. Collins <sean_collins2@cable.comcast.com>
Mon, 7 Apr 2014 18:35:58 +0000 (14:35 -0400)
committerSean M. Collins <sean_collins2@cable.comcast.com>
Mon, 9 Jun 2014 17:48:44 +0000 (13:48 -0400)
The IPv6 attributes for subnets provides support for IPv6 subnets that
are managed by non-OpenStack gateway devices, by creating a subnet that
has the ipv6_ra_mode attribute not set, and the ipv6_address_mode
attribute set to IPV6_SLAAC.

In order to support stateless IPv6, Neutron should calculate an IPv6
address based on the IPv6 prefix and MAC address via EUI-64 specification,
and assign this address to the port.

Implements blueprint ipv6-provider-nets-slaac

DocImpact

Change-Id: Ia76d76a67e0dc9f996a72e38d987a05aecc1b55f
Co-Authored-By: Xu Han Peng <xuhanp@cn.ibm.com>
Co-Authored-By: Dazhao Yu <dzyu@cn.ibm.com>
neutron/db/db_base_plugin_v2.py
neutron/tests/unit/oneconvergence/test_nvsd_plugin.py
neutron/tests/unit/test_db_plugin.py

index 7b3ae166ada775abf53e920048a9ab31e00f183d..7f61fe55265dc167db2f7bdc0fb4ed139480ddd7 100644 (file)
@@ -26,6 +26,7 @@ from sqlalchemy import sql
 from neutron.api.v2 import attributes
 from neutron.common import constants
 from neutron.common import exceptions as n_exc
+from neutron.common import ipv6_utils
 from neutron import context as ctx
 from neutron.db import api as db
 from neutron.db import models_v2
@@ -351,6 +352,12 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
             ip_address=ip_address,
             subnet_id=subnet_id).delete()
 
+    @staticmethod
+    def _check_if_subnet_uses_eui64(subnet):
+        """Check if ipv6 address will be calculated via EUI64."""
+        return (subnet['ipv6_address_mode'] == constants.IPV6_SLAAC
+                or subnet['ipv6_address_mode'] == constants.DHCPV6_STATELESS)
+
     @staticmethod
     def _generate_ip(context, subnets):
         try:
@@ -670,6 +677,20 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
                     v4.append(subnet)
                 else:
                     v6.append(subnet)
+            for subnet in v6:
+                if self._check_if_subnet_uses_eui64(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.
+                    mac = p['mac_address']
+                    prefix = subnet['cidr']
+                    ip_address = ipv6_utils.get_ipv6_addr_by_EUI64(
+                        prefix, mac)
+                    ips.append({'ip_address': ip_address.format(),
+                                'subnet_id': subnet['id']})
+                    v6.remove(subnet)
             version_subnets = [v4, v6]
             for subnets in version_subnets:
                 if subnets:
@@ -1345,7 +1366,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
         p = port['port']
         port_id = p.get('id') or uuidutils.generate_uuid()
         network_id = p['network_id']
-        mac_address = p['mac_address']
         # NOTE(jkoelker) Get the tenant_id outside of the session to avoid
         #                unneeded db action if the operation raises
         tenant_id = self._get_tenant_id_for_create(context, p)
@@ -1358,16 +1378,19 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
 
             # Ensure that a MAC address is defined and it is unique on the
             # network
-            if mac_address is attributes.ATTR_NOT_SPECIFIED:
-                mac_address = NeutronDbPluginV2._generate_mac(context,
-                                                              network_id)
+            if p['mac_address'] is attributes.ATTR_NOT_SPECIFIED:
+                #Note(scollins) Add the generated mac_address to the port,
+                #since _allocate_ips_for_port will need the mac when
+                #calculating an EUI-64 address for a v6 subnet
+                p['mac_address'] = NeutronDbPluginV2._generate_mac(context,
+                                                                   network_id)
             else:
                 # Ensure that the mac on the network is unique
                 if not NeutronDbPluginV2._check_unique_mac(context,
                                                            network_id,
-                                                           mac_address):
+                                                           p['mac_address']):
                     raise n_exc.MacAddressInUse(net_id=network_id,
-                                                mac=mac_address)
+                                                mac=p['mac_address'])
 
             # Returns the IP's for the port
             ips = self._allocate_ips_for_port(context, network, port)
@@ -1381,7 +1404,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
                                   name=p['name'],
                                   id=port_id,
                                   network_id=network_id,
-                                  mac_address=mac_address,
+                                  mac_address=p['mac_address'],
                                   admin_state_up=p['admin_state_up'],
                                   status=status,
                                   device_id=p['device_id'],
index 6cc8a14969f70fa81f36bf72399cadde38f7494e..564b8c56a81d4cab21ac1dd1486a3dacfc54d974 100644 (file)
@@ -115,6 +115,9 @@ class TestOneConvergencePluginPortsV2(test_plugin.TestPortsV2,
                 self.assertEqual(port['binding:vif_type'],
                                  portbindings.VIF_TYPE_OVS)
 
+    def test_ip_allocation_for_ipv6_subnet_slaac_adddress_mode(self):
+        self.skipTest("NVSD Plugin does not support IPV6.")
+
 
 class TestOneConvergenceBasicGet(test_plugin.TestBasicGet,
                                  OneConvergencePluginV2TestCase):
index d1ff9a90ce1d904593a737ee928163c774eb9daa..cfb3daa11ef454b4cb39fbe1ebcd64f6be06c295 100644 (file)
@@ -30,6 +30,7 @@ from neutron.api.v2 import attributes
 from neutron.api.v2 import router
 from neutron.common import constants
 from neutron.common import exceptions as n_exc
+from neutron.common import ipv6_utils
 from neutron.common import test_lib
 from neutron.common import utils
 from neutron import context
@@ -1397,6 +1398,24 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
                 self._delete('ports', port3['port']['id'])
                 self._delete('ports', port4['port']['id'])
 
+    def test_ip_allocation_for_ipv6_subnet_slaac_adddress_mode(self):
+        res = self._create_network(fmt=self.fmt, name='net',
+                                   admin_state_up=True)
+        network = self.deserialize(self.fmt, res)
+        v6_subnet = self._make_subnet(self.fmt, network,
+                                      gateway='fe80::1',
+                                      cidr='fe80::/80',
+                                      ip_version=6,
+                                      ipv6_ra_mode=None,
+                                      ipv6_address_mode=constants.IPV6_SLAAC)
+        port = self._make_port(self.fmt, network['network']['id'])
+        self.assertEqual(len(port['port']['fixed_ips']), 1)
+        port_mac = port['port']['mac_address']
+        subnet_cidr = v6_subnet['subnet']['cidr']
+        eui_addr = str(ipv6_utils.get_ipv6_addr_by_EUI64(subnet_cidr,
+                                                         port_mac))
+        self.assertEqual(port['port']['fixed_ips'][0]['ip_address'], eui_addr)
+
     def test_range_allocation(self):
         with self.subnet(gateway_ip='10.0.0.3',
                          cidr='10.0.0.0/29') as subnet: