]> 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)
committerSridhar Gaddam <sridhar.gaddam@enovance.com>
Thu, 4 Jun 2015 02:19:41 +0000 (02:19 +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 is a bug fix for the broken feature in kilo.

Conflicts:
        neutron/agent/l3/ha_router.py
        neutron/tests/unit/agent/l3/test_agent.py

Implements: blueprint ipv6-router
Change-Id: I26dc5ce9e46c74423358aa8a9559bc6c7cbdf85e
(cherry picked from commit 89489d2720c80c3465e36dad566aa835215fb92e)

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 0b5e169303a180ad01c08ba949c669f1ebf31bff..a44f92ea156a8344ab6227aedd52663a1c6f3705 100644 (file)
@@ -192,10 +192,8 @@ 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', [])
-        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
@@ -209,6 +207,9 @@ class HaRouter(router.RouterInfo):
                     keepalived.KeepalivedVirtualRoute(
                         default_gw, gw_ip, interface_name))
 
+        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 0683422e21756fe631304d6c1dce87f88e41419e..c5746e3666db8d0223e076d3cd8a85d622563125 100755 (executable)
@@ -119,9 +119,6 @@ class L3AgentTestFramework(base.BaseOVSLinuxTestCase):
             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,
@@ -399,6 +396,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)
 
@@ -453,6 +452,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)
@@ -607,6 +618,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 a5016f815f916908ab1a4e179b795600acd953eb..f659628b91d495c3cdfb21a9db03ebe509693a7c 100644 (file)
@@ -204,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()