Merge "Add namespace support for "bridge" commands"

This commit is contained in:
Zuul 2019-04-29 15:59:37 +00:00 committed by Gerrit Code Review
commit b43aad9d75
4 changed files with 131 additions and 24 deletions

View File

@ -21,7 +21,6 @@ import os
from oslo_utils import excutils
from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils
# NOTE(toabctl): Don't use /sys/devices/virtual/net here because not all tap
# devices are listed here (i.e. when using Xen)
@ -109,32 +108,37 @@ class BridgeDevice(ip_lib.IPDevice):
class FdbInterface(object):
"""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)
@classmethod
def _execute(cls, op, mac, dev, ip_dst, **kwargs):
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]
return utils.execute(cmd, run_as_root=True, **kwargs)
cls._execute_bridge(cmd, namespace, **kwargs)
@classmethod
def add(cls, mac, dev, ip_dst=None, **kwargs):
return cls._execute('add', mac, dev, ip_dst, **kwargs)
def add(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
return cls._cmd('add', mac, dev, ip_dst, namespace, **kwargs)
@classmethod
def append(cls, mac, dev, ip_dst=None, **kwargs):
return cls._execute('append', mac, dev, ip_dst, **kwargs)
def append(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
return cls._cmd('append', mac, dev, ip_dst, namespace, **kwargs)
@classmethod
def replace(cls, mac, dev, ip_dst=None, **kwargs):
return cls._execute('replace', mac, dev, ip_dst, **kwargs)
def replace(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
return cls._cmd('replace', mac, dev, ip_dst, namespace, **kwargs)
@classmethod
def delete(cls, mac, dev, ip_dst=None, **kwargs):
return cls._execute('delete', mac, dev, ip_dst, **kwargs)
def delete(cls, mac, dev, ip_dst=None, namespace=None, **kwargs):
return cls._cmd('delete', mac, dev, ip_dst, namespace, **kwargs)
@classmethod
def show(cls, dev=None, **kwargs):
def show(cls, dev=None, namespace=None, **kwargs):
cmd = ['bridge', 'fdb', 'show']
if dev:
cmd += ['dev', dev]
return utils.execute(cmd, run_as_root=True, **kwargs)
return cls._execute_bridge(cmd, namespace, **kwargs)

View File

@ -12,9 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import re
import netaddr
from oslo_utils import uuidutils
import testscenarios
from neutron.agent.linux import bridge_lib
from neutron.agent.linux import ip_lib
from neutron.privileged.agent.linux import ip_lib as priv_ip_lib
from neutron.tests.common import net_helpers
from neutron.tests.functional import base
@ -85,3 +91,99 @@ class BridgeLibTestCase(base.BaseSudoTestCase):
with open(sysfs_path, 'r') as sysfs_disable_ipv6_file:
sysfs_disable_ipv6 = sysfs_disable_ipv6_file.read()
self.assertEqual("1\n", sysfs_disable_ipv6)
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()}),
('no_namespace', {'namespace': None})
]
def setUp(self):
super(FdbInterfaceTestCase, self).setUp()
self.device = 'interface'
self.device_vxlan = 'int_vxlan'
self.ip = '10.220.0.1/24'
self.ip_vxlan = '10.221.0.1/24'
if self.namespace:
priv_ip_lib.create_netns(self.namespace)
self.addCleanup(self._cleanup)
ip_wrapper = ip_lib.IPWrapper(self.namespace)
ip_wrapper.add_dummy(self.device)
ip_wrapper.add_vxlan(self.device_vxlan, 100, dev=self.device)
ip_device = ip_lib.IPDevice(self.device, self.namespace)
ip_device.link.set_up()
ip_device.addr.add(self.ip)
ip_device_vxlan = ip_lib.IPDevice(self.device_vxlan, self.namespace)
ip_device_vxlan.link.set_up()
ip_device_vxlan.addr.add(self.ip_vxlan)
def _cleanup(self):
if self.namespace:
priv_ip_lib.remove_netns(self.namespace)
else:
priv_ip_lib.delete_interface(self.device_vxlan, None)
priv_ip_lib.delete_interface(self.device, None)
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 test_add_delete(self):
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
bridge_lib.FdbInterface.add(self.MAC1, self.device,
namespace=self.namespace)
self.assertIn(self.MAC1, self._list_fdb_rules(self.device))
bridge_lib.FdbInterface.delete(self.MAC1, self.device,
namespace=self.namespace)
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
def test_add_delete_dst(self):
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device_vxlan))
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])
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))
def test_append(self):
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
bridge_lib.FdbInterface.append(self.MAC1, self.device,
namespace=self.namespace)
self.assertIn(self.MAC1, self._list_fdb_rules(self.device))
def test_append_dst(self):
self.assertNotIn(self.MAC1, self._list_fdb_rules(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])
def test_replace(self):
self.assertNotIn(self.MAC1, self._list_fdb_rules(self.device))
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])
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])

View File

@ -23,6 +23,7 @@ import six
from neutron.agent.l2.extensions.fdb_population import (
FdbPopulationAgentExtension)
from neutron.agent.linux import ip_lib
from neutron.plugins.ml2.drivers.linuxbridge.agent.common import (
constants as linux_bridge_constants)
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
@ -70,13 +71,13 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
fdb_extension = FdbPopulationAgentExtension()
self.assertRaises(SystemExit, fdb_extension.initialize, None, 'sriov')
@mock.patch('neutron.agent.linux.utils.execute')
@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)
@mock.patch('neutron.agent.linux.utils.execute')
@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)
@ -88,7 +89,7 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
for mac in macs:
self.assertIn(mac, updated_macs_for_device)
@mock.patch('neutron.agent.linux.utils.execute')
@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()
@ -101,7 +102,7 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
mac = self.UPDATE_MSG['mac_address']
self.assertIn(mac, updated_macs_for_device)
@mock.patch('neutron.agent.linux.utils.execute')
@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()
@ -145,7 +146,7 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
self.assertIsNone(updated_macs_for_device)
@mock.patch('neutron.agent.linux.utils.execute')
@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
@ -155,7 +156,7 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
mac = self.UPDATE_MSG['mac_address']
self.assertNotIn(mac, updated_macs_for_device)
@mock.patch('neutron.agent.linux.utils.execute')
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
def test_catch_delete_port_exception(self, mock_execute):
fdb_extension = self._get_fdb_extension(mock_execute, '')
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
@ -166,7 +167,7 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
mac = self.UPDATE_MSG['mac_address']
self.assertIn(mac, updated_macs_for_device)
@mock.patch('neutron.agent.linux.utils.execute')
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
def test_delete_port(self, mock_execute):
fdb_extension = self._get_fdb_extension(mock_execute, '')
fdb_extension.handle_port(context=None, details=self.UPDATE_MSG)
@ -176,7 +177,7 @@ class FdbPopulationExtensionTestCase(base.BaseTestCase):
'dev', self.DEVICE]
mock_execute.assert_called_once_with(cmd, run_as_root=True)
@mock.patch('neutron.agent.linux.utils.execute')
@mock.patch.object(ip_lib.IpNetnsCommand, 'execute')
def test_multiple_devices(self, mock_execute):
cfg.CONF.set_override('shared_physical_device_mappings',
['physnet1:p1p1', 'physnet1:p2p2'], 'FDB')

View File

@ -859,7 +859,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
'ensure_vxlan',
return_value=None),\
mock.patch.object(
utils,
ip_lib.IpNetnsCommand,
'execute',
side_effect=None if fdb_append else RuntimeError()),\
mock.patch.object(ip_lib,
@ -1071,7 +1071,7 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
'network_type': 'vxlan',
'segment_id': 1}}
with mock.patch.object(utils, 'execute',
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:
@ -1140,7 +1140,7 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
'network_type': 'vxlan',
'segment_id': 1}}
with mock.patch.object(utils, 'execute',
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: