From 89489d2720c80c3465e36dad566aa835215fb92e Mon Sep 17 00:00:00 2001 From: sridhargaddam Date: Tue, 14 Apr 2015 08:03:49 +0000 Subject: [PATCH] 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 extends the functionality to an HA router. Implements: blueprint ipv6-router Change-Id: I26dc5ce9e46c74423358aa8a9559bc6c7cbdf85e --- neutron/agent/l3/ha_router.py | 9 +++--- neutron/agent/l3/router_info.py | 19 +++++++----- neutron/agent/linux/interface.py | 4 +-- .../tests/functional/agent/test_l3_agent.py | 29 +++++++++++++++++-- neutron/tests/unit/agent/l3/test_agent.py | 5 ++-- 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/neutron/agent/l3/ha_router.py b/neutron/agent/l3/ha_router.py index ebccf32b4..dce0dc1cb 100644 --- a/neutron/agent/l3/ha_router.py +++ b/neutron/agent/l3/ha_router.py @@ -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 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 2c9907471..cda969ced 100644 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -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) diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 5db78acbb..b4f96a0b5 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -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() -- 2.45.2