From: sridhargaddam Date: Tue, 14 Apr 2015 08:03:49 +0000 (+0000) Subject: Support BP:ipv6-router in Neutron HA Router X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=eec77c1872bba05ff9cf94e383a72c8abd6a17a6;p=openstack-build%2Fneutron-build.git Support BP:ipv6-router in Neutron HA Router 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) --- diff --git a/neutron/agent/l3/ha_router.py b/neutron/agent/l3/ha_router.py index 0b5e16930..a44f92ea1 100644 --- a/neutron/agent/l3/ha_router.py +++ b/neutron/agent/l3/ha_router.py @@ -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 diff --git a/neutron/agent/l3/router_info.py b/neutron/agent/l3/router_info.py index b9b54b610..9b7443cad 100644 --- a/neutron/agent/l3/router_info.py +++ b/neutron/agent/l3/router_info.py @@ -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, diff --git a/neutron/agent/linux/interface.py b/neutron/agent/linux/interface.py index 99654cfc8..82ecbcf89 100644 --- a/neutron/agent/linux/interface.py +++ b/neutron/agent/linux/interface.py @@ -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( diff --git a/neutron/tests/functional/agent/test_l3_agent.py b/neutron/tests/functional/agent/test_l3_agent.py index 0683422e2..c5746e366 100755 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -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) diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index a5016f815..f659628b9 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -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()