]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Support BP:ipv6-router in Neutron HA Router
authorsridhargaddam <sridhar.gaddam@enovance.com>
Tue, 14 Apr 2015 08:03:49 +0000 (08:03 +0000)
committersridhargaddam <sridhar.gaddam@enovance.com>
Mon, 11 May 2015 09:33:01 +0000 (09:33 +0000)
blueprint ipv6-router (ChangeID:Iaefa95f788053ded9fc9c7ff6845c3030c6fd6df),
supports an IPv6 Router where the router gateway port has no subnet.

The BP implements the following. If an external network (without any subnet)
is attached to the Neutron router, it reads the ipv6_gateway config parameter
(LLA of upstream router) from l3_agent.ini file and adds a default route that
points to this LLA.  If the ipv6_gateway config value is not configured, it
would configure the gateway interface to accept router advts from upstream
router to build the default route.

For an HA router, we would have to configure keepalived to perform this
operation. This patch extends the functionality to an HA router.

Implements: blueprint ipv6-router
Change-Id: I26dc5ce9e46c74423358aa8a9559bc6c7cbdf85e

neutron/agent/l3/ha_router.py
neutron/agent/l3/router_info.py
neutron/agent/linux/interface.py
neutron/tests/functional/agent/test_l3_agent.py
neutron/tests/unit/agent/l3/test_agent.py

index ebccf32b4ab37c4504c678a5a1fa0f791b9851c1..dce0dc1cb3d16f5034945449d261090dea9f8931 100644 (file)
@@ -186,11 +186,9 @@ class HaRouter(router.RouterInfo):
         self.routes = new_routes
 
     def _add_default_gw_virtual_route(self, ex_gw_port, interface_name):
-        subnets = ex_gw_port.get('subnets', [])
         default_gw_rts = []
-        for subnet in subnets:
-            gw_ip = subnet['gateway_ip']
-            if gw_ip:
+        gateway_ips, enable_ra_on_gw = self._get_external_gw_ips(ex_gw_port)
+        for gw_ip in gateway_ips:
                 # TODO(Carl) This is repeated everywhere.  A method would
                 # be nice.
                 default_gw = (n_consts.IPv4_ANY if
@@ -201,6 +199,9 @@ class HaRouter(router.RouterInfo):
                     default_gw, gw_ip, interface_name))
         instance.virtual_routes.gateway_routes = default_gw_rts
 
+        if enable_ra_on_gw:
+            self.driver.configure_ipv6_ra(self.ns_name, interface_name)
+
     def _should_delete_ipv6_lladdr(self, ipv6_lladdr):
         """Only the master should have any IP addresses configured.
         Let keepalived manage IPv6 link local addresses, the same way we let
index b9b54b6107dd692f085207711ce665bd0181bb9b..9b7443cad419cd1c67728aeff5689bcba3c70532 100644 (file)
@@ -427,13 +427,7 @@ class RouterInfo(object):
                              namespace=ns_name,
                              prefix=EXTERNAL_DEV_PREFIX)
 
-    def _external_gateway_added(self, ex_gw_port, interface_name,
-                                ns_name, preserve_ips):
-        self._plug_external_gateway(ex_gw_port, interface_name, ns_name)
-
-        # Build up the interface and gateway IP addresses that
-        # will be added to the interface.
-        ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
+    def _get_external_gw_ips(self, ex_gw_port):
         gateway_ips = []
         enable_ra_on_gw = False
         if 'subnets' in ex_gw_port:
@@ -449,6 +443,17 @@ class RouterInfo(object):
                 # ipv6_gateway is also not configured.
                 # Use RA for default route.
                 enable_ra_on_gw = True
+        return gateway_ips, enable_ra_on_gw
+
+    def _external_gateway_added(self, ex_gw_port, interface_name,
+                                ns_name, preserve_ips):
+        self._plug_external_gateway(ex_gw_port, interface_name, ns_name)
+
+        # Build up the interface and gateway IP addresses that
+        # will be added to the interface.
+        ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
+
+        gateway_ips, enable_ra_on_gw = self._get_external_gw_ips(ex_gw_port)
         self.driver.init_l3(interface_name,
                             ip_cidrs,
                             namespace=ns_name,
index 99654cfc8f47d2b77f112f2268a3db5afbf1fd96..82ecbcf89afad0e46030c2c6330d5a85cee32a7e 100644 (file)
@@ -117,7 +117,7 @@ class LinuxInterfaceDriver(object):
             device.route.add_gateway(gateway_ip)
 
         if enable_ra_on_gw:
-            self._configure_ipv6_ra(namespace, device_name)
+            self.configure_ipv6_ra(namespace, device_name)
 
         new_onlink_routes = set(s['cidr'] for s in extra_subnets)
         existing_onlink_routes = set(
@@ -172,7 +172,7 @@ class LinuxInterfaceDriver(object):
         return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
 
     @staticmethod
-    def _configure_ipv6_ra(namespace, dev_name):
+    def configure_ipv6_ra(namespace, dev_name):
         """Configure acceptance of IPv6 route advertisements on an intf."""
         # Learn the default router's IP address via RAs
         ip_lib.IPWrapper(namespace=namespace).netns.execute(
index 2c9907471296f08bbff2efb330914b237d940537..cda969ced4f65b1ae5a6136e9d5c6ba105ca2b2b 100644 (file)
@@ -113,9 +113,6 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
             enable_fip = False
             extra_routes = False
 
-        if not v6_ext_gw_with_sub:
-            self.agent.conf.set_override('ipv6_gateway',
-                                         'fe80::f816:3eff:fe2e:1')
         return test_l3_agent.prepare_router_data(ip_version=ip_version,
                                                  enable_snat=enable_snat,
                                                  enable_floating_ip=enable_fip,
@@ -385,6 +382,8 @@ class L3AgentTestCase(L3AgentTestFramework):
         self._router_lifecycle(enable_ha=False, dual_stack=True)
 
     def test_legacy_router_lifecycle_with_no_gateway_subnet(self):
+        self.agent.conf.set_override('ipv6_gateway',
+                                     'fe80::f816:3eff:fe2e:1')
         self._router_lifecycle(enable_ha=False, dual_stack=True,
                                v6_ext_gw_with_sub=False)
 
@@ -439,6 +438,18 @@ class L3AgentTestCase(L3AgentTestFramework):
     def test_ipv6_ha_router_lifecycle(self):
         self._router_lifecycle(enable_ha=True, ip_version=6)
 
+    def test_ipv6_ha_router_lifecycle_with_no_gw_subnet(self):
+        self.agent.conf.set_override('ipv6_gateway',
+                                     'fe80::f816:3eff:fe2e:1')
+        self._router_lifecycle(enable_ha=True, ip_version=6,
+                               v6_ext_gw_with_sub=False)
+
+    def test_ipv6_ha_router_lifecycle_with_no_gw_subnet_for_router_advts(self):
+        # Verify that router gw interface is configured to receive Router
+        # Advts from upstream router when no external gateway is configured.
+        self._router_lifecycle(enable_ha=True, dual_stack=True,
+                               v6_ext_gw_with_sub=False)
+
     def test_keepalived_configuration(self):
         router_info = self.generate_router_info(enable_ha=True)
         router = self.manage_router(self.agent, router_info)
@@ -593,6 +604,18 @@ class L3AgentTestCase(L3AgentTestFramework):
             self._assert_extra_routes(router)
         self._assert_metadata_chains(router)
 
+        # Verify router gateway interface is configured to receive Router Advts
+        # when IPv6 is enabled and no IPv6 gateway is configured.
+        if router.use_ipv6 and not v6_ext_gw_with_sub:
+            if not self.agent.conf.ipv6_gateway:
+                external_port = router.get_ex_gw_port()
+                external_device_name = router.get_external_device_name(
+                    external_port['id'])
+                ip_wrapper = ip_lib.IPWrapper(namespace=router.ns_name)
+                ra_state = ip_wrapper.netns.execute(['sysctl', '-b',
+                    'net.ipv6.conf.%s.accept_ra' % external_device_name])
+                self.assertEqual('2', ra_state)
+
         if enable_ha:
             self._assert_ha_device(router)
             self.assertTrue(router.keepalived_manager.get_process().active)
index 5db78acbbadf3b8c51e48ad653228668231eb4a0..b4f96a0b57d1a945fc587b7b7023b203bc562ee9 100644 (file)
@@ -182,20 +182,19 @@ def prepare_router_data(ip_version=4, enable_snat=None, num_internal_ports=1,
                         v6_ext_gw_with_sub=True, **kwargs):
     fixed_ips = []
     subnets = []
+    gateway_mac = kwargs.get('gateway_mac', 'ca:fe:de:ad:be:ee')
     for loop_version in (4, 6):
         if loop_version == 4 and (ip_version == 4 or dual_stack):
             ip_address = kwargs.get('ip_address', '19.4.4.4')
             prefixlen = 24
             subnet_cidr = kwargs.get('subnet_cidr', '19.4.4.0/24')
             gateway_ip = kwargs.get('gateway_ip', '19.4.4.1')
-            gateway_mac = kwargs.get('gateway_mac', 'ca:fe:de:ad:be:ee')
         elif (loop_version == 6 and (ip_version == 6 or dual_stack) and
               v6_ext_gw_with_sub):
             ip_address = kwargs.get('ip_address', 'fd00::4')
             prefixlen = 64
             subnet_cidr = kwargs.get('subnet_cidr', 'fd00::/64')
             gateway_ip = kwargs.get('gateway_ip', 'fd00::1')
-            gateway_mac = kwargs.get('gateway_mac', 'ca:fe:de:ad:be:ee')
         else:
             continue
         subnet_id = _uuid()
@@ -205,7 +204,7 @@ def prepare_router_data(ip_version=4, enable_snat=None, num_internal_ports=1,
         subnets.append({'id': subnet_id,
                         'cidr': subnet_cidr,
                         'gateway_ip': gateway_ip})
-    if not fixed_ips:
+    if not fixed_ips and v6_ext_gw_with_sub:
         raise ValueError("Invalid ip_version: %s" % ip_version)
 
     router_id = _uuid()