Merge "Add namespace support for "bridge" commands"
This commit is contained in:
commit
b43aad9d75
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue