diff --git a/neutron/agent/l3/ha_router.py b/neutron/agent/l3/ha_router.py index c204f5f9b64..222e1d1df36 100644 --- a/neutron/agent/l3/ha_router.py +++ b/neutron/agent/l3/ha_router.py @@ -63,6 +63,11 @@ class HaRouterNamespace(namespaces.RouterNamespace): super(HaRouterNamespace, self).create(ipv6_forwarding=False) # HA router namespaces should not have ip_nonlocal_bind enabled ip_lib.set_ip_nonlocal_bind_for_namespace(self.name, 0) + # Linux should not automatically assign link-local addr for HA routers + # They are managed by keepalived + ip_wrapper = ip_lib.IPWrapper(namespace=self.name) + cmd = ['sysctl', '-w', 'net.ipv6.conf.all.addr_gen_mode=1'] + ip_wrapper.netns.execute(cmd, privsep_exec=True) class HaRouter(router.RouterInfo): diff --git a/neutron/tests/functional/agent/l3/framework.py b/neutron/tests/functional/agent/l3/framework.py index af476b7085a..a25d52ecf37 100644 --- a/neutron/tests/functional/agent/l3/framework.py +++ b/neutron/tests/functional/agent/l3/framework.py @@ -276,6 +276,19 @@ class L3AgentTestFramework(base.BaseSudoTestCase): def _assert_external_device(self, router): self.assertTrue(self._check_external_device(router)) + def _wait_until_addr_gen_mode_has_state( + self, ns_name, state): + ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) + + def _addr_gen_mode_state(): + addr_gen_mode_state = ip_wrapper.netns.execute( + ['sysctl', '-b', 'net.ipv6.conf.all.addr_gen_mode'], + privsep_exec=True) + return ( + state == int(addr_gen_mode_state)) + + common_utils.wait_until_true(_addr_gen_mode_state) + def _wait_until_ipv6_accept_ra_has_state( self, ns_name, device_name, enabled): ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) diff --git a/neutron/tests/functional/agent/l3/test_ha_router.py b/neutron/tests/functional/agent/l3/test_ha_router.py index 23e519c4e2f..710025c7f2c 100644 --- a/neutron/tests/functional/agent/l3/test_ha_router.py +++ b/neutron/tests/functional/agent/l3/test_ha_router.py @@ -352,6 +352,37 @@ class L3HATestCase(framework.L3AgentTestFramework): raise self.assertEqual(0, ip_nonlocal_bind_value) + @testtools.skipUnless(netutils.is_ipv6_enabled(), "IPv6 is not enabled") + def test_ha_router_addr_gen_mode(self): + router_info = self.generate_router_info(enable_ha=True) + router_info[constants.HA_INTERFACE_KEY]['status'] = ( + constants.PORT_STATUS_DOWN) + router = self.manage_router(self.agent, router_info) + external_port = router.get_ex_gw_port() + external_device_name = router.get_external_device_name( + external_port['id']) + + def check_gw_lla_status(expected): + lladdr = ip_lib.get_ipv6_lladdr( + external_port['mac_address']) + exists = ip_lib.device_exists_with_ips_and_mac( + external_device_name, [lladdr], + external_port['mac_address'], router.ns_name) + self.assertEqual(expected, exists) + + self.wait_until_ha_router_has_state(router, 'backup') + self._wait_until_addr_gen_mode_has_state( + router.ns_name, 1) + check_gw_lla_status(False) + + router.router[constants.HA_INTERFACE_KEY]['status'] = ( + constants.PORT_STATUS_ACTIVE) + self.agent._process_updated_router(router.router) + self.wait_until_ha_router_has_state(router, 'primary') + self._wait_until_addr_gen_mode_has_state( + router.ns_name, 1) + check_gw_lla_status(True) + @testtools.skipUnless(netutils.is_ipv6_enabled(), "IPv6 is not enabled") def test_ha_router_namespace_has_ipv6_forwarding_disabled(self): router_info = self.generate_router_info(enable_ha=True)