You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
8.9 KiB
Python
193 lines
8.9 KiB
Python
# Copyright (c) 2016 Mellanox Technologies, Ltd
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import copy
|
|
|
|
import mock
|
|
from neutron_lib import constants
|
|
from neutron_lib.utils import helpers
|
|
from oslo_config import cfg
|
|
import six
|
|
|
|
from neutron.agent.l2.extensions.fdb_population import (
|
|
FdbPopulationAgentExtension)
|
|
from neutron.plugins.ml2.drivers.linuxbridge.agent.common import (
|
|
constants as linux_bridge_constants)
|
|
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
|
|
constants as ovs_constants)
|
|
from neutron.tests import base
|
|
|
|
|
|
class FdbPopulationExtensionTestCase(base.BaseTestCase):
|
|
|
|
UPDATE_MSG = {u'device_owner': constants.DEVICE_OWNER_ROUTER_INTF,
|
|
u'physical_network': u'physnet1',
|
|
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()
|
|
|
|
def _get_existing_device(self):
|
|
device_mappings = helpers.parse_mappings(
|
|
cfg.CONF.FDB.shared_physical_device_mappings, unique_keys=False)
|
|
DEVICES = six.next(six.itervalues(device_mappings))
|
|
return DEVICES[0]
|
|
|
|
def _get_fdb_extension(self, mock_execute, fdb_table):
|
|
mock_execute.return_value = fdb_table
|
|
fdb_pop = FdbPopulationAgentExtension()
|
|
fdb_pop.initialize(None, ovs_constants.EXTENSION_DRIVER_TYPE)
|
|
return fdb_pop
|
|
|
|
@mock.patch('neutron.agent.linux.utils.execute')
|
|
def test_initialize(self, mock_execute):
|
|
fdb_extension = FdbPopulationAgentExtension()
|
|
fdb_extension.initialize(None, ovs_constants.EXTENSION_DRIVER_TYPE)
|
|
fdb_extension.initialize(None,
|
|
linux_bridge_constants.EXTENSION_DRIVER_TYPE)
|
|
|
|
@mock.patch('neutron.agent.linux.utils.execute')
|
|
def test_initialize_invalid_agent(self, mock_execute):
|
|
fdb_extension = FdbPopulationAgentExtension()
|
|
self.assertRaises(SystemExit, fdb_extension.initialize, None, 'sriov')
|
|
|
|
@mock.patch('neutron.agent.linux.utils.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')
|
|
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)
|
|
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)
|
|
|
|
@mock.patch('neutron.agent.linux.utils.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()
|
|
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)
|
|
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('neutron.agent.linux.utils.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()
|
|
mac = self.UPDATE_MSG['mac_address']
|
|
updated_mac = 'fa:16:3e:ba:bc:33'
|
|
commands = []
|
|
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
|
|
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)
|
|
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.linux.utils.execute')
|
|
def test_unpermitted_device_owner(self, mock_execute):
|
|
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
|
mock_execute.reset_mock()
|
|
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.linux.utils.execute')
|
|
def test_catch_init_exception(self, mock_execute):
|
|
mock_execute.side_effect = RuntimeError
|
|
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
|
updated_macs_for_device = (
|
|
fdb_extension.fdb_tracker.device_to_macs.get(self.DEVICE))
|
|
self.assertIsNone(updated_macs_for_device)
|
|
|
|
@mock.patch('neutron.agent.linux.utils.execute')
|
|
def test_catch_update_port_exception(self, mock_execute):
|
|
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
|
mock_execute.side_effect = RuntimeError
|
|
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('neutron.agent.linux.utils.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)
|
|
mock_execute.side_effect = RuntimeError
|
|
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)
|
|
|
|
@mock.patch('neutron.agent.linux.utils.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)
|
|
mock_execute.reset_mock()
|
|
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)
|
|
|
|
@mock.patch('neutron.agent.linux.utils.execute')
|
|
def test_multiple_devices(self, mock_execute):
|
|
cfg.CONF.set_override('shared_physical_device_mappings',
|
|
['physnet1:p1p1', 'physnet1:p2p2'], 'FDB')
|
|
|
|
fdb_extension = self._get_fdb_extension(mock_execute, '')
|
|
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)
|