Merge "Implement "FdbInterface" with Pyroute2"
This commit is contained in:
commit
484f7fe78e
@ -20,6 +20,7 @@ from neutron_lib import constants
|
||||
from neutron_lib.utils import helpers
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from pyroute2.netlink import exceptions as netlink_exceptions
|
||||
|
||||
from neutron.agent.linux import bridge_lib
|
||||
from neutron.conf.agent import l2_ext_fdb_population
|
||||
@ -70,13 +71,14 @@ class FdbPopulationAgentExtension(
|
||||
# update macs already in the physical interface's FDB table
|
||||
for device in devices:
|
||||
try:
|
||||
_stdout = bridge_lib.FdbInterface.show(device)
|
||||
except RuntimeError as e:
|
||||
rules = bridge_lib.FdbInterface.show(dev=device)
|
||||
except (OSError, netlink_exceptions.NetlinkError) as e:
|
||||
LOG.warning(
|
||||
'Unable to find FDB Interface %(device)s. '
|
||||
'Exception: %(e)s', {'device': device, 'e': e})
|
||||
continue
|
||||
self.device_to_macs[device] = _stdout.split()[::3]
|
||||
self.device_to_macs[device] = [rule['mac'] for rule in
|
||||
rules[device]]
|
||||
|
||||
def update_port(self, device, port_id, mac):
|
||||
# check if device is updated
|
||||
@ -90,14 +92,9 @@ class FdbPopulationAgentExtension(
|
||||
# check if rule for mac already exists
|
||||
if mac in self.device_to_macs[device]:
|
||||
return
|
||||
try:
|
||||
bridge_lib.FdbInterface.add(mac, device)
|
||||
except RuntimeError as e:
|
||||
LOG.warning(
|
||||
'Unable to add mac %(mac)s '
|
||||
'to FDB Interface %(device)s. '
|
||||
'Exception: %(e)s',
|
||||
{'mac': mac, 'device': device, 'e': e})
|
||||
if not bridge_lib.FdbInterface.add(mac, device):
|
||||
LOG.warning('Unable to add mac %(mac)s to FDB Interface '
|
||||
'%(device)s.', {'mac': mac, 'device': device})
|
||||
return
|
||||
self.device_to_macs[device].append(mac)
|
||||
|
||||
@ -110,14 +107,10 @@ class FdbPopulationAgentExtension(
|
||||
return
|
||||
for device in devices:
|
||||
if mac in self.device_to_macs[device]:
|
||||
try:
|
||||
bridge_lib.FdbInterface.delete(mac, device)
|
||||
except RuntimeError as e:
|
||||
LOG.warning(
|
||||
'Unable to delete mac %(mac)s '
|
||||
'from FDB Interface %(device)s. '
|
||||
'Exception: %(e)s',
|
||||
{'mac': mac, 'device': device, 'e': e})
|
||||
if not bridge_lib.FdbInterface.delete(mac, device):
|
||||
LOG.warning('Unable to delete mac %(mac)s from FDB '
|
||||
'Interface %(device)s.',
|
||||
{'mac': mac, 'device': device})
|
||||
return
|
||||
self.device_to_macs[device].remove(mac)
|
||||
del self.portid_to_mac[port_id]
|
||||
|
@ -16,6 +16,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import functools
|
||||
import os
|
||||
|
||||
@ -128,39 +129,66 @@ class BridgeDevice(ip_lib.IPDevice):
|
||||
|
||||
|
||||
class FdbInterface(object):
|
||||
"""provide basic functionality to edit the FDB table"""
|
||||
"""Provide basic functionality to edit the FDB table"""
|
||||
|
||||
@staticmethod
|
||||
def _execute_bridge(cmd, namespace, **kwargs):
|
||||
ip_wrapper = ip_lib.IPWrapper(namespace)
|
||||
return ip_wrapper.netns.execute(cmd, run_as_root=True, **kwargs)
|
||||
@catch_exceptions
|
||||
def add(mac, dev, dst_ip=None, namespace=None, **kwargs):
|
||||
priv_ip_lib.add_bridge_fdb(mac, dev, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def _cmd(cls, op, mac, dev, ip_dst, namespace, **kwargs):
|
||||
cmd = ['bridge', 'fdb', op, mac, 'dev', dev]
|
||||
if ip_dst is not None:
|
||||
cmd += ['dst', ip_dst]
|
||||
cls._execute_bridge(cmd, namespace, **kwargs)
|
||||
@staticmethod
|
||||
@catch_exceptions
|
||||
def append(mac, dev, dst_ip=None, namespace=None, **kwargs):
|
||||
priv_ip_lib.append_bridge_fdb(mac, dev, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def add(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
|
||||
return cls._cmd('add', mac, dev, ip_dst, namespace, **kwargs)
|
||||
@staticmethod
|
||||
@catch_exceptions
|
||||
def replace(mac, dev, dst_ip=None, namespace=None, **kwargs):
|
||||
try:
|
||||
priv_ip_lib.delete_bridge_fdb(mac, dev, namespace=namespace,
|
||||
**kwargs)
|
||||
except (RuntimeError, OSError, netlink_exceptions.NetlinkError):
|
||||
pass
|
||||
priv_ip_lib.add_bridge_fdb(mac, dev, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def append(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
|
||||
return cls._cmd('append', mac, dev, ip_dst, namespace, **kwargs)
|
||||
@staticmethod
|
||||
@catch_exceptions
|
||||
def delete(mac, dev, dst_ip=None, namespace=None, **kwargs):
|
||||
priv_ip_lib.delete_bridge_fdb(mac, dev, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def replace(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
|
||||
return cls._cmd('replace', mac, dev, ip_dst, namespace, **kwargs)
|
||||
@staticmethod
|
||||
def show(dev=None, namespace=None, **kwargs):
|
||||
"""List the FDB entries in a namespace
|
||||
|
||||
@classmethod
|
||||
def delete(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
|
||||
return cls._cmd('delete', mac, dev, ip_dst, namespace, **kwargs)
|
||||
:parameter dev: device name to filter the query
|
||||
:parameter namespace: namespace name
|
||||
:returns: a dictionary with the device names and the list of entries
|
||||
per device.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def show(cls, dev=None, namespace=None, **kwargs):
|
||||
cmd = ['bridge', 'fdb', 'show']
|
||||
if dev:
|
||||
cmd += ['dev', dev]
|
||||
return cls._execute_bridge(cmd, namespace, **kwargs)
|
||||
def find_device_name(ifindex, devices):
|
||||
for device in (device for device in devices if
|
||||
device['index'] == ifindex):
|
||||
return device['name']
|
||||
|
||||
ret = collections.defaultdict(list)
|
||||
fdbs = priv_ip_lib.list_bridge_fdb(namespace=namespace, **kwargs)
|
||||
devices = ip_lib.get_devices_info(namespace)
|
||||
for fdb in fdbs:
|
||||
name = find_device_name(fdb['ifindex'], devices)
|
||||
if dev and dev != name:
|
||||
continue
|
||||
|
||||
master = find_device_name(ip_lib.get_attr(fdb, 'NDA_MASTER'),
|
||||
devices)
|
||||
fdb_info = {'mac': ip_lib.get_attr(fdb, 'NDA_LLADDR'),
|
||||
'master': master,
|
||||
'vlan': ip_lib.get_attr(fdb, 'NDA_VLAN'),
|
||||
'dst_ip': ip_lib.get_attr(fdb, 'NDA_DST')}
|
||||
ret[name].append(fdb_info)
|
||||
|
||||
return ret
|
||||
|
@ -686,16 +686,7 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
||||
def vxlan_ucast_supported(self):
|
||||
if not cfg.CONF.VXLAN.l2_population:
|
||||
return False
|
||||
if not ip_lib.iproute_arg_supported(
|
||||
['bridge', 'fdb'], 'append'):
|
||||
LOG.warning('Option "%(option)s" must be supported by command '
|
||||
'"%(command)s" to enable %(mode)s mode',
|
||||
{'option': 'append',
|
||||
'command': 'bridge fdb',
|
||||
'mode': 'VXLAN UCAST'})
|
||||
return False
|
||||
|
||||
test_iface = None
|
||||
for seg_id in range(1, constants.MAX_VXLAN_VNI + 1):
|
||||
if (ip_lib.device_exists(self.get_vxlan_device_name(seg_id)) or
|
||||
ip_lib.vxlan_in_use(seg_id)):
|
||||
@ -706,15 +697,10 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
||||
LOG.error('No valid Segmentation ID to perform UCAST test.')
|
||||
return False
|
||||
|
||||
try:
|
||||
bridge_lib.FdbInterface.append(constants.FLOODING_ENTRY[0],
|
||||
test_iface, '1.1.1.1',
|
||||
log_fail_as_error=False)
|
||||
return True
|
||||
except RuntimeError:
|
||||
return False
|
||||
finally:
|
||||
self.delete_interface(test_iface)
|
||||
ret = bridge_lib.FdbInterface.append(constants.FLOODING_ENTRY[0],
|
||||
test_iface, '1.1.1.1')
|
||||
self.delete_interface(test_iface)
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def vxlan_mcast_supported():
|
||||
@ -754,11 +740,13 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
||||
|
||||
@staticmethod
|
||||
def fdb_bridge_entry_exists(mac, interface, agent_ip=None):
|
||||
entries = bridge_lib.FdbInterface.show(interface)
|
||||
entries = bridge_lib.FdbInterface.show(dev=interface)
|
||||
macs = [entry['mac'] for entry in entries[interface]]
|
||||
ips = [entry['dst_ip'] for entry in entries[interface]]
|
||||
if not agent_ip:
|
||||
return mac in entries
|
||||
return mac in macs
|
||||
|
||||
return (agent_ip in entries and mac in entries)
|
||||
return agent_ip in ips and mac in macs
|
||||
|
||||
@staticmethod
|
||||
def add_fdb_ip_entry(mac, ip, interface):
|
||||
@ -774,25 +762,23 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
|
||||
for mac, ip in ports:
|
||||
if mac != constants.FLOODING_ENTRY[0]:
|
||||
self.add_fdb_ip_entry(mac, ip, interface)
|
||||
bridge_lib.FdbInterface.replace(mac, interface, agent_ip,
|
||||
check_exit_code=False)
|
||||
bridge_lib.FdbInterface.replace(mac, interface,
|
||||
dst_ip=agent_ip)
|
||||
elif self.vxlan_mode == lconst.VXLAN_UCAST:
|
||||
if self.fdb_bridge_entry_exists(mac, interface):
|
||||
bridge_lib.FdbInterface.append(mac, interface, agent_ip,
|
||||
check_exit_code=False)
|
||||
bridge_lib.FdbInterface.append(mac, interface,
|
||||
dst_ip=agent_ip)
|
||||
else:
|
||||
bridge_lib.FdbInterface.add(mac, interface, agent_ip,
|
||||
check_exit_code=False)
|
||||
bridge_lib.FdbInterface.add(mac, interface,
|
||||
dst_ip=agent_ip)
|
||||
|
||||
def remove_fdb_entries(self, agent_ip, ports, interface):
|
||||
for mac, ip in ports:
|
||||
if mac != constants.FLOODING_ENTRY[0]:
|
||||
self.remove_fdb_ip_entry(mac, ip, interface)
|
||||
bridge_lib.FdbInterface.delete(mac, interface, agent_ip,
|
||||
check_exit_code=False)
|
||||
bridge_lib.FdbInterface.delete(mac, interface, dst_ip=agent_ip)
|
||||
elif self.vxlan_mode == lconst.VXLAN_UCAST:
|
||||
bridge_lib.FdbInterface.delete(mac, interface, agent_ip,
|
||||
check_exit_code=False)
|
||||
bridge_lib.FdbInterface.delete(mac, interface, dst_ip=agent_ip)
|
||||
|
||||
def get_agent_id(self):
|
||||
if self.bridge_mappings:
|
||||
|
@ -761,3 +761,59 @@ def delete_ip_route(namespace, cidr, ip_version, device=None, via=None,
|
||||
if e.errno == errno.ENOENT:
|
||||
raise NetworkNamespaceNotFound(netns_name=namespace)
|
||||
raise
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def list_bridge_fdb(namespace=None, **kwargs):
|
||||
"""List bridge fdb table"""
|
||||
# NOTE(ralonsoh): fbd does not support ifindex filtering in pyroute2 0.5.14
|
||||
try:
|
||||
with get_iproute(namespace) as ip:
|
||||
return make_serializable(ip.fdb('dump', **kwargs))
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
raise NetworkNamespaceNotFound(netns_name=namespace)
|
||||
raise
|
||||
|
||||
|
||||
def _command_bridge_fdb(command, mac, device, dst_ip=None, namespace=None,
|
||||
**kwargs):
|
||||
try:
|
||||
kwargs['lladdr'] = mac
|
||||
kwargs['ifindex'] = get_link_id(device, namespace)
|
||||
if dst_ip:
|
||||
kwargs['dst'] = dst_ip
|
||||
with get_iproute(namespace) as ip:
|
||||
return make_serializable(ip.fdb(command, **kwargs))
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
raise NetworkNamespaceNotFound(netns_name=namespace)
|
||||
raise
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def add_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):
|
||||
"""Add a FDB entry"""
|
||||
return _command_bridge_fdb('add', mac, device, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def append_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):
|
||||
"""Add a FDB entry"""
|
||||
return _command_bridge_fdb('append', mac, device, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def replace_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):
|
||||
"""Add a FDB entry"""
|
||||
return _command_bridge_fdb('replace', mac, device, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
||||
|
||||
@privileged.default.entrypoint
|
||||
def delete_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):
|
||||
"""Add a FDB entry"""
|
||||
return _command_bridge_fdb('del', mac, device, dst_ip=dst_ip,
|
||||
namespace=namespace, **kwargs)
|
||||
|
@ -13,7 +13,6 @@
|
||||
# under the License.
|
||||
|
||||
import random
|
||||
import re
|
||||
|
||||
import netaddr
|
||||
from neutron_lib import constants
|
||||
@ -27,6 +26,9 @@ from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional import base
|
||||
|
||||
|
||||
MAC_ALL_NODES_ADDRESS = '33:33:00:00:00:01'
|
||||
|
||||
|
||||
class BridgeLibTestCase(base.BaseSudoTestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -131,8 +133,6 @@ class FdbInterfaceTestCase(testscenarios.WithScenarios, base.BaseSudoTestCase):
|
||||
|
||||
MAC1 = 'ca:fe:ca:fe:ca:fe'
|
||||
MAC2 = 'ca:fe:ca:fe:ca:01'
|
||||
RULE_PATTERN = (r"^(?P<mac>([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})) "
|
||||
r"(dst (?P<ip>\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b))*")
|
||||
|
||||
scenarios = [
|
||||
('namespace', {'namespace': 'ns_' + uuidutils.generate_uuid()}),
|
||||
@ -172,59 +172,109 @@ class FdbInterfaceTestCase(testscenarios.WithScenarios, base.BaseSudoTestCase):
|
||||
except priv_ip_lib.NetworkInterfaceNotFound:
|
||||
pass
|
||||
|
||||
def _list_fdb_rules(self, device):
|
||||
output = bridge_lib.FdbInterface.show(dev=device,
|
||||
namespace=self.namespace)
|
||||
rules = re.finditer(self.RULE_PATTERN, output, flags=re.MULTILINE)
|
||||
ret = {}
|
||||
for rule in rules:
|
||||
ret[rule.groupdict()['mac']] = rule.groupdict()['ip']
|
||||
return ret
|
||||
def _assert_mac(self, mac_address, device, present=True):
|
||||
msg = ('MAC address %(mac_address)s %(present)s in the FDB table for '
|
||||
'the device %(device)s in namespace %(namespace)s' %
|
||||
{'mac_address': mac_address, 'device': device,
|
||||
'namespace': self.namespace,
|
||||
'present': 'not present' if present else 'present'})
|
||||
|
||||
for _device, fdbs in bridge_lib.FdbInterface.show(
|
||||
dev=device, namespace=self.namespace).items():
|
||||
self.assertEqual(device, _device)
|
||||
macs = [fdb['mac'] for fdb in fdbs]
|
||||
if ((mac_address in macs and not present) or
|
||||
(mac_address not in macs and present)):
|
||||
self.fail(msg)
|
||||
|
||||
def _assert_ip(self, mac_address, ip_address, device):
|
||||
msg = ('Destination IP address %(ip_address)s not present in the FDB '
|
||||
'table for the MAC address %(mac_address)s and device '
|
||||
'%(device)s in namespace %(namespace)s' %
|
||||
{'mac_address': mac_address, 'device': device,
|
||||
'namespace': self.namespace, 'ip_address': ip_address})
|
||||
|
||||
for _device, fdbs in bridge_lib.FdbInterface.show(
|
||||
dev=device, namespace=self.namespace).items():
|
||||
self.assertEqual(device, _device)
|
||||
for _ in (fdb for fdb in fdbs if fdb['mac'] == mac_address and
|
||||
fdb['dst_ip'] == ip_address):
|
||||
return
|
||||
self.fail(msg)
|
||||
|
||||
def test_add_delete(self):
|
||||
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
|
||||
self._assert_mac(self.MAC1, self.device, present=False)
|
||||
bridge_lib.FdbInterface.add(self.MAC1, self.device,
|
||||
namespace=self.namespace)
|
||||
self.assertIn(self.MAC1, self._list_fdb_rules(self.device))
|
||||
self._assert_mac(self.MAC1, self.device)
|
||||
bridge_lib.FdbInterface.delete(self.MAC1, self.device,
|
||||
namespace=self.namespace)
|
||||
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
|
||||
self._assert_mac(self.MAC1, self.device, present=False)
|
||||
|
||||
def test_add_delete_dst(self):
|
||||
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device_vxlan))
|
||||
self._assert_mac(self.MAC1, self.device_vxlan, present=False)
|
||||
bridge_lib.FdbInterface.add(
|
||||
self.MAC1, self.device_vxlan, namespace=self.namespace,
|
||||
ip_dst=str(netaddr.IPNetwork(self.ip).ip))
|
||||
rules = self._list_fdb_rules(self.device_vxlan)
|
||||
self.assertEqual(str(netaddr.IPNetwork(self.ip).ip), rules[self.MAC1])
|
||||
dst_ip=str(netaddr.IPNetwork(self.ip).ip))
|
||||
self._assert_ip(self.MAC1, str(netaddr.IPNetwork(self.ip).ip),
|
||||
self.device_vxlan)
|
||||
bridge_lib.FdbInterface.delete(
|
||||
self.MAC1, self.device_vxlan, namespace=self.namespace,
|
||||
ip_dst=str(netaddr.IPNetwork(self.ip).ip))
|
||||
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device_vxlan))
|
||||
dst_ip=str(netaddr.IPNetwork(self.ip).ip))
|
||||
self._assert_mac(self.MAC1, self.device_vxlan, present=False)
|
||||
|
||||
def test_append(self):
|
||||
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
|
||||
self._assert_mac(self.MAC1, self.device, present=False)
|
||||
bridge_lib.FdbInterface.append(self.MAC1, self.device,
|
||||
namespace=self.namespace)
|
||||
self.assertIn(self.MAC1, self._list_fdb_rules(self.device))
|
||||
self._assert_mac(self.MAC1, self.device)
|
||||
|
||||
def test_append_dst(self):
|
||||
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device_vxlan))
|
||||
self._assert_mac(self.MAC1, self.device_vxlan)
|
||||
bridge_lib.FdbInterface.append(
|
||||
self.MAC1, self.device_vxlan, namespace=self.namespace,
|
||||
ip_dst=str(netaddr.IPNetwork(self.ip).ip))
|
||||
rules = self._list_fdb_rules(self.device_vxlan)
|
||||
self.assertEqual(str(netaddr.IPNetwork(self.ip).ip), rules[self.MAC1])
|
||||
dst_ip=str(netaddr.IPNetwork(self.ip).ip))
|
||||
self._assert_ip(self.MAC1, str(netaddr.IPNetwork(self.ip).ip),
|
||||
self.device_vxlan)
|
||||
|
||||
def test_replace(self):
|
||||
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
|
||||
self._assert_mac(self.MAC1, self.device, present=False)
|
||||
bridge_lib.FdbInterface.add(
|
||||
self.MAC1, self.device_vxlan, namespace=self.namespace,
|
||||
ip_dst=str(netaddr.IPNetwork(self.ip).ip))
|
||||
rules = self._list_fdb_rules(self.device_vxlan)
|
||||
self.assertEqual(str(netaddr.IPNetwork(self.ip).ip), rules[self.MAC1])
|
||||
dst_ip=str(netaddr.IPNetwork(self.ip).ip))
|
||||
self._assert_ip(self.MAC1, str(netaddr.IPNetwork(self.ip).ip),
|
||||
self.device_vxlan)
|
||||
bridge_lib.FdbInterface.replace(
|
||||
self.MAC1, self.device_vxlan, namespace=self.namespace,
|
||||
ip_dst='1.1.1.1')
|
||||
rules = self._list_fdb_rules(self.device_vxlan)
|
||||
self.assertEqual('1.1.1.1', rules[self.MAC1])
|
||||
dst_ip='1.1.1.1')
|
||||
self._assert_ip(self.MAC1, '1.1.1.1', self.device_vxlan)
|
||||
|
||||
def test_show(self):
|
||||
ip_str = str(netaddr.IPNetwork(self.ip).ip)
|
||||
bridge_lib.FdbInterface.add(
|
||||
self.MAC1, self.device_vxlan, namespace=self.namespace,
|
||||
dst_ip=ip_str)
|
||||
rules = bridge_lib.FdbInterface.show(dev=self.device_vxlan,
|
||||
namespace=self.namespace)
|
||||
self.assertEqual(1, len(rules))
|
||||
self.assertEqual(1, len(rules[self.device_vxlan]))
|
||||
self.assertEqual(self.MAC1, rules[self.device_vxlan][0]['mac'])
|
||||
self.assertEqual(ip_str, rules[self.device_vxlan][0]['dst_ip'])
|
||||
|
||||
_uuid = uuidutils.generate_uuid()
|
||||
bridge_name = ('br_' + _uuid)[:constants.DEVICE_NAME_MAX_LEN]
|
||||
priv_ip_lib.create_interface(bridge_name, self.namespace, 'bridge')
|
||||
bridge = bridge_lib.BridgeDevice(bridge_name, namespace=self.namespace)
|
||||
bridge.addif(self.device)
|
||||
rules = bridge_lib.FdbInterface.show(dev=bridge_name,
|
||||
namespace=self.namespace)
|
||||
self.assertEqual(1, len(rules))
|
||||
self._assert_mac(MAC_ALL_NODES_ADDRESS, bridge_name)
|
||||
|
||||
rules = bridge_lib.FdbInterface.show(dev=self.device,
|
||||
namespace=self.namespace)
|
||||
mac_address = ip_lib.IPDevice(self.device, self.namespace).link.address
|
||||
for rule in (rule for rule in rules[self.device] if
|
||||
rule['mac'] == mac_address):
|
||||
self.assertEqual(bridge_name, rule['master'])
|
||||
self.assertIn(rule['vlan'], (1, None))
|
||||
|
@ -220,10 +220,10 @@ class TcFiltersTestCase(functional_base.BaseSudoTestCase):
|
||||
|
||||
bridge_lib.FdbInterface.append(
|
||||
'00:00:00:00:00:00', self.device_vxlan[0], namespace=self.ns[0],
|
||||
ip_dst=str(netaddr.IPNetwork(self.ip[1]).ip))
|
||||
dst_ip=str(netaddr.IPNetwork(self.ip[1]).ip))
|
||||
bridge_lib.FdbInterface.append(
|
||||
'00:00:00:00:00:00', self.device_vxlan[1], namespace=self.ns[1],
|
||||
ip_dst=str(netaddr.IPNetwork(self.ip[0]).ip))
|
||||
dst_ip=str(netaddr.IPNetwork(self.ip[0]).ip))
|
||||
|
||||
def test_add_tc_filter_vxlan(self):
|
||||
# The traffic control is applied on the veth pair device of the first
|
||||
|
@ -19,10 +19,11 @@ from unittest import mock
|
||||
from neutron_lib import constants
|
||||
from neutron_lib.utils import helpers
|
||||
from oslo_config import cfg
|
||||
from pyroute2.netlink import exceptions as netlink_exceptions
|
||||
|
||||
from neutron.agent.l2.extensions.fdb_population import (
|
||||
FdbPopulationAgentExtension)
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.agent.linux import bridge_lib
|
||||
from neutron.plugins.ml2.drivers.linuxbridge.agent.common import (
|
||||
constants as linux_bridge_constants)
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
|
||||
@ -37,14 +38,22 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
|
||||
u'mac_address': u'fa:16:3e:ba:bc:21',
|
||||
u'port_id': u'17ceda02-43e1-48d8-beb6-35885b20cae6'}
|
||||
DELETE_MSG = {u'port_id': u'17ceda02-43e1-48d8-beb6-35885b20cae6'}
|
||||
FDB_TABLE = ("aa:aa:aa:aa:aa:aa self permanent\n"
|
||||
"bb:bb:bb:bb:bb:bb self permanent")
|
||||
|
||||
def setUp(self):
|
||||
super(FdbPopulationExtensionTestCase, self).setUp()
|
||||
cfg.CONF.set_override('shared_physical_device_mappings',
|
||||
['physnet1:p1p1'], 'FDB')
|
||||
self.DEVICE = self._get_existing_device()
|
||||
self.mock_add = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'add', return_value=0).start()
|
||||
self.mock_append = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'append', return_value=0).start()
|
||||
self.mock_replace = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'replace', return_value=0).start()
|
||||
self.mock_delete = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'delete', return_value=0).start()
|
||||
self.mock_show = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'show').start()
|
||||
|
||||
def _get_existing_device(self):
|
||||
device_mappings = helpers.parse_mappings(
|
||||
@ -52,14 +61,12 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
|
||||
DEVICES = next(iter(device_mappings.values()))
|
||||
return DEVICES[0]
|
||||
|
||||
def _get_fdb_extension(self, mock_execute, fdb_table):
|
||||
mock_execute.return_value = fdb_table
|
||||
def _get_fdb_extension(self):
|
||||
fdb_pop = FdbPopulationAgentExtension()
|
||||
fdb_pop.initialize(None, ovs_constants.EXTENSION_DRIVER_TYPE)
|
||||
return fdb_pop
|
||||
|
||||
@mock.patch('neutron.agent.common.utils.execute')
|
||||
def test_initialize(self, mock_execute):
|
||||
def test_initialize(self):
|
||||
fdb_extension = FdbPopulationAgentExtension()
|
||||
fdb_extension.initialize(None, ovs_constants.EXTENSION_DRIVER_TYPE)
|
||||
fdb_extension.initialize(None,
|
||||
@ -70,123 +77,101 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
|
||||
fdb_extension = FdbPopulationAgentExtension()
|
||||
self.assertRaises(SystemExit, fdb_extension.initialize, None, 'sriov')
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_construct_empty_fdb_table(self, mock_execute):
|
||||
self._get_fdb_extension(mock_execute, fdb_table='')
|
||||
cmd = ['bridge', 'fdb', 'show', 'dev', self.DEVICE]
|
||||
mock_execute.assert_called_once_with(cmd, run_as_root=True)
|
||||
def test_construct_empty_fdb_table(self):
|
||||
self._get_fdb_extension()
|
||||
self.mock_show.assert_called_once_with(dev=self.DEVICE)
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_construct_existing_fdb_table(self, mock_execute):
|
||||
fdb_extension = self._get_fdb_extension(mock_execute,
|
||||
fdb_table=self.FDB_TABLE)
|
||||
cmd = ['bridge', 'fdb', 'show', 'dev', self.DEVICE]
|
||||
mock_execute.assert_called_once_with(cmd, run_as_root=True)
|
||||
def test_construct_existing_fdb_table(self):
|
||||
self.mock_show.return_value = {
|
||||
self.DEVICE: [{'mac': 'aa:aa:aa:aa:aa:aa'},
|
||||
{'mac': 'bb:bb:bb:bb:bb:bb'}]
|
||||
}
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
self.mock_show.assert_called_once_with(dev=self.DEVICE)
|
||||
updated_macs_for_device = (
|
||||
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
||||
macs = [line.split()[0] for line in self.FDB_TABLE.split('\n')]
|
||||
for mac in macs:
|
||||
self.assertIn(mac, updated_macs_for_device)
|
||||
macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
|
||||
self.assertEqual(sorted(macs), sorted(updated_macs_for_device))
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_update_port_add_rule(self, mock_execute):
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, self.FDB_TABLE)
|
||||
mock_execute.reset_mock()
|
||||
def test_update_port_add_rule(self):
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
self.mock_add.return_value = True
|
||||
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
|
||||
cmd = ['bridge', 'fdb', 'add', self.UPDATE_MSG['mac_address'],
|
||||
'dev', self.DEVICE]
|
||||
mock_execute.assert_called_once_with(cmd, run_as_root=True)
|
||||
self.mock_add.assert_called_once_with(self.UPDATE_MSG['mac_address'],
|
||||
self.DEVICE)
|
||||
updated_macs_for_device = (
|
||||
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
||||
mac = self.UPDATE_MSG['mac_address']
|
||||
self.assertIn(mac, updated_macs_for_device)
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_update_port_changed_mac(self, mock_execute):
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, self.FDB_TABLE)
|
||||
mock_execute.reset_mock()
|
||||
def test_update_port_changed_mac(self):
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
mac = self.UPDATE_MSG['mac_address']
|
||||
updated_mac = 'fa:16:3e:ba:bc:33'
|
||||
commands = []
|
||||
self.mock_add.return_value = True
|
||||
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
|
||||
commands.append(['bridge', 'fdb', 'add', mac, 'dev', self.DEVICE])
|
||||
self.UPDATE_MSG['mac_address'] = updated_mac
|
||||
self.mock_delete.return_value = True
|
||||
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
|
||||
commands.append(['bridge', 'fdb', 'delete', mac, 'dev', self.DEVICE])
|
||||
commands.append(['bridge', 'fdb', 'add', updated_mac,
|
||||
'dev', self.DEVICE])
|
||||
calls = []
|
||||
for cmd in commands:
|
||||
calls.append(mock.call(cmd, run_as_root=True))
|
||||
mock_execute.assert_has_calls(calls)
|
||||
calls_add = [mock.call(mac, self.DEVICE),
|
||||
mock.call(updated_mac, self.DEVICE)]
|
||||
self.mock_add.assert_has_calls(calls_add)
|
||||
self.mock_delete.assert_called_once_with(mac, self.DEVICE)
|
||||
updated_macs_for_device = (
|
||||
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
||||
self.assertIn(updated_mac, updated_macs_for_device)
|
||||
self.assertNotIn(mac, updated_macs_for_device)
|
||||
|
||||
@mock.patch('neutron.agent.common.utils.execute')
|
||||
def test_unpermitted_device_owner(self, mock_execute):
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
||||
mock_execute.reset_mock()
|
||||
def test_unpermitted_device_owner(self):
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
details = copy.deepcopy(self.UPDATE_MSG)
|
||||
details['device_owner'] = constants.DEVICE_OWNER_LOADBALANCER
|
||||
fdb_extension.handle_port(context=None, details=details)
|
||||
self.assertFalse(mock_execute.called)
|
||||
updated_macs_for_device = (
|
||||
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
||||
mac = self.UPDATE_MSG['mac_address']
|
||||
self.assertNotIn(mac, updated_macs_for_device)
|
||||
|
||||
@mock.patch('neutron.agent.common.utils.execute')
|
||||
def test_catch_init_exception(self, mock_execute):
|
||||
mock_execute.side_effect = RuntimeError
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
||||
def test_catch_init_exception(self):
|
||||
self.mock_add.side_effect = netlink_exceptions.NetlinkError
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
updated_macs_for_device = (
|
||||
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
||||
self.assertIsNone(updated_macs_for_device)
|
||||
self.assertEqual([], updated_macs_for_device)
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_catch_update_port_exception(self, mock_execute):
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
||||
mock_execute.side_effect = RuntimeError
|
||||
def test_catch_update_port_exception(self):
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
self.mock_add.return_value = False
|
||||
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
|
||||
updated_macs_for_device = (
|
||||
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
||||
mac = self.UPDATE_MSG['mac_address']
|
||||
self.assertNotIn(mac, updated_macs_for_device)
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_catch_delete_port_exception(self, mock_execute):
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
||||
def test_catch_delete_port_exception(self):
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
self.mock_add.return_value = True
|
||||
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
|
||||
mock_execute.side_effect = RuntimeError
|
||||
self.mock_delete.return_value = False
|
||||
fdb_extension.delete_port(context=None, details=self.DELETE_MSG)
|
||||
updated_macs_for_device = (
|
||||
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
||||
mac = self.UPDATE_MSG['mac_address']
|
||||
self.assertIn(mac, updated_macs_for_device)
|
||||
self.assertIn(self.UPDATE_MSG['mac_address'], updated_macs_for_device)
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_delete_port(self, mock_execute):
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
||||
def test_delete_port(self):
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
self.mock_add.return_value = True
|
||||
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
|
||||
mock_execute.reset_mock()
|
||||
self.mock_delete.return_value = False
|
||||
fdb_extension.delete_port(context=None, details=self.DELETE_MSG)
|
||||
cmd = ['bridge', 'fdb', 'delete', self.UPDATE_MSG['mac_address'],
|
||||
'dev', self.DEVICE]
|
||||
mock_execute.assert_called_once_with(cmd, run_as_root=True)
|
||||
self.mock_delete.assert_called_once_with(
|
||||
self.UPDATE_MSG['mac_address'], self.DEVICE)
|
||||
|
||||
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
|
||||
def test_multiple_devices(self, mock_execute):
|
||||
def test_multiple_devices(self):
|
||||
cfg.CONF.set_override('shared_physical_device_mappings',
|
||||
['physnet1:p1p1', 'physnet1:p2p2'], 'FDB')
|
||||
|
||||
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
||||
fdb_extension = self._get_fdb_extension()
|
||||
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
|
||||
mac = self.UPDATE_MSG['mac_address']
|
||||
calls = []
|
||||
cmd = ['bridge', 'fdb', 'add', mac, 'dev', 'p1p1']
|
||||
calls.append(mock.call(cmd, run_as_root=True))
|
||||
cmd = ['bridge', 'fdb', 'add', mac, 'dev', 'p2p2']
|
||||
calls.append(mock.call(cmd, run_as_root=True))
|
||||
mock_execute.assert_has_calls(calls, any_order=True)
|
||||
calls = [mock.call(self.UPDATE_MSG['mac_address'], 'p1p1'),
|
||||
mock.call(self.UPDATE_MSG['mac_address'], 'p2p2')]
|
||||
self.mock_add.assert_has_calls(calls)
|
||||
|
@ -847,60 +847,33 @@ class TestLinuxBridgeManager(base.BaseTestCase):
|
||||
vxlan_mcast_supported=False)
|
||||
|
||||
def _check_vxlan_ucast_supported(
|
||||
self, expected, l2_population, iproute_arg_supported, fdb_append):
|
||||
self, expected, l2_population, fdb_append):
|
||||
cfg.CONF.set_override('l2_population', l2_population, 'VXLAN')
|
||||
with mock.patch.object(ip_lib, 'device_exists', return_value=False),\
|
||||
mock.patch.object(ip_lib, 'vxlan_in_use', return_value=False),\
|
||||
mock.patch.object(self.lbm,
|
||||
'delete_interface',
|
||||
return_value=None),\
|
||||
mock.patch.object(self.lbm,
|
||||
'ensure_vxlan',
|
||||
return_value=None),\
|
||||
mock.patch.object(
|
||||
ip_lib.IpNetnsCommand,
|
||||
'execute',
|
||||
side_effect=None if fdb_append else RuntimeError()),\
|
||||
mock.patch.object(ip_lib,
|
||||
'iproute_arg_supported',
|
||||
return_value=iproute_arg_supported):
|
||||
mock.patch.object(self.lbm, 'delete_interface'),\
|
||||
mock.patch.object(self.lbm, 'ensure_vxlan',
|
||||
return_value=None), \
|
||||
mock.patch.object(bridge_lib.FdbInterface, 'append',
|
||||
return_value=fdb_append):
|
||||
self.assertEqual(expected, self.lbm.vxlan_ucast_supported())
|
||||
|
||||
def test_vxlan_ucast_supported(self):
|
||||
self._check_vxlan_ucast_supported(
|
||||
expected=False,
|
||||
l2_population=False, iproute_arg_supported=True, fdb_append=True)
|
||||
expected=False, l2_population=False, fdb_append=mock.ANY)
|
||||
self._check_vxlan_ucast_supported(
|
||||
expected=False,
|
||||
l2_population=True, iproute_arg_supported=False, fdb_append=True)
|
||||
expected=False, l2_population=True, fdb_append=False)
|
||||
self._check_vxlan_ucast_supported(
|
||||
expected=False,
|
||||
l2_population=True, iproute_arg_supported=True, fdb_append=False)
|
||||
self._check_vxlan_ucast_supported(
|
||||
expected=True,
|
||||
l2_population=True, iproute_arg_supported=True, fdb_append=True)
|
||||
expected=True, l2_population=True, fdb_append=True)
|
||||
|
||||
def _check_vxlan_mcast_supported(
|
||||
self, expected, vxlan_group, iproute_arg_supported):
|
||||
def _check_vxlan_mcast_supported(self, expected, vxlan_group):
|
||||
cfg.CONF.set_override('vxlan_group', vxlan_group, 'VXLAN')
|
||||
with mock.patch.object(
|
||||
ip_lib, 'iproute_arg_supported',
|
||||
return_value=iproute_arg_supported):
|
||||
self.assertEqual(expected, self.lbm.vxlan_mcast_supported())
|
||||
self.assertEqual(expected, self.lbm.vxlan_mcast_supported())
|
||||
|
||||
def test_vxlan_mcast_supported(self):
|
||||
self._check_vxlan_mcast_supported(
|
||||
expected=False,
|
||||
vxlan_group='',
|
||||
iproute_arg_supported=True)
|
||||
self._check_vxlan_mcast_supported(
|
||||
expected=False,
|
||||
vxlan_group='224.0.0.1',
|
||||
iproute_arg_supported=False)
|
||||
self._check_vxlan_mcast_supported(
|
||||
expected=True,
|
||||
vxlan_group='224.0.0.1',
|
||||
iproute_arg_supported=True)
|
||||
self._check_vxlan_mcast_supported(expected=False, vxlan_group='')
|
||||
self._check_vxlan_mcast_supported(expected=True,
|
||||
vxlan_group='224.0.0.1')
|
||||
|
||||
def _test_ensure_port_admin_state(self, admin_state):
|
||||
port_id = 'fake_id'
|
||||
@ -975,6 +948,16 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
|
||||
segment.segmentation_id = 1
|
||||
self.lb_rpc.network_map['net_id'] = segment
|
||||
cfg.CONF.set_default('host', 'host')
|
||||
self.mock_add = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'add').start()
|
||||
self.mock_append = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'append').start()
|
||||
self.mock_replace = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'replace').start()
|
||||
self.mock_delete = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'delete').start()
|
||||
self.mock_show = mock.patch.object(
|
||||
bridge_lib.FdbInterface, 'show').start()
|
||||
|
||||
def test_network_delete_mapped_net(self):
|
||||
mock_net = mock.Mock()
|
||||
@ -1070,26 +1053,14 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}
|
||||
|
||||
with mock.patch.object(ip_lib.IpNetnsCommand, 'execute',
|
||||
return_value='') as execute_fn, \
|
||||
mock.patch.object(ip_lib, 'add_neigh_entry',
|
||||
return_value='') as add_fn:
|
||||
with mock.patch.object(ip_lib, 'add_neigh_entry',
|
||||
return_value='') as add_fn:
|
||||
self.lb_rpc.fdb_add(None, fdb_entries)
|
||||
|
||||
expected = [
|
||||
mock.call(['bridge', 'fdb', 'show', 'dev', 'vxlan-1'],
|
||||
run_as_root=True),
|
||||
mock.call(['bridge', 'fdb', 'add',
|
||||
constants.FLOODING_ENTRY[0],
|
||||
'dev', 'vxlan-1', 'dst', 'agent_ip'],
|
||||
run_as_root=True,
|
||||
check_exit_code=False),
|
||||
mock.call(['bridge', 'fdb', 'replace', 'port_mac', 'dev',
|
||||
'vxlan-1', 'dst', 'agent_ip'],
|
||||
run_as_root=True,
|
||||
check_exit_code=False),
|
||||
]
|
||||
execute_fn.assert_has_calls(expected)
|
||||
self.mock_show.assert_called_once_with(dev='vxlan-1')
|
||||
self.mock_add.assert_called_once_with(
|
||||
constants.FLOODING_ENTRY[0], 'vxlan-1', dst_ip='agent_ip')
|
||||
self.mock_replace.assert_called_once_with(
|
||||
'port_mac', 'vxlan-1', dst_ip='agent_ip')
|
||||
if proxy_enabled:
|
||||
add_fn.assert_called_with('port_ip', 'port_mac', 'vxlan-1')
|
||||
else:
|
||||
@ -1139,24 +1110,13 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}
|
||||
|
||||
with mock.patch.object(ip_lib.IpNetnsCommand, 'execute',
|
||||
return_value='') as execute_fn, \
|
||||
mock.patch.object(ip_lib, 'delete_neigh_entry',
|
||||
return_value='') as del_fn:
|
||||
with mock.patch.object(ip_lib, 'delete_neigh_entry',
|
||||
return_value='') as del_fn:
|
||||
self.lb_rpc.fdb_remove(None, fdb_entries)
|
||||
|
||||
expected = [
|
||||
mock.call(['bridge', 'fdb', 'delete',
|
||||
constants.FLOODING_ENTRY[0],
|
||||
'dev', 'vxlan-1', 'dst', 'agent_ip'],
|
||||
run_as_root=True,
|
||||
check_exit_code=False),
|
||||
mock.call(['bridge', 'fdb', 'delete', 'port_mac',
|
||||
'dev', 'vxlan-1', 'dst', 'agent_ip'],
|
||||
run_as_root=True,
|
||||
check_exit_code=False),
|
||||
]
|
||||
execute_fn.assert_has_calls(expected)
|
||||
calls = [mock.call(constants.FLOODING_ENTRY[0], 'vxlan-1',
|
||||
dst_ip='agent_ip'),
|
||||
mock.call('port_mac', 'vxlan-1', dst_ip='agent_ip')]
|
||||
self.mock_delete.assert_has_calls(calls)
|
||||
if proxy_enabled:
|
||||
del_fn.assert_called_with('port_ip', 'port_mac', 'vxlan-1')
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user