#
# l2_population = False
+# Enable local ARP responder. Requires OVS 2.1. This is only used by the l2
+# population ML2 MechanismDriver.
+#
+# arp_responder = False
+
[securitygroup]
# Firewall driver for realizing neutron security group function.
# firewall_driver = neutron.agent.firewall.NoopFirewallDriver
from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils
from neutron.common import exceptions
+from neutron.common import utils as common_utils
from neutron.openstack.common import excutils
from neutron.openstack.common import jsonutils
from neutron.openstack.common import log as logging
flow_expr_arr.append(actions)
return ','.join(flow_expr_arr)
+
+
+def ofctl_arg_supported(root_helper, cmd, args):
+ '''Verify if ovs-ofctl binary supports command with specific args.
+
+ :param root_helper: utility to use when running shell cmds.
+ :param cmd: ovs-vsctl command to use for test.
+ :param args: arguments to test with command.
+ :returns: a boolean if the args supported.
+ '''
+ supported = True
+ br_name = 'br-test-%s' % common_utils.get_random_string(6)
+ test_br = OVSBridge(br_name, root_helper)
+ test_br.reset_bridge()
+
+ full_args = ["ovs-ofctl", cmd, test_br.br_name] + args
+ try:
+ utils.execute(full_args, root_helper=root_helper)
+ except Exception:
+ supported = False
+
+ test_br.destroy()
+ return supported
"""Utilities and helper functions."""
+import datetime
+import hashlib
import logging as std_logging
import os
+import random
import signal
import socket
def is_valid_vlan_tag(vlan):
return q_const.MIN_VLAN_TAG <= vlan <= q_const.MAX_VLAN_TAG
+
+
+def get_random_string(length):
+ """Get a random hex string of the specified length.
+
+ based on Cinder library
+ cinder/transfer/api.py
+ """
+ rndstr = ""
+ random.seed(datetime.datetime.now().microsecond)
+ while len(rndstr) < length:
+ rndstr += hashlib.sha224(str(random.random())).hexdigest()
+
+ return rndstr[0:length]
--- /dev/null
+Neutron ML2 l2 population Mechanism Drivers
+
+l2 population (l2pop) mechanism drivers implements the ML2 driver to improve
+open source plugins overlay implementations (VXLAN with Linux bridge and
+GRE/VXLAN with OVS). This mechanism driver is implemented in ML2 to propagate
+the forwarding information among agents using a common RPC API.
+
+More informations could be found on the wiki page [1].
+
+VXLAN Linux kernel:
+-------------------
+The VXLAN Linux kernel module provide all necessary functionalities to populate
+the forwarding table and local ARP responder tables. This module appears on
+release 3.7 of the vanilla Linux kernel in experimental:
+- 3.8: first stable release, no edge replication (multicast necessary),
+- 3.9: edge replication only for the broadcasted packets,
+- 3.11: edge replication for broadcast, multicast and unknown packets.
+
+Note: Some distributions (like RHEL) have backported this module on precedent
+ kernel version.
+
+OpenvSwitch:
+------------
+The OVS OpenFlow tables provide all of the necessary functionality to populate
+the forwarding table and local ARP responder tables.
+A wiki page describe how the flow tables did evolve on OVS agents:
+- [2] without local ARP responder
+- [3] with local ARP responder. /!\ This functionality is only available since
+ the development branch 2.1. It's possible
+ to disable (enable by default) it through
+ the flag 'arp_responder'. /!\
+
+
+Note: A difference persists between the LB and OVS agents when they are used
+ with the l2-pop mechanism driver (and local ARP responder available). The
+ LB agent will drop unknown unicast (VXLAN bridge mode), whereas the OVS
+ agent will flood it.
+
+[1] https://wiki.openstack.org/wiki/L2population_blueprint
+[2] https://wiki.openstack.org/wiki/Ovs-flow-logic#OVS_flows_logic
+[3] https://wiki.openstack.org/wiki/Ovs-flow-logic#OVS_flows_logic_with_local_ARP_responder
\ No newline at end of file
veth_mtu=None, l2_population=False,
minimize_polling=False,
ovsdb_monitor_respawn_interval=(
- constants.DEFAULT_OVSDBMON_RESPAWN)):
+ constants.DEFAULT_OVSDBMON_RESPAWN),
+ arp_responder=False):
'''Constructor.
:param integ_br: name of the integration bridge.
:param ovsdb_monitor_respawn_interval: Optional, when using polling
minimization, the number of seconds to wait before respawning
the ovsdb monitor.
+ :param arp_responder: Optional, enable local ARP responder if it is
+ supported.
'''
self.veth_mtu = veth_mtu
self.root_helper = root_helper
q_const.MAX_VLAN_TAG))
self.tunnel_types = tunnel_types or []
self.l2_pop = l2_population
+ # TODO(ethuleau): Initially, local ARP responder is be dependent to the
+ # ML2 l2 population mechanism driver.
+ self.arp_responder_enabled = (arp_responder and
+ self._check_arp_responder_support() and
+ self.l2_pop)
self.agent_state = {
'binary': 'neutron-openvswitch-agent',
'host': cfg.CONF.host,
'configurations': {'bridge_mappings': bridge_mappings,
'tunnel_types': self.tunnel_types,
'tunneling_ip': local_ip,
- 'l2_population': self.l2_pop},
+ 'l2_population': self.l2_pop,
+ 'arp_responder_enabled':
+ self.arp_responder_enabled},
'agent_type': q_const.AGENT_TYPE_OVS,
'start_flag': True}
LOG.exception(_("Agent terminated"))
raise SystemExit(1)
+ def _check_arp_responder_support(self):
+ '''Check if OVS supports to modify ARP headers.
+
+ This functionality is only available since the development branch 2.1.
+ '''
+ args = ['arp,action=load:0x2->NXM_OF_ARP_OP[],'
+ 'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],'
+ 'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[]']
+ supported = ovs_lib.ofctl_arg_supported(self.root_helper, 'add-flow',
+ args)
+ if not supported:
+ LOG.warning(_('OVS version can not support ARP responder.'))
+ return supported
+
def _report_state(self):
# How many devices are likely used by a VM
self.agent_state.get('configurations')['devices'] = (
actions="strip_vlan,set_tunnel:%s,"
"output:%s" % (lvm.segmentation_id, ofports))
else:
- # TODO(feleouet): add ARP responder entry
+ self._set_arp_responder('add', lvm.vlan, port_info[0],
+ port_info[1])
self.tun_br.add_flow(table=constants.UCAST_TO_TUN,
priority=2,
dl_vlan=lvm.vlan,
# Check if this tunnel port is still used
self.cleanup_tunnel_port(ofport, lvm.network_type)
else:
- #TODO(feleouet): remove ARP responder entry
+ self._set_arp_responder('remove', lvm.vlan, port_info[0],
+ port_info[1])
self.tun_br.delete_flows(table=constants.UCAST_TO_TUN,
dl_vlan=lvm.vlan,
dl_dst=port_info[0])
+ def _fdb_chg_ip(self, context, fdb_entries):
+ '''fdb update when an IP of a port is updated.
+
+ The ML2 l2-pop mechanism driver send an fdb update rpc message when an
+ IP of a port is updated.
+
+ :param context: RPC context.
+ :param fdb_entries: fdb dicts that contain all mac/IP informations per
+ agent and network.
+ {'net1':
+ {'agent_ip':
+ {'before': [[mac, ip]],
+ 'after': [[mac, ip]]
+ }
+ }
+ 'net2':
+ ...
+ }
+ '''
+ LOG.debug(_("update chg_ip received"))
+
+ # TODO(ethuleau): Use OVS defer apply flows for all rules will be an
+ # interesting improvement here. But actually, OVS lib defer apply flows
+ # methods doesn't ensure the add flows will be applied before delete.
+ for network_id, agent_ports in fdb_entries.items():
+ lvm = self.local_vlan_map.get(network_id)
+ if not lvm:
+ continue
+
+ for agent_ip, state in agent_ports.items():
+ if agent_ip == self.local_ip:
+ continue
+
+ after = state.get('after')
+ for mac, ip in after:
+ self._set_arp_responder('add', lvm.vlan, mac, ip)
+
+ before = state.get('before')
+ for mac, ip in before:
+ self._set_arp_responder('remove', lvm.vlan, mac, ip)
+
def fdb_update(self, context, fdb_entries):
LOG.debug(_("fdb_update received"))
for action, values in fdb_entries.items():
getattr(self, method)(context, values)
+ def _set_arp_responder(self, action, lvid, mac_str, ip_str):
+ '''Set the ARP respond entry.
+
+ When the l2 population mechanism driver and OVS supports to edit ARP
+ fields, a table (ARP_RESPONDER) to resolve ARP locally is added to the
+ tunnel bridge.
+
+ :param action: add or remove ARP entry.
+ :param lvid: local VLAN map of network's ARP entry.
+ :param mac_str: MAC string value.
+ :param ip_str: IP string value.
+ '''
+ if not self.arp_responder_enabled:
+ return
+
+ mac = netaddr.EUI(mac_str, dialect=netaddr.mac_unix)
+ ip = netaddr.IPAddress(ip_str)
+
+ if action == 'add':
+ actions = ('move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],'
+ 'mod_dl_src:%(mac)s,'
+ 'load:0x2->NXM_OF_ARP_OP[],'
+ 'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],'
+ 'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],'
+ 'load:%(mac)#x->NXM_NX_ARP_SHA[],'
+ 'load:%(ip)#x->NXM_OF_ARP_SPA[],'
+ 'in_port' % {'mac': mac, 'ip': ip})
+ self.tun_br.add_flow(table=constants.ARP_RESPONDER,
+ priority=1,
+ proto='arp',
+ dl_vlan=lvid,
+ nw_dst='%s' % ip,
+ actions=actions)
+ elif action == 'remove':
+ self.tun_br.delete_flows(table=constants.ARP_RESPONDER,
+ proto='arp',
+ dl_vlan=lvid,
+ nw_dst='%s' % ip)
+ else:
+ LOG.warning(_('Action %s not supported'), action)
+
def create_rpc_dispatcher(self):
'''Get the rpc dispatcher for this manager.
actions="resubmit(,%s)" %
constants.PATCH_LV_TO_TUN)
self.tun_br.add_flow(priority=0, actions="drop")
+ if self.arp_responder_enabled:
+ # ARP broadcast-ed request go to the local ARP_RESPONDER table to
+ # be locally resolved
+ self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
+ priority=1,
+ proto='arp',
+ dl_dst="ff:ff:ff:ff:ff:ff",
+ actions=("resubmit(,%s)" %
+ constants.ARP_RESPONDER))
# PATCH_LV_TO_TUN table will handle packets coming from patch_int
# unicasts go to table UCAST_TO_TUN where remote adresses are learnt
self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
+ priority=0,
dl_dst="00:00:00:00:00:00/01:00:00:00:00:00",
actions="resubmit(,%s)" % constants.UCAST_TO_TUN)
# Broadcasts/multicasts go to table FLOOD_TO_TUN that handles flooding
self.tun_br.add_flow(table=constants.PATCH_LV_TO_TUN,
+ priority=0,
dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
actions="resubmit(,%s)" % constants.FLOOD_TO_TUN)
# Tables [tunnel_type]_TUN_TO_LV will set lvid depending on tun_id
priority=0,
actions="resubmit(,%s)" %
constants.FLOOD_TO_TUN)
+ if self.arp_responder_enabled:
+ # If none of the ARP entries correspond to the requested IP, the
+ # broadcast-ed packet is resubmitted to the flooding table
+ self.tun_br.add_flow(table=constants.ARP_RESPONDER,
+ priority=0,
+ actions="resubmit(,%s)" %
+ constants.FLOOD_TO_TUN)
# FLOOD_TO_TUN will handle flooding in tunnels based on lvid,
# for now, add a default drop action
self.tun_br.add_flow(table=constants.FLOOD_TO_TUN,
tunnel_types=config.AGENT.tunnel_types,
veth_mtu=config.AGENT.veth_mtu,
l2_population=config.AGENT.l2_population,
+ arp_responder=config.AGENT.arp_responder,
)
# If enable_tunneling is TRUE, set tunnel_type to default to GRE
cfg.BoolOpt('l2_population', default=False,
help=_("Use ml2 l2population mechanism driver to learn "
"remote mac and IPs and improve tunnel scalability")),
+ cfg.BoolOpt('arp_responder', default=False,
+ help=_("Enable local ARP responder if it is supported")),
]
VXLAN_TUN_TO_LV = 3
LEARN_FROM_TUN = 10
UCAST_TO_TUN = 20
-FLOOD_TO_TUN = 21
+ARP_RESPONDER = 21
+FLOOD_TO_TUN = 22
+
# Map tunnel types to tables number
TUN_TABLE = {p_const.TYPE_GRE: GRE_TUN_TO_LV,
p_const.TYPE_VXLAN: VXLAN_TUN_TO_LV}
min_kernel_ver = constants.MINIMUM_LINUX_KERNEL_OVS_VXLAN
self._check_ovs_vxlan_version(min_vxlan_ver, min_vxlan_ver,
min_kernel_ver, expecting_ok=True)
+
+ def test_ofctl_arg_supported(self):
+ with mock.patch('neutron.common.utils.get_random_string') as utils:
+ utils.return_value = 'test'
+ supported = ovs_lib.ofctl_arg_supported(self.root_helper, 'cmd',
+ ['args'])
+ self.execute.assert_has_calls([
+ mock.call(['ovs-vsctl', self.TO, '--', '--if-exists', 'del-br',
+ 'br-test-test'], root_helper=self.root_helper),
+ mock.call(['ovs-vsctl', self.TO, '--', '--may-exist', 'add-br',
+ 'br-test-test'], root_helper=self.root_helper),
+ mock.call(['ovs-ofctl', 'cmd', 'br-test-test', 'args'],
+ root_helper=self.root_helper),
+ mock.call(['ovs-vsctl', self.TO, '--', '--if-exists', 'del-br',
+ 'br-test-test'], root_helper=self.root_helper)
+ ])
+ self.assertTrue(supported)
+
+ self.execute.side_effect = Exception
+ supported = ovs_lib.ofctl_arg_supported(self.root_helper, 'cmd',
+ ['args'])
+ self.execute.assert_has_calls([
+ mock.call(['ovs-vsctl', self.TO, '--', '--if-exists', 'del-br',
+ 'br-test-test'], root_helper=self.root_helper),
+ mock.call(['ovs-vsctl', self.TO, '--', '--may-exist', 'add-br',
+ 'br-test-test'], root_helper=self.root_helper),
+ mock.call(['ovs-ofctl', 'cmd', 'br-test-test', 'args'],
+ root_helper=self.root_helper),
+ mock.call(['ovs-vsctl', self.TO, '--', '--if-exists', 'del-br',
+ 'br-test-test'], root_helper=self.root_helper)
+ ])
+ self.assertFalse(supported)
self.assertEqual(0, len(cfg.CONF.OVS.bridge_mappings))
self.assertEqual(0, len(cfg.CONF.OVS.network_vlan_ranges))
self.assertEqual(0, len(cfg.CONF.OVS.tunnel_id_ranges))
+ self.assertFalse(cfg.CONF.AGENT.l2_population)
+ self.assertFalse(cfg.CONF.AGENT.arp_responder)
import sys
import mock
+import netaddr
from oslo.config import cfg
import testtools
'ovs_neutron_plugin.AgentNotifierApi')
OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0"
+FAKE_MAC = '00:11:22:33:44:55'
+FAKE_IP1 = '10.0.0.1'
+FAKE_IP2 = '10.0.0.2'
+
class CreateAgentConfigMap(base.BaseTestCase):
return_value='00:00:00:00:00:01'),
mock.patch('neutron.openstack.common.loopingcall.'
'FixedIntervalLoopingCall',
- new=MockFixedIntervalLoopingCall)):
+ new=MockFixedIntervalLoopingCall),
+ mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
+ 'OVSNeutronAgent._check_arp_responder_support',
+ return_value=True)):
self.agent = ovs_neutron_agent.OVSNeutronAgent(**kwargs)
self.agent.tun_br = mock.Mock()
self.agent.sg_agent = mock.Mock()
self.agent.local_vlan_map = {'net1': lvm1, 'net2': lvm2}
self.agent.tun_br_ofports = {'gre':
{'1.1.1.1': '1', '2.2.2.2': '2'}}
+ self.agent.arp_responder_enabled = True
def test_fdb_ignore_network(self):
self._prepare_l2_pop_ofports()
'segment_id': 'tun2',
'ports':
{'agent_ip':
- [['mac', 'ip'],
+ [[FAKE_MAC, FAKE_IP1],
n_const.FLOODING_ENTRY]}}}
with mock.patch.object(self.agent.tun_br,
"defer_apply_on") as defer_fn:
'segment_id': 'tun1',
'ports':
{'2.2.2.2':
- [['mac', 'ip'],
+ [[FAKE_MAC, FAKE_IP1],
n_const.FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_flow'),
) as (add_flow_fn, mod_flow_fn, add_tun_fn):
self.agent.fdb_add(None, fdb_entry)
self.assertFalse(add_tun_fn.called)
- add_flow_fn.assert_called_with(table=constants.UCAST_TO_TUN,
- priority=2,
- dl_vlan='vlan1',
- dl_dst='mac',
- actions='strip_vlan,'
- 'set_tunnel:seg1,output:2')
+ actions = ('move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],'
+ 'mod_dl_src:%(mac)s,'
+ 'load:0x2->NXM_OF_ARP_OP[],'
+ 'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],'
+ 'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],'
+ 'load:%(mac)#x->NXM_NX_ARP_SHA[],'
+ 'load:%(ip)#x->NXM_OF_ARP_SPA[],'
+ 'in_port' %
+ {'mac': netaddr.EUI(FAKE_MAC, dialect=netaddr.mac_unix),
+ 'ip': netaddr.IPAddress(FAKE_IP1)})
+ add_flow_fn.assert_has_calls([
+ mock.call(table=constants.ARP_RESPONDER,
+ priority=1,
+ proto='arp',
+ dl_vlan='vlan1',
+ nw_dst=FAKE_IP1,
+ actions=actions),
+ mock.call(table=constants.UCAST_TO_TUN,
+ priority=2,
+ dl_vlan='vlan1',
+ dl_dst=FAKE_MAC,
+ actions='strip_vlan,'
+ 'set_tunnel:seg1,output:2')
+ ])
mod_flow_fn.assert_called_with(table=constants.FLOOD_TO_TUN,
dl_vlan='vlan1',
actions='strip_vlan,'
'segment_id': 'tun2',
'ports':
{'2.2.2.2':
- [['mac', 'ip'],
+ [[FAKE_MAC, FAKE_IP1],
n_const.FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'mod_flow'),
mock.patch.object(self.agent.tun_br, 'delete_flows'),
) as (mod_flow_fn, del_flow_fn):
self.agent.fdb_remove(None, fdb_entry)
- del_flow_fn.assert_called_with(table=constants.UCAST_TO_TUN,
- dl_vlan='vlan2',
- dl_dst='mac')
+ del_flow_fn.assert_has_calls([
+ mock.call(table=constants.ARP_RESPONDER,
+ proto='arp',
+ dl_vlan='vlan2',
+ nw_dst=FAKE_IP1),
+ mock.call(table=constants.UCAST_TO_TUN,
+ dl_vlan='vlan2',
+ dl_dst=FAKE_MAC)
+ ])
mod_flow_fn.assert_called_with(table=constants.FLOOD_TO_TUN,
dl_vlan='vlan2',
actions='strip_vlan,'
fdb_entry = {'net1':
{'network_type': 'gre',
'segment_id': 'tun1',
- 'ports': {'1.1.1.1': [['mac', 'ip']]}}}
+ 'ports': {'1.1.1.1': [[FAKE_MAC, FAKE_IP1]]}}}
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_flow'),
mock.patch.object(self.agent.tun_br, 'mod_flow'),
) as (add_flow_fn, mod_flow_fn, add_tun_fn):
self.agent.fdb_add(None, fdb_entry)
self.assertFalse(add_tun_fn.called)
- fdb_entry['net1']['ports']['10.10.10.10'] = [['mac', 'ip']]
+ fdb_entry['net1']['ports']['10.10.10.10'] = [[FAKE_MAC, FAKE_IP1]]
self.agent.fdb_add(None, fdb_entry)
add_tun_fn.assert_called_with('gre-0a0a0a0a', '10.10.10.10', 'gre')
self.agent.fdb_remove(None, fdb_entry)
del_port_fn.assert_called_once_with('gre-02020202')
+ def test_fdb_update_chg_ip(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entries = {'chg_ip':
+ {'net1':
+ {'agent_ip':
+ {'before': [[FAKE_MAC, FAKE_IP1]],
+ 'after': [[FAKE_MAC, FAKE_IP2]]}}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'add_flow'),
+ mock.patch.object(self.agent.tun_br, 'delete_flows')
+ ) as (add_flow_fn, del_flow_fn):
+ self.agent.fdb_update(None, fdb_entries)
+ actions = ('move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],'
+ 'mod_dl_src:%(mac)s,'
+ 'load:0x2->NXM_OF_ARP_OP[],'
+ 'move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],'
+ 'move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],'
+ 'load:%(mac)#x->NXM_NX_ARP_SHA[],'
+ 'load:%(ip)#x->NXM_OF_ARP_SPA[],'
+ 'in_port' %
+ {'mac': netaddr.EUI(FAKE_MAC, dialect=netaddr.mac_unix),
+ 'ip': netaddr.IPAddress(FAKE_IP2)})
+ add_flow_fn.assert_called_once_with(table=constants.ARP_RESPONDER,
+ priority=1,
+ proto='arp',
+ dl_vlan='vlan1',
+ nw_dst=FAKE_IP2,
+ actions=actions)
+ del_flow_fn.assert_called_once_with(table=constants.ARP_RESPONDER,
+ proto='arp',
+ dl_vlan='vlan1',
+ nw_dst=FAKE_IP1)
+
def test_recl_lv_port_to_preserve(self):
self._prepare_l2_pop_ofports()
self.agent.l2_pop = True
return_value=bridges),
mock.patch(
'neutron.agent.linux.ovs_lib.get_bridge_external_bridge_id',
- side_effect=pullup_side_effect)):
+ side_effect=pullup_side_effect),
+ mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
+ 'OVSNeutronAgent._check_arp_responder_support',
+ return_value=True)):
self.agent = ovs_neutron_agent.OVSNeutronAgent(**self.kwargs)
self.assertEqual(len(ancillary), len(self.agent.ancillary_brs))
if ancillary:
'neutron.openstack.common.rpc.impl_fake')
cfg.CONF.set_override('report_interval', 0, 'AGENT')
+ check_arp_responder_str = ('neutron.plugins.openvswitch.agent.'
+ 'ovs_neutron_agent.OVSNeutronAgent.'
+ '_check_arp_responder_support')
+ self.mock_check_arp_resp = mock.patch(check_arp_responder_str).start()
+ self.mock_check_arp_resp.return_value = True
+
self.INT_BRIDGE = 'integration_bridge'
self.TUN_BRIDGE = 'tunnel_bridge'
self.MAP_TUN_BRIDGE = 'tunnel_bridge_mapping'
in_port=self.INT_OFPORT,
actions="resubmit(,%s)" %
constants.PATCH_LV_TO_TUN),
- mock.call.add_flow(priority=0, actions='drop'),
- mock.call.add_flow(table=constants.PATCH_LV_TO_TUN,
+ mock.call.add_flow(priority=0, actions="drop"),
+ mock.call.add_flow(priority=0, table=constants.PATCH_LV_TO_TUN,
dl_dst=UCAST_MAC,
actions="resubmit(,%s)" %
constants.UCAST_TO_TUN),
- mock.call.add_flow(table=constants.PATCH_LV_TO_TUN,
+ mock.call.add_flow(priority=0, table=constants.PATCH_LV_TO_TUN,
dl_dst=BCAST_MAC,
actions="resubmit(,%s)" %
constants.FLOOD_TO_TUN),
self.VETH_MTU)
self._verify_mock_calls()
+ # TODO(ethuleau): Initially, local ARP responder is be dependent to the
+ # ML2 l2 population mechanism driver.
+ # The next two tests use l2_pop flag to test ARP responder
+ def test_construct_with_arp_responder(self):
+ ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', self.NET_MAPPING,
+ 'sudo', 2, ['gre'],
+ self.VETH_MTU, l2_population=True,
+ arp_responder=True)
+ self.mock_tun_bridge_expected.insert(
+ 5, mock.call.add_flow(table=constants.PATCH_LV_TO_TUN,
+ priority=1,
+ proto="arp",
+ dl_dst="ff:ff:ff:ff:ff:ff",
+ actions="resubmit(,%s)" %
+ constants.ARP_RESPONDER)
+ )
+ self.mock_tun_bridge_expected.insert(
+ 12, mock.call.add_flow(table=constants.ARP_RESPONDER,
+ priority=0,
+ actions="resubmit(,%s)" %
+ constants.FLOOD_TO_TUN)
+ )
+ self._verify_mock_calls()
+
+ def test_construct_without_arp_responder(self):
+ ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', self.NET_MAPPING,
+ 'sudo', 2, ['gre'],
+ self.VETH_MTU, l2_population=False,
+ arp_responder=True)
+ self._verify_mock_calls()
+
def test_construct_vxlan(self):
with mock.patch.object(ovs_lib, 'get_installed_ovs_klm_version',
return_value="1.10") as klm_ver: