239 lines
11 KiB
Python
239 lines
11 KiB
Python
#
|
|
# 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 mock
|
|
import oslo_messaging
|
|
from oslo_utils import uuidutils
|
|
import testtools
|
|
|
|
from neutron.api.rpc.callbacks import events
|
|
from neutron.api.rpc.handlers import resources_rpc
|
|
from neutron.objects import trunk
|
|
from neutron.services.trunk import constants as t_const
|
|
from neutron.services.trunk.drivers.linuxbridge.agent import driver
|
|
from neutron.services.trunk.drivers.linuxbridge.agent import trunk_plumber
|
|
from neutron.tests import base
|
|
|
|
|
|
class LinuxBridgeTrunkDriverTestCase(base.BaseTestCase):
|
|
def setUp(self):
|
|
super(LinuxBridgeTrunkDriverTestCase, self).setUp()
|
|
self.plumber = mock.create_autospec(trunk_plumber.Plumber())
|
|
self.stub = mock.create_autospec(driver.trunk_rpc.TrunkStub())
|
|
self.tapi = mock.create_autospec(driver._TrunkAPI(self.stub))
|
|
self.lbd = driver.LinuxBridgeTrunkDriver(self.plumber, self.tapi)
|
|
self.trunk = trunk.Trunk(id=uuidutils.generate_uuid(),
|
|
port_id=uuidutils.generate_uuid(),
|
|
tenant_id=uuidutils.generate_uuid())
|
|
self.subports = [trunk.SubPort(id=uuidutils.generate_uuid(),
|
|
port_id=uuidutils.generate_uuid(),
|
|
segmentation_type='vlan',
|
|
trunk_id=self.trunk.id,
|
|
segmentation_id=i)
|
|
for i in range(20)]
|
|
self.trunk.sub_ports = self.subports
|
|
|
|
def test_handle_trunks_created(self):
|
|
self._test_handle_trunks_wire_event(events.CREATED)
|
|
|
|
def test_handle_trunks_updated(self):
|
|
self._test_handle_trunks_wire_event(events.UPDATED)
|
|
|
|
def _test_handle_trunks_wire_event(self, event):
|
|
self.plumber.trunk_on_host.return_value = True
|
|
self.lbd.handle_trunks([self.trunk], event)
|
|
self.tapi.put_trunk.assert_called_once_with(
|
|
self.trunk.port_id, self.trunk)
|
|
self.tapi.bind_subports_to_host.assert_called_once_with(
|
|
mock.ANY, self.trunk)
|
|
self.assertFalse(self.plumber.delete_trunk_subports.called)
|
|
|
|
def test_handle_trunks_deleted(self):
|
|
self.lbd.handle_trunks([self.trunk], events.DELETED)
|
|
self.tapi.put_trunk.assert_called_once_with(
|
|
self.trunk.port_id, None)
|
|
self.plumber.delete_trunk_subports.assert_called_once_with(self.trunk)
|
|
|
|
def test_handle_subports_deleted(self):
|
|
self.tapi.get_trunk.return_value = self.trunk
|
|
self.lbd.handle_subports(self.trunk.sub_ports, events.DELETED)
|
|
self.assertEqual(20, len(self.tapi.delete_trunk_subport.mock_calls))
|
|
# should have tried to wire trunk at the end with state
|
|
self.plumber.trunk_on_host.assert_called_once_with(self.trunk)
|
|
|
|
def test_handle_subports_created(self):
|
|
self.tapi.get_trunk.return_value = self.trunk
|
|
self.lbd.handle_subports(self.trunk.sub_ports, events.CREATED)
|
|
self.assertEqual(20, len(self.tapi.put_trunk_subport.mock_calls))
|
|
# should have tried to wire trunk at the end with state
|
|
self.plumber.trunk_on_host.assert_called_once_with(self.trunk)
|
|
|
|
def test_agent_port_change_is_trunk(self):
|
|
self.tapi.get_trunk.return_value = self.trunk
|
|
self.lbd.agent_port_change('resource', 'event', 'trigger', 'context',
|
|
{'port_id': self.trunk.port_id})
|
|
# should have tried to wire trunk
|
|
self.plumber.trunk_on_host.assert_called_once_with(self.trunk)
|
|
|
|
def test_agent_port_change_not_trunk(self):
|
|
self.tapi.get_trunk.return_value = None
|
|
self.tapi.get_trunk_for_subport.return_value = None
|
|
other_port_id = uuidutils.generate_uuid()
|
|
self.lbd.agent_port_change('resource', 'event', 'trigger', 'context',
|
|
{'port_id': other_port_id})
|
|
self.plumber.delete_subports_by_port_id.assert_called_once_with(
|
|
other_port_id)
|
|
|
|
def test_agent_port_change_is_subport(self):
|
|
self.tapi.get_trunk.return_value = None
|
|
self.tapi.get_trunk_for_subport.return_value = self.trunk
|
|
self.lbd.agent_port_change('resource', 'event', 'trigger', 'context',
|
|
{'port_id': self.trunk.sub_ports[0].port_id,
|
|
'mac_address': 'mac_addr'})
|
|
self.plumber.delete_subports_by_port_id.assert_called_once_with(
|
|
self.trunk.sub_ports[0].port_id)
|
|
self.tapi.get_trunk_for_subport.assert_called_once_with(
|
|
mock.ANY, self.trunk.sub_ports[0].port_id)
|
|
self.plumber.set_port_mac.assert_called_once_with(
|
|
self.trunk.sub_ports[0].port_id, 'mac_addr')
|
|
|
|
def test_wire_trunk_happy_path(self):
|
|
self.lbd.wire_trunk('ctx', self.trunk)
|
|
self.tapi.bind_subports_to_host.assert_called_once_with(
|
|
'ctx', self.trunk)
|
|
self.plumber.ensure_trunk_subports.assert_called_once_with(self.trunk)
|
|
self.tapi.set_trunk_status.assert_called_once_with(
|
|
'ctx', self.trunk, t_const.ACTIVE_STATUS)
|
|
|
|
def test_wire_trunk_not_on_host(self):
|
|
# trunk device not on host
|
|
self.plumber.trunk_on_host.return_value = False
|
|
self.lbd.wire_trunk('ctx', self.trunk)
|
|
# don't bind and don't set status
|
|
self.assertFalse(self.tapi.bind_subports_to_host.called)
|
|
self.assertFalse(self.tapi.set_trunk_status.called)
|
|
|
|
def test_wire_trunk_concurrent_removal(self):
|
|
self.plumber.trunk_on_host.side_effect = [True, False]
|
|
self.plumber.ensure_trunk_subports.side_effect = ValueError()
|
|
self.lbd.wire_trunk('ctx', self.trunk)
|
|
# we don't change status if port was just removed
|
|
self.assertFalse(self.tapi.set_trunk_status.called)
|
|
|
|
def test_wire_trunk_other_exception(self):
|
|
self.plumber.ensure_trunk_subports.side_effect = ValueError()
|
|
self.lbd.wire_trunk('ctx', self.trunk)
|
|
# degraded due to dataplane failure
|
|
self.tapi.set_trunk_status.assert_called_once_with(
|
|
'ctx', self.trunk, t_const.DEGRADED_STATUS)
|
|
|
|
|
|
class TrunkAPITestCase(base.BaseTestCase):
|
|
def setUp(self):
|
|
super(TrunkAPITestCase, self).setUp()
|
|
self.stub = mock.create_autospec(driver.trunk_rpc.TrunkStub())
|
|
self.tapi = driver._TrunkAPI(self.stub)
|
|
self.trunk = trunk.Trunk(id=uuidutils.generate_uuid(),
|
|
port_id=uuidutils.generate_uuid(),
|
|
tenant_id=uuidutils.generate_uuid())
|
|
self.subports = [trunk.SubPort(id=uuidutils.generate_uuid(),
|
|
port_id=uuidutils.generate_uuid(),
|
|
segmentation_type='vlan',
|
|
trunk_id=self.trunk.id,
|
|
segmentation_id=i)
|
|
for i in range(20)]
|
|
self.trunk.sub_ports = self.subports
|
|
self.stub.get_trunk_details.return_value = self.trunk
|
|
|
|
def test_fetch_trunk(self):
|
|
self.assertEqual(self.trunk, self.tapi._fetch_trunk('ctx', 'port'))
|
|
self.stub.get_trunk_details.assert_called_once_with('ctx', 'port')
|
|
|
|
def test_fetch_trunk_missing(self):
|
|
self.stub.get_trunk_details.side_effect = (
|
|
resources_rpc.ResourceNotFound(resource_id='1', resource_type='1'))
|
|
self.assertIsNone(self.tapi._fetch_trunk('ctx', 'port'))
|
|
|
|
def test_fetch_trunk_plugin_disabled(self):
|
|
self.stub.get_trunk_details.side_effect = (
|
|
oslo_messaging.RemoteError('CallbackNotFound'))
|
|
self.assertIsNone(self.tapi._fetch_trunk('ctx', 'port'))
|
|
|
|
def test_fetch_trunk_plugin_other_error(self):
|
|
self.stub.get_trunk_details.side_effect = (
|
|
oslo_messaging.RemoteError('vacuum full'))
|
|
with testtools.ExpectedException(oslo_messaging.RemoteError):
|
|
self.tapi._fetch_trunk('ctx', 'port')
|
|
|
|
def test_set_trunk_status(self):
|
|
self.tapi.set_trunk_status('ctx', self.trunk, 'STATUS')
|
|
self.stub.update_trunk_status.assert_called_once_with(
|
|
'ctx', self.trunk.id, 'STATUS')
|
|
|
|
def test_bind_subports_to_host(self):
|
|
self.tapi.bind_subports_to_host('ctx', self.trunk)
|
|
self.stub.update_subport_bindings.assert_called_once_with(
|
|
'ctx', self.trunk.sub_ports)
|
|
|
|
def test_put_trunk_subport_non_existent_trunk(self):
|
|
# trunks not registered are ignored
|
|
self.tapi.put_trunk_subport(
|
|
'non_trunk_id', self.trunk.sub_ports[0])
|
|
|
|
def test_put_trunk_subport(self):
|
|
self.tapi.put_trunk(self.trunk.port_id, self.trunk)
|
|
new = trunk.SubPort(id=uuidutils.generate_uuid(),
|
|
port_id=uuidutils.generate_uuid(),
|
|
segmentation_type='vlan',
|
|
trunk_id=self.trunk.id,
|
|
segmentation_id=1010)
|
|
self.tapi.put_trunk_subport(self.trunk.id, new)
|
|
subs = self.tapi.get_trunk('ctx', self.trunk.port_id).sub_ports
|
|
self.assertEqual(21, len(subs))
|
|
self.assertEqual(new, subs[-1])
|
|
|
|
def test_delete_trunk_subport(self):
|
|
self.tapi.put_trunk(self.trunk.port_id, self.trunk)
|
|
sub = self.trunk.sub_ports[10]
|
|
self.tapi.delete_trunk_subport(self.trunk.id, sub)
|
|
subs = self.tapi.get_trunk('ctx', self.trunk.port_id).sub_ports
|
|
self.assertNotIn(sub, subs)
|
|
self.assertEqual(19, len(subs))
|
|
|
|
def test_get_trunk(self):
|
|
self.tapi.put_trunk(self.trunk.port_id, self.trunk)
|
|
self.assertEqual(self.trunk,
|
|
self.tapi.get_trunk('ctx', self.trunk.port_id))
|
|
self.tapi.get_trunk('ctx', self.trunk.port_id)
|
|
self.assertFalse(self.stub.get_trunk_details.called)
|
|
|
|
def test_get_trunk_cache_miss(self):
|
|
self.assertEqual(self.trunk,
|
|
self.tapi.get_trunk('ctx', self.trunk.port_id))
|
|
self.tapi.get_trunk('ctx', self.trunk.port_id)
|
|
self.assertEqual(1, len(self.stub.get_trunk_details.mock_calls))
|
|
|
|
def test_get_trunk_not_found(self):
|
|
self.stub.get_trunk_details.side_effect = (
|
|
resources_rpc.ResourceNotFound(resource_id='1', resource_type='1'))
|
|
self.assertIsNone(self.tapi.get_trunk('ctx', self.trunk.port_id))
|
|
self.tapi.get_trunk('ctx', self.trunk.port_id)
|
|
self.assertEqual(1, len(self.stub.get_trunk_details.mock_calls))
|
|
|
|
def test_get_trunk_for_subport(self):
|
|
self.tapi.put_trunk(self.trunk.port_id, self.trunk)
|
|
t = self.tapi.get_trunk_for_subport(
|
|
'ctx', self.trunk.sub_ports[0].port_id)
|
|
self.assertEqual(self.trunk, t)
|