else:
# As GARP is processed in a distinct thread the call below
# won't raise an exception to be handled.
- self._send_gratuitous_arp_packet(
- ri.ns_name, interface_name, fip_ip)
+ ip_lib.send_gratuitous_arp(ri.ns_name,
+ interface_name,
+ fip_ip,
+ self.conf.send_arp_for_ha,
+ self.root_helper)
return l3_constants.FLOATINGIP_STATUS_ACTIVE
def _remove_floating_ip(self, ri, device, ip_cidr):
def _get_ex_gw_port(self, ri):
return ri.router.get('gw_port')
- def _arping(self, ns_name, interface_name, ip_address, distributed=False):
- if distributed:
- device = ip_lib.IPDevice(interface_name, self.root_helper,
- namespace=ns_name)
- ip_cidr = str(ip_address) + FLOATING_IP_CIDR_SUFFIX
- net = netaddr.IPNetwork(ip_cidr)
- device.addr.add(net.version, ip_cidr, str(net.broadcast))
-
- arping_cmd = ['arping', '-A',
- '-I', interface_name,
- '-c', self.conf.send_arp_for_ha,
- ip_address]
- try:
- ip_wrapper = ip_lib.IPWrapper(self.root_helper,
- namespace=ns_name)
- ip_wrapper.netns.execute(arping_cmd, check_exit_code=True)
- except Exception:
- LOG.exception(_LE("Failed sending gratuitous ARP."))
- if distributed:
- device.addr.delete(net.version, ip_cidr)
-
- def _send_gratuitous_arp_packet(self, ns_name, interface_name, ip_address,
- distributed=False):
- if self.conf.send_arp_for_ha > 0:
- eventlet.spawn_n(self._arping, ns_name, interface_name, ip_address,
- distributed)
-
def get_internal_device_name(self, port_id):
return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
extra_subnets=ex_gw_port.get('extra_subnets', []),
preserve_ips=preserve_ips)
ip_address = ex_gw_port['ip_cidr'].split('/')[0]
- self._send_gratuitous_arp_packet(ns_name,
- interface_name, ip_address)
+ ip_lib.send_gratuitous_arp(ns_name,
+ interface_name,
+ ip_address,
+ self.conf.send_arp_for_ha,
+ self.root_helper)
def external_gateway_removed(self, ri, ex_gw_port, interface_name):
if ri.router['distributed']:
self.driver.init_l3(interface_name, [internal_cidr],
namespace=ns_name)
ip_address = internal_cidr.split('/')[0]
- self._send_gratuitous_arp_packet(ns_name, interface_name,
- ip_address)
+ ip_lib.send_gratuitous_arp(ns_name,
+ interface_name,
+ ip_address,
+ self.conf.send_arp_for_ha,
+ self.root_helper)
def internal_network_added(self, ri, port):
network_id = port['network_id']
self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']],
namespace=ns_name)
ip_address = ex_gw_port['ip_cidr'].split('/')[0]
- self._send_gratuitous_arp_packet(ns_name, interface_name, ip_address)
+ ip_lib.send_gratuitous_arp(ns_name,
+ interface_name,
+ ip_address,
+ self.conf.send_arp_for_ha,
+ self.root_helper)
gw_ip = ex_gw_port['subnet']['gateway_ip']
if gw_ip:
device.route.add_route(fip_cidr, str(rtr_2_fip.ip))
interface_name = (
self.get_fip_ext_device_name(self.agent_gateway_port['id']))
- self._send_gratuitous_arp_packet(fip_ns_name,
- interface_name, floating_ip,
- distributed=True)
+ ip_lib.send_garp_for_proxyarp(fip_ns_name,
+ interface_name,
+ floating_ip,
+ self.conf.send_arp_for_ha,
+ self.root_helper)
# update internal structures
ri.dist_fip_count = ri.dist_fip_count + 1
# License for the specific language governing permissions and limitations
# under the License.
+import eventlet
import os
import netaddr
from neutron.agent.linux import utils
from neutron.common import exceptions
+from neutron.i18n import _LE
+from neutron.openstack.common import log as logging
+LOG = logging.getLogger(__name__)
OPTS = [
cfg.BoolOpt('ip_lib_force_root',
stdout, stderr = utils.execute(command, root_helper=root_helper,
check_exit_code=False, return_stderr=True)
return any(arg in line for line in stderr.split('\n'))
+
+
+def _arping(ns_name, iface_name, address, count, root_helper):
+ arping_cmd = ['arping', '-A', '-I', iface_name, '-c', count, address]
+ try:
+ ip_wrapper = IPWrapper(root_helper, namespace=ns_name)
+ ip_wrapper.netns.execute(arping_cmd, check_exit_code=True)
+ except Exception:
+ msg = _LE("Failed sending gratuitous ARP "
+ "to %(addr)s on %(iface)s in namespace %(ns)s")
+ LOG.exception(msg, {'addr': address,
+ 'iface': iface_name,
+ 'ns': ns_name})
+
+
+def send_gratuitous_arp(ns_name, iface_name, address, count, root_helper):
+ """Send a gratuitous arp using given namespace, interface, and address"""
+
+ def arping():
+ _arping(ns_name, iface_name, address, count, root_helper)
+
+ if count > 0:
+ eventlet.spawn_n(arping)
+
+
+def send_garp_for_proxyarp(ns_name, iface_name, address, count, root_helper):
+ """
+ Send a gratuitous arp using given namespace, interface, and address
+
+ This version should be used when proxy arp is in use since the interface
+ won't actually have the address configured. We actually need to configure
+ the address on the interface and then remove it when the proxy arp has been
+ sent.
+ """
+ def arping_with_temporary_address():
+ # Configure the address on the interface
+ device = IPDevice(iface_name, root_helper, namespace=ns_name)
+ net = netaddr.IPNetwork(str(address))
+ device.addr.add(net.version, str(net), str(net.broadcast))
+
+ _arping(ns_name, iface_name, address, count, root_helper)
+
+ # Delete the address from the interface
+ device.addr.delete(net.version, str(net))
+
+ if count > 0:
+ eventlet.spawn_n(arping_with_temporary_address)
'%s/external/pids' % temp_dir)
conf.set_override('host', host)
agent = l3_test_agent.TestL3NATAgent(host, conf)
- mock.patch.object(agent, '_arping').start()
+ mock.patch.object(ip_lib, 'send_gratuitous_arp').start()
return agent
self.external_process = self.external_process_p.start()
self.send_arp_p = mock.patch(
- 'neutron.agent.l3.agent.L3NATAgent._send_gratuitous_arp_packet')
+ 'neutron.agent.linux.ip_lib.send_gratuitous_arp')
self.send_arp = self.send_arp_p.start()
+ self.send_arp_proxyarp_p = mock.patch(
+ 'neutron.agent.linux.ip_lib.send_garp_for_proxyarp')
+ self.send_arp_proxyarp = self.send_arp_proxyarp_p.start()
+
self.dvr_cls_p = mock.patch('neutron.agent.linux.interface.NullDriver')
driver_cls = self.dvr_cls_p.start()
self.mock_driver = mock.MagicMock()
self.assertEqual(self.mock_driver.plug.call_count, 1)
self.assertEqual(self.mock_driver.init_l3.call_count, 1)
self.send_arp.assert_called_once_with(ri.ns_name, interface_name,
- '99.0.1.9')
+ '99.0.1.9',
+ mock.ANY, mock.ANY)
elif action == 'remove':
self.device_exists.return_value = True
agent.internal_network_removed(ri, port)
self.assertEqual(self.mock_driver.init_l3.call_count, 1)
self.send_arp.assert_called_once_with(ri.ns_name,
interface_name,
- '20.0.0.30')
+ '20.0.0.30',
+ mock.ANY, mock.ANY)
kwargs = {'preserve_ips': ['192.168.1.34/32'],
'namespace': 'qrouter-' + router['id'],
'gateway': '20.0.0.1',
self.assertEqual(self.mock_driver.plug.call_count, 0)
self.assertEqual(self.mock_driver.init_l3.call_count, 1)
self.send_arp.assert_called_once_with(ri.ns_name, interface_name,
- '20.0.0.30')
+ '20.0.0.30', mock.ANY, mock.ANY)
kwargs = {'preserve_ips': ['192.168.1.34/32'],
'namespace': 'qrouter-' + router['id'],
'gateway': '20.0.0.1',
router['gw_port_host'] = HOSTNAME
self._test_external_gateway_action('add', router)
- def _test_arping(self, namespace):
- if not namespace:
- self.conf.set_override('use_namespaces', False)
-
- router_id = _uuid()
- ri = l3router.RouterInfo(router_id, self.conf.root_helper, {})
- agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
- floating_ip = '20.0.0.101'
- interface_name = agent.get_external_device_name(router_id)
- agent._arping(ri, interface_name, floating_ip)
-
- arping_cmd = ['arping', '-A',
- '-I', interface_name,
- '-c', self.conf.send_arp_for_ha,
- floating_ip]
- self.mock_ip.netns.execute.assert_any_call(
- arping_cmd, check_exit_code=True)
-
- def test_arping_namespace(self):
- self._test_arping(namespace=True)
-
- def test_arping_no_namespace(self):
- self._test_arping(namespace=False)
-
def test_agent_remove_external_gateway(self):
router = prepare_router_data(num_internal_ports=2)
self._test_external_gateway_action('remove', router)
self.assertEqual(self.mock_driver.init_l3.call_count, 1)
if self.conf.use_namespaces:
self.send_arp.assert_called_once_with(fip_ns_name, interface_name,
- '20.0.0.30')
+ '20.0.0.30',
+ mock.ANY, mock.ANY)
else:
self.utils_exec.assert_any_call(
check_exit_code=True, root_helper=self.conf.root_helper)
self.neigh_cmd.delete(4, '192.168.45.100', 'cc:dd:ee:ff:ab:cd')
self._assert_sudo([4], ('del', '192.168.45.100', 'lladdr',
'cc:dd:ee:ff:ab:cd', 'dev', 'tap0'))
+
+
+class TestArpPing(TestIPCmdBase):
+ def _test_arping(self, function, address, spawn_n, mIPWrapper):
+ spawn_n.side_effect = lambda f: f()
+ function(mock.sentinel.ns_name,
+ mock.sentinel.iface_name,
+ address,
+ mock.sentinel.count,
+ mock.sentinel.root_helper)
+
+ self.assertTrue(spawn_n.called)
+ mIPWrapper.assert_called_once_with(mock.sentinel.root_helper,
+ namespace=mock.sentinel.ns_name)
+
+ ip_wrapper = mIPWrapper(mock.sentinel.root_helper,
+ mock.sentinel.ns_name)
+
+ # Just test that arping is called with the right arguments
+ arping_cmd = ['arping', '-A',
+ '-I', mock.sentinel.iface_name,
+ '-c', mock.sentinel.count,
+ address]
+ ip_wrapper.netns.execute.assert_any_call(arping_cmd,
+ check_exit_code=True)
+
+ @mock.patch.object(ip_lib, 'IPWrapper')
+ @mock.patch('eventlet.spawn_n')
+ def test_send_gratuitous_arp(self, spawn_n, mIPWrapper):
+ self._test_arping(
+ ip_lib.send_gratuitous_arp, '20.0.0.1', spawn_n, mIPWrapper)
+
+ @mock.patch.object(ip_lib, 'IPDevice')
+ @mock.patch.object(ip_lib, 'IPWrapper')
+ @mock.patch('eventlet.spawn_n')
+ def test_send_garp_for_proxy_arp(self, spawn_n, mIPWrapper, mIPDevice):
+ addr = '20.0.0.1'
+ ip_wrapper = mIPWrapper(mock.sentinel.root_helper,
+ mock.sentinel.ns_name)
+ mIPWrapper.reset_mock()
+ device = mIPDevice(mock.sentinel.iface_name,
+ mock.sentinel.root_helper,
+ namespace=mock.sentinel.ns_name)
+ mIPDevice.reset_mock()
+
+ # Check that the address was added to the interface before arping
+ def check_added_address(*args, **kwargs):
+ mIPDevice.assert_called_once_with(mock.sentinel.iface_name,
+ mock.sentinel.root_helper,
+ namespace=mock.sentinel.ns_name)
+ device.addr.add.assert_called_once_with(4, addr + '/32', addr)
+ self.assertFalse(device.addr.delete.called)
+ device.addr.reset_mock()
+
+ ip_wrapper.netns.execute.side_effect = check_added_address
+
+ self._test_arping(
+ ip_lib.send_garp_for_proxyarp, addr, spawn_n, mIPWrapper)
+
+ # Test that the address was removed after arping
+ device = mIPDevice(mock.sentinel.iface_name,
+ mock.sentinel.root_helper,
+ namespace=mock.sentinel.ns_name)
+ device.addr.delete.assert_called_once_with(4, addr + '/32')
+
+ # If this was called then check_added_address probably had a assert
+ self.assertFalse(device.addr.add.called)