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