Browse Source

LBaaS VIP doesn't work after delete and re-add

When delete and re-add the same vip, we need to send gratuitous ARP
to flush the ARP cache in the Router.

Change-Id: Id97088abb95f4433a100abdae8c8726b3be42ed2
Closes-Bug: 1301165
changes/47/84647/13
LipingMao 8 years ago committed by Liping Mao
parent
commit
e7d7414548
  1. 4
      etc/lbaas_agent.ini
  2. 3
      etc/neutron/rootwrap.d/lbaas-haproxy.filters
  3. 17
      neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py
  4. 31
      neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py

4
etc/lbaas_agent.ini

@ -36,3 +36,7 @@
# The user group
# user_group = nogroup
# When delete and re-add the same vip, send this many gratuitous ARPs to flush
# the ARP cache in the Router. Set it below or equal to 0 to disable this feature.
# send_gratuitous_arp = 3

3
etc/neutron/rootwrap.d/lbaas-haproxy.filters

@ -20,3 +20,6 @@ mm-ctl: CommandFilter, mm-ctl, root
# ip_lib
ip: IpFilter, ip, root
ip_exec: IpNetnsExecFilter, ip, root
# arping
arping: CommandFilter, arping, root

17
neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py

@ -53,6 +53,13 @@ OPTS = [
default=USER_GROUP_DEFAULT,
help=_('The user group'),
deprecated_opts=[cfg.DeprecatedOpt('user_group')],
),
cfg.IntOpt(
'send_gratuitous_arp',
default=3,
help=_('When delete and re-add the same vip, send this many '
'gratuitous ARPs to flush the ARP cache in the Router. '
'Set it below or equal to 0 to disable this feature.'),
)
]
cfg.CONF.register_opts(OPTS, 'haproxy')
@ -270,6 +277,16 @@ class HaproxyNSDriver(agent_device_driver.AgentDeviceDriver):
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
namespace=namespace)
ip_wrapper.netns.execute(cmd, check_exit_code=False)
# When delete and re-add the same vip, we need to
# send gratuitous ARP to flush the ARP cache in the Router.
gratuitous_arp = self.conf.haproxy.send_gratuitous_arp
if gratuitous_arp > 0:
for ip in port['fixed_ips']:
cmd_arping = ['arping', '-U',
'-I', interface_name,
'-c', gratuitous_arp,
ip['ip_address']]
ip_wrapper.netns.execute(cmd_arping, check_exit_code=False)
def _unplug(self, namespace, port_id):
port_stub = {'id': port_id}

31
neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_namespace_driver.py

@ -33,7 +33,9 @@ class TestHaproxyNSDriver(base.BaseTestCase):
conf.haproxy.loadbalancer_state_path = '/the/path'
conf.interface_driver = 'intdriver'
conf.haproxy.user_group = 'test_group'
conf.haproxy.send_gratuitous_arp = 3
conf.AGENT.root_helper = 'sudo_test'
self.conf = conf
self.mock_importer = mock.patch.object(namespace_driver,
'importutils').start()
@ -278,15 +280,44 @@ class TestHaproxyNSDriver(base.BaseTestCase):
namespace=
'test_ns')
cmd = ['route', 'add', 'default', 'gw', '10.0.0.1']
cmd_arping = ['arping', '-U', '-I',
'test_interface', '-c',
self.conf.haproxy.send_gratuitous_arp, '10.0.0.2']
ip_wrap.assert_has_calls([
mock.call('sudo_test', namespace='test_ns'),
mock.call().netns.execute(cmd, check_exit_code=False),
mock.call().netns.execute(cmd_arping, check_exit_code=False),
])
dev_exists.return_value = True
self.assertRaises(exceptions.PreexistingDeviceFailure,
self.driver._plug, 'test_ns', test_port, False)
def test_plug_not_send_gratuitous_arp(self):
self.conf.haproxy.send_gratuitous_arp = 0
test_port = {'id': 'port_id',
'network_id': 'net_id',
'mac_address': 'mac_addr',
'fixed_ips': [{'ip_address': '10.0.0.2',
'subnet': {'cidr': '10.0.0.0/24',
'gateway_ip': '10.0.0.1'}}]}
with contextlib.nested(
mock.patch('neutron.agent.linux.ip_lib.device_exists'),
mock.patch('netaddr.IPNetwork'),
mock.patch('neutron.agent.linux.ip_lib.IPWrapper'),
) as (dev_exists, ip_net, ip_wrap):
self.vif_driver.get_device_name.return_value = 'test_interface'
dev_exists.return_value = False
ip_net.return_value = ip_net
ip_net.prefixlen = 24
self.driver._plug('test_ns', test_port)
cmd = ['route', 'add', 'default', 'gw', '10.0.0.1']
expected = [
mock.call('sudo_test', namespace='test_ns'),
mock.call().netns.execute(cmd, check_exit_code=False)]
self.assertEqual(expected, ip_wrap.mock_calls)
def test_plug_no_gw(self):
test_port = {'id': 'port_id',
'network_id': 'net_id',

Loading…
Cancel
Save