Merge "Disable RA and IPv6 forwarding on backup HA routers"

This commit is contained in:
Jenkins 2017-03-30 19:15:52 +00:00 committed by Gerrit Code Review
commit 4ed53a8807
7 changed files with 108 additions and 23 deletions

View File

@ -121,26 +121,36 @@ class AgentMixin(object):
if ri is None:
return
self._configure_ipv6_ra_on_ext_gw_port_if_necessary(ri, state)
# TODO(dalvarez): Fix bug 1677279 by moving the IPv6 parameters
# configuration to keepalived-state-change in order to remove the
# dependency that currently exists on l3-agent running for the IPv6
# failover.
self._configure_ipv6_params_on_ext_gw_port_if_necessary(ri, state)
if self.conf.enable_metadata_proxy:
self._update_metadata_proxy(ri, router_id, state)
self._update_radvd_daemon(ri, state)
self.pd.process_ha_state(router_id, state == 'master')
self.state_change_notifier.queue_event((router_id, state))
def _configure_ipv6_ra_on_ext_gw_port_if_necessary(self, ri, state):
def _configure_ipv6_params_on_ext_gw_port_if_necessary(self, ri, state):
# If ipv6 is enabled on the platform, ipv6_gateway config flag is
# not set and external_network associated to the router does not
# include any IPv6 subnet, enable the gateway interface to accept
# Router Advts from upstream router for default route.
# Router Advts from upstream router for default route on master
# instances as well as ipv6 forwarding. Otherwise, disable them.
ex_gw_port_id = ri.ex_gw_port and ri.ex_gw_port['id']
if state == 'master' and ex_gw_port_id:
interface_name = ri.get_external_device_name(ex_gw_port_id)
if ri.router.get('distributed', False):
namespace = ri.ha_namespace
else:
namespace = ri.ns_name
ri._enable_ra_on_gw(ri.ex_gw_port, namespace, interface_name)
if not ex_gw_port_id:
return
interface_name = ri.get_external_device_name(ex_gw_port_id)
if ri.router.get('distributed', False):
namespace = ri.ha_namespace
else:
namespace = ri.ns_name
enable = state == 'master'
ri._configure_ipv6_params_on_gw(ri.ex_gw_port, namespace,
interface_name, enable)
def _update_metadata_proxy(self, ri, router_id, state):
if state == 'master':

View File

@ -396,8 +396,13 @@ class HaRouter(router.RouterInfo):
self._plug_external_gateway(ex_gw_port, interface_name, self.ns_name)
self._add_gateway_vip(ex_gw_port, interface_name)
self._disable_ipv6_addressing_on_interface(interface_name)
if self.ha_state == 'master':
self._enable_ra_on_gw(ex_gw_port, self.ns_name, interface_name)
# Enable RA and IPv6 forwarding only for master instances. This will
# prevent backup routers from sending packets to the upstream switch
# and disrupt connections.
enable = self.ha_state == 'master'
self._configure_ipv6_params_on_gw(ex_gw_port, self.ns_name,
interface_name, enable)
def external_gateway_updated(self, ex_gw_port, interface_name):
self._plug_external_gateway(

View File

@ -647,14 +647,22 @@ class RouterInfo(object):
device = ip_lib.IPDevice(device_name, namespace=namespace)
device.route.add_route(subnet['gateway_ip'], scope='link')
def _enable_ra_on_gw(self, ex_gw_port, ns_name, interface_name):
gateway_ips = self._get_external_gw_ips(ex_gw_port)
if not self.use_ipv6 or self.is_v6_gateway_set(gateway_ips):
def _configure_ipv6_params_on_gw(self, ex_gw_port, ns_name, interface_name,
enabled):
if not self.use_ipv6:
return
# There is no IPv6 gw_ip, use RouterAdvt for default route.
self.driver.configure_ipv6_ra(ns_name, interface_name,
n_const.ACCEPT_RA_WITH_FORWARDING)
if not enabled:
self.driver.configure_ipv6_ra(ns_name, interface_name,
n_const.ACCEPT_RA_DISABLED)
else:
gateway_ips = self._get_external_gw_ips(ex_gw_port)
if self.is_v6_gateway_set(gateway_ips):
return
# There is no IPv6 gw_ip, use RouterAdvt for default route.
self.driver.configure_ipv6_ra(ns_name, interface_name,
n_const.ACCEPT_RA_WITH_FORWARDING)
self.driver.configure_ipv6_forwarding(ns_name, interface_name, enabled)
def _external_gateway_added(self, ex_gw_port, interface_name,
ns_name, preserve_ips):
@ -690,7 +698,8 @@ class RouterInfo(object):
for ip in gateway_ips:
device.route.add_gateway(ip)
self._enable_ra_on_gw(ex_gw_port, ns_name, interface_name)
self._configure_ipv6_params_on_gw(ex_gw_port, ns_name, interface_name,
True)
for fixed_ip in ex_gw_port['fixed_ips']:
ip_lib.send_ip_addr_adv_notif(ns_name,

View File

@ -246,6 +246,13 @@ class LinuxInterfaceDriver(object):
'value': value}]
ip_lib.sysctl(cmd, namespace=namespace)
@staticmethod
def configure_ipv6_forwarding(namespace, dev_name, enabled):
"""Configure IPv6 forwarding on an interface."""
cmd = ['net.ipv6.conf.%(dev)s.forwarding=%(enabled)s' %
{'dev': dev_name, 'enabled': int(enabled)}]
ip_lib.sysctl(cmd, namespace=namespace)
@abc.abstractmethod
def plug_new(self, network_id, port_id, device_name, mac_address,
bridge=None, namespace=None, prefix=None, mtu=None):

View File

@ -33,6 +33,7 @@ from neutron.agent import l3_agent as l3_agent_main
from neutron.agent.linux import external_process
from neutron.agent.linux import ip_lib
from neutron.agent.linux import keepalived
from neutron.common import constants as n_const
from neutron.common import utils as common_utils
from neutron.conf.agent import common as agent_config
from neutron.conf import common as common_config
@ -215,14 +216,23 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
def _assert_external_device(self, router):
self.assertTrue(self._check_external_device(router))
def _assert_ipv6_accept_ra(self, router):
def _assert_ipv6_accept_ra(self, router, enabled=True):
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)
self.assertEqual(enabled, int(ra_state) != n_const.ACCEPT_RA_DISABLED)
def _assert_ipv6_forwarding(self, router, enabled=True):
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)
fwd_state = ip_wrapper.netns.execute(['sysctl', '-b',
'net.ipv6.conf.%s.forwarding' % external_device_name])
self.assertEqual(int(enabled), int(fwd_state))
def _router_lifecycle(self, enable_ha, ip_version=4,
dual_stack=False, v6_ext_gw_with_sub=True,

View File

@ -101,9 +101,10 @@ class L3HATestCase(framework.L3AgentTestFramework):
@testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(),
"IPv6 is not enabled")
def test_ipv6_router_advts_after_router_state_change(self):
def test_ipv6_router_advts_and_fwd_after_router_state_change_master(self):
# Schedule router to l3 agent, and then add router gateway. Verify
# that router gw interface is configured to receive Router Advts.
# that router gw interface is configured to receive Router Advts and
# IPv6 forwarding is enabled.
router_info = l3_test_common.prepare_router_data(
enable_snat=True, enable_ha=True, dual_stack=True, enable_gw=False)
router = self.manage_router(self.agent, router_info)
@ -113,6 +114,25 @@ class L3HATestCase(framework.L3AgentTestFramework):
router_info['gw_port'] = ex_port
router.process()
self._assert_ipv6_accept_ra(router)
self._assert_ipv6_forwarding(router)
@testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(),
"IPv6 is not enabled")
def test_ipv6_router_advts_and_fwd_after_router_state_change_backup(self):
# Schedule router to l3 agent, and then add router gateway. Verify
# that router gw interface is configured to discard Router Advts and
# IPv6 forwarding is disabled.
router_info = l3_test_common.prepare_router_data(
enable_snat=True, enable_ha=True, dual_stack=True, enable_gw=False)
router = self.manage_router(self.agent, router_info)
self.fail_ha_router(router)
common_utils.wait_until_true(lambda: router.ha_state == 'backup')
_ext_dev_name, ex_port = l3_test_common.prepare_ext_gw_test(
mock.Mock(), router)
router_info['gw_port'] = ex_port
router.process()
self._assert_ipv6_accept_ra(router, False)
self._assert_ipv6_forwarding(router, False)
def test_keepalived_configuration(self):
router_info = self.generate_router_info(enable_ha=True)

View File

@ -210,6 +210,30 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent.enqueue_state_change(router.id, 'master')
self.assertFalse(agent._update_metadata_proxy.call_count)
def _test__configure_ipv6_params_on_ext_gw_port_if_necessary_helper(
self, state, enable_expected):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router_info = l3router.RouterInfo(agent, _uuid(), {}, **self.ri_kwargs)
router_info.ex_gw_port = {'id': _uuid()}
with mock.patch.object(router_info, '_configure_ipv6_params_on_gw'
) as mock_configure_ipv6:
agent._configure_ipv6_params_on_ext_gw_port_if_necessary(
router_info, state)
interface_name = router_info.get_external_device_name(
router_info.ex_gw_port['id'])
mock_configure_ipv6.assert_called_once_with(
router_info.ex_gw_port, router_info.ns_name, interface_name,
enable_expected)
def test__configure_ipv6_params_on_ext_gw_port_if_necessary_master(self):
self._test__configure_ipv6_params_on_ext_gw_port_if_necessary_helper(
'master', True)
def test__configure_ipv6_params_on_ext_gw_port_if_necessary_backup(self):
self._test__configure_ipv6_params_on_ext_gw_port_if_necessary_helper(
'backup', False)
def test_check_ha_state_for_router_master_standby(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = mock.Mock()