Merge "Adds trunked VLANs support"
This commit is contained in:
commit
83f8a20c82
|
@ -250,12 +250,7 @@ networking-plugin-hyperv_agent.html
|
|||
self._utils.connect_vnic_to_vswitch(map['vswitch_name'], port_id)
|
||||
|
||||
if network_type == constants.TYPE_VLAN:
|
||||
LOG.info(_LI('Binding VLAN ID %(segmentation_id)s '
|
||||
'to switch port %(port_id)s'),
|
||||
dict(segmentation_id=segmentation_id, port_id=port_id))
|
||||
self._utils.set_vswitch_port_vlan_id(
|
||||
segmentation_id,
|
||||
port_id)
|
||||
self._vlan_driver.bind_vlan_port(port_id, segmentation_id)
|
||||
elif network_type == constants.TYPE_NVGRE and self._nvgre_enabled:
|
||||
self._nvgre_ops.bind_nvgre_port(
|
||||
segmentation_id, map['vswitch_name'], port_id)
|
||||
|
|
|
@ -34,6 +34,7 @@ from oslo_service import loopingcall
|
|||
from hyperv.common.i18n import _, _LE, _LI # noqa
|
||||
from hyperv.neutron import constants as h_const
|
||||
from hyperv.neutron import hyperv_neutron_agent
|
||||
from hyperv.neutron import trunk_driver
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
@ -151,6 +152,8 @@ class HyperVNeutronAgent(hyperv_neutron_agent.HyperVNeutronAgentMixin):
|
|||
self._report_state)
|
||||
heartbeat.start(interval=report_interval)
|
||||
|
||||
self._vlan_driver = trunk_driver.HyperVTrunkDriver(self.context)
|
||||
|
||||
|
||||
def main():
|
||||
config.register_agent_state_opts_helper(cfg.CONF)
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
# Copyright 2017 Cloudbase Solutions Srl
|
||||
# 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.
|
||||
|
||||
from neutron.api.rpc.callbacks import events
|
||||
from neutron.api.rpc.handlers import resources_rpc
|
||||
from neutron.services.trunk import constants as t_const
|
||||
from neutron.services.trunk.rpc import agent as trunk_rpc
|
||||
from os_win import constants as os_win_const
|
||||
from os_win import utilsfactory
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
|
||||
from hyperv.common.i18n import _LI, _LE # noqa
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HyperVTrunkDriver(trunk_rpc.TrunkSkeleton):
|
||||
"""Driver responsible for handling trunk/subport/port events.
|
||||
|
||||
Receives data model events from the neutron server and uses them to setup
|
||||
VLAN trunks for Hyper-V vSwitch ports.
|
||||
"""
|
||||
|
||||
def __init__(self, context):
|
||||
super(HyperVTrunkDriver, self).__init__()
|
||||
self._context = context
|
||||
self._utils = utilsfactory.get_networkutils()
|
||||
self._trunk_rpc = trunk_rpc.TrunkStub()
|
||||
|
||||
# Map between trunk.id and trunk.
|
||||
self._trunks = {}
|
||||
|
||||
def handle_trunks(self, trunks, event_type):
|
||||
"""Trunk data model change from the server."""
|
||||
|
||||
LOG.debug("Trunks event received: %(event_type)s. Trunks: %(trunks)s",
|
||||
{'event_type': event_type, 'trunks': trunks})
|
||||
|
||||
if event_type == events.DELETED:
|
||||
# The port trunks have been deleted. Remove them from cache.
|
||||
for trunk in trunks:
|
||||
self._trunks.pop(trunk.id, None)
|
||||
else:
|
||||
for trunk in trunks:
|
||||
self._trunks[trunk.id] = trunk
|
||||
self._setup_trunk(trunk)
|
||||
|
||||
def handle_subports(self, subports, event_type):
|
||||
"""Subport data model change from the server."""
|
||||
|
||||
LOG.debug("Subports event received: %(event_type)s. "
|
||||
"Subports: %(subports)s",
|
||||
{'event_type': event_type, 'subports': subports})
|
||||
|
||||
# update the cache.
|
||||
if event_type == events.CREATED:
|
||||
for subport in subports:
|
||||
trunk = self._trunks.get(subport['trunk_id'])
|
||||
if trunk:
|
||||
trunk.sub_ports.append(subport)
|
||||
elif event_type == events.DELETED:
|
||||
for subport in subports:
|
||||
trunk = self._trunks.get(subport['trunk_id'])
|
||||
if trunk and subport in trunk.sub_ports:
|
||||
trunk.sub_ports.remove(subport)
|
||||
|
||||
# update the bound trunks.
|
||||
affected_trunk_ids = set([s['trunk_id'] for s in subports])
|
||||
for trunk_id in affected_trunk_ids:
|
||||
trunk = self._trunks.get(trunk_id)
|
||||
if trunk:
|
||||
self._setup_trunk(trunk)
|
||||
|
||||
def bind_vlan_port(self, port_id, segmentation_id):
|
||||
trunk = self._fetch_trunk(port_id)
|
||||
if not trunk:
|
||||
# No trunk found. No VLAN IDs to set in trunk mode.
|
||||
self._set_port_vlan(port_id, segmentation_id)
|
||||
return
|
||||
|
||||
self._setup_trunk(trunk, segmentation_id)
|
||||
|
||||
def _fetch_trunk(self, port_id, context=None):
|
||||
context = context or self._context
|
||||
try:
|
||||
trunk = self._trunk_rpc.get_trunk_details(context, port_id)
|
||||
LOG.debug("Found trunk for port_id %(port_id)s: %(trunk)s",
|
||||
{'port_id': port_id, 'trunk': trunk})
|
||||
|
||||
# cache it.
|
||||
self._trunks[trunk.id] = trunk
|
||||
return trunk
|
||||
except resources_rpc.ResourceNotFound:
|
||||
return None
|
||||
except oslo_messaging.RemoteError as ex:
|
||||
if 'CallbackNotFound' not in str(ex):
|
||||
raise
|
||||
LOG.debug("Trunk plugin disabled on server. Assuming port %s is "
|
||||
"not a trunk.", port_id)
|
||||
return None
|
||||
|
||||
def _setup_trunk(self, trunk, vlan_id=None):
|
||||
"""Sets up VLAN trunk and updates the trunk status."""
|
||||
|
||||
LOG.info(_LI('Binding trunk port: %s.'), trunk)
|
||||
try:
|
||||
# bind sub_ports to host.
|
||||
self._trunk_rpc.update_subport_bindings(self._context,
|
||||
trunk.sub_ports)
|
||||
|
||||
vlan_trunk = [s.segmentation_id for s in trunk.sub_ports]
|
||||
self._set_port_vlan(trunk.port_id, vlan_id, vlan_trunk)
|
||||
|
||||
self._trunk_rpc.update_trunk_status(self._context, trunk.id,
|
||||
t_const.ACTIVE_STATUS)
|
||||
except Exception:
|
||||
# something broke
|
||||
LOG.exception(_LE("Failure setting up subports for %s"),
|
||||
trunk.port_id)
|
||||
self._trunk_rpc.update_trunk_status(self._context, trunk.id,
|
||||
t_const.DEGRADED_STATUS)
|
||||
|
||||
def _set_port_vlan(self, port_id, vlan_id, vlan_trunk=None):
|
||||
LOG.info(_LI('Binding VLAN ID: %(vlan_id)s, VLAN trunk: '
|
||||
'%(vlan_trunk)s to switch port %(port_id)s'),
|
||||
dict(vlan_id=vlan_id, vlan_trunk=vlan_trunk, port_id=port_id))
|
||||
|
||||
op_mode = (os_win_const.VLAN_MODE_TRUNK if vlan_trunk else
|
||||
os_win_const.VLAN_MODE_ACCESS)
|
||||
self._utils.set_vswitch_port_vlan_id(
|
||||
vlan_id,
|
||||
port_id,
|
||||
operation_mode=op_mode,
|
||||
vlan_trunk=vlan_trunk)
|
|
@ -25,6 +25,7 @@ import traceback
|
|||
import eventlet.timeout
|
||||
import fixtures
|
||||
import mock
|
||||
from os_win import utilsfactory
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import strutils
|
||||
import testtools
|
||||
|
@ -136,3 +137,12 @@ class BaseTestCase(testtools.TestCase):
|
|||
group = kw.pop('group', None)
|
||||
for k, v in kw.items():
|
||||
CONF.set_override(k, v, group)
|
||||
|
||||
|
||||
class HyperVBaseTestCase(BaseTestCase):
|
||||
def setUp(self):
|
||||
super(HyperVBaseTestCase, self).setUp()
|
||||
|
||||
utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class')
|
||||
utilsfactory_patcher.start()
|
||||
self.addCleanup(utilsfactory_patcher.stop)
|
||||
|
|
|
@ -20,7 +20,6 @@ Unit tests for Windows Hyper-V virtual switch neutron driver
|
|||
|
||||
import mock
|
||||
from os_win import exceptions
|
||||
from os_win import utilsfactory
|
||||
from oslo_config import cfg
|
||||
|
||||
from hyperv.neutron import constants
|
||||
|
@ -31,15 +30,12 @@ from hyperv.tests import base
|
|||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestHyperVNeutronAgent(base.BaseTestCase):
|
||||
class TestHyperVNeutronAgent(base.HyperVBaseTestCase):
|
||||
|
||||
_FAKE_PORT_ID = 'fake_port_id'
|
||||
|
||||
def setUp(self):
|
||||
super(TestHyperVNeutronAgent, self).setUp()
|
||||
utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class')
|
||||
utilsfactory_patcher.start()
|
||||
self.addCleanup(utilsfactory_patcher.stop)
|
||||
|
||||
self.agent = hyperv_neutron_agent.HyperVNeutronAgentMixin()
|
||||
self.agent._qos_ext = mock.MagicMock()
|
||||
|
@ -54,6 +50,7 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
|
|||
self.agent.notifier = mock.Mock()
|
||||
self.agent._utils = mock.MagicMock()
|
||||
self.agent._nvgre_ops = mock.MagicMock()
|
||||
self.agent._vlan_driver = mock.MagicMock()
|
||||
|
||||
def test_load_physical_network_mappings(self):
|
||||
test_mappings = ['fakenetwork1:fake_vswitch',
|
||||
|
@ -338,9 +335,7 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
|
|||
|
||||
@mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin,
|
||||
'_provision_network')
|
||||
def test_port_bound_nvgre(self, mock_provision_network):
|
||||
self.agent._nvgre_enabled = True
|
||||
network_type = constants.TYPE_NVGRE
|
||||
def _check_port_bound_net_type(self, mock_provision_network, network_type):
|
||||
net_uuid = 'my-net-uuid'
|
||||
fake_map = {'vswitch_name': mock.sentinel.vswitch_name,
|
||||
'ports': []}
|
||||
|
@ -360,6 +355,17 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
|
|||
mock.sentinel.physical_network, mock.sentinel.segmentation_id)
|
||||
self.agent._utils.connect_vnic_to_vswitch.assert_called_once_with(
|
||||
mock.sentinel.vswitch_name, mock.sentinel.port_id)
|
||||
|
||||
def test_port_bound_vlan(self):
|
||||
self._check_port_bound_net_type(network_type=constants.TYPE_VLAN)
|
||||
|
||||
self.agent._vlan_driver.bind_vlan_port.assert_called_once_with(
|
||||
mock.sentinel.port_id, mock.sentinel.segmentation_id)
|
||||
|
||||
def test_port_bound_nvgre(self):
|
||||
self.agent._nvgre_enabled = True
|
||||
self._check_port_bound_net_type(network_type=constants.TYPE_NVGRE)
|
||||
|
||||
self.agent._nvgre_ops.bind_nvgre_port.assert_called_once_with(
|
||||
mock.sentinel.segmentation_id, mock.sentinel.vswitch_name,
|
||||
mock.sentinel.port_id)
|
||||
|
|
|
@ -119,6 +119,7 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
|
|||
self.agent.context, {'start_flag': True})
|
||||
self.assertTrue(self.agent.agent_state['start_flag'])
|
||||
|
||||
@mock.patch.object(l2_agent.trunk_driver, 'HyperVTrunkDriver')
|
||||
@mock.patch.object(l2_agent.qos_extension, 'QosAgentExtension')
|
||||
@mock.patch.object(l2_agent.loopingcall, 'FixedIntervalLoopingCall')
|
||||
@mock.patch.object(l2_agent.n_rpc, 'get_client')
|
||||
|
@ -128,7 +129,7 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
|
|||
@mock.patch.object(l2_agent, 'CONF')
|
||||
def test_setup_rpc(self, mock_CONF, mock_agent_rpc, mock_SGRpcApi,
|
||||
mock_HyperVSecurityAgent, mock_get_client,
|
||||
mock_LoopingCall, mock_qos_ext):
|
||||
mock_LoopingCall, mock_qos_ext, mock_HyperVTrunkDriver):
|
||||
mock_CONF.NVGRE.enable_support = True
|
||||
mock_CONF.AGENT.report_interval = mock.sentinel.report_interval
|
||||
mock_CONF.AGENT.enable_qos_extension = True
|
||||
|
@ -144,6 +145,8 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
|
|||
self.assertEqual(mock_agent_rpc.create_consumers.return_value,
|
||||
self.agent.connection)
|
||||
self.assertEqual(mock_get_client.return_value, self.agent.client)
|
||||
self.assertEqual(mock_HyperVTrunkDriver.return_value,
|
||||
self.agent._vlan_driver)
|
||||
|
||||
mock_HyperVSecurityAgent.assert_called_once_with(
|
||||
self.agent.context, self.agent.sg_plugin_rpc)
|
||||
|
@ -163,6 +166,7 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
|
|||
self.agent.connection.consume_in_threads.assert_called_once_with()
|
||||
mock_LoopingCall.return_value.start.assert_called_once_with(
|
||||
interval=mock.sentinel.report_interval)
|
||||
mock_HyperVTrunkDriver.assert_called_once_with(self.agent.context)
|
||||
|
||||
|
||||
class TestMain(base.BaseTestCase):
|
||||
|
|
|
@ -18,7 +18,6 @@ Unit tests for Windows Hyper-V NVGRE driver.
|
|||
"""
|
||||
|
||||
import mock
|
||||
from os_win import utilsfactory
|
||||
from oslo_config import cfg
|
||||
|
||||
from hyperv.neutron import constants
|
||||
|
@ -28,7 +27,7 @@ from hyperv.tests import base
|
|||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestHyperVNvgreOps(base.BaseTestCase):
|
||||
class TestHyperVNvgreOps(base.HyperVBaseTestCase):
|
||||
|
||||
FAKE_MAC_ADDR = 'fa:ke:ma:ca:dd:re:ss'
|
||||
FAKE_CIDR = '10.0.0.0/24'
|
||||
|
@ -36,9 +35,6 @@ class TestHyperVNvgreOps(base.BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(TestHyperVNvgreOps, self).setUp()
|
||||
utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class')
|
||||
utilsfactory_patcher.start()
|
||||
self.addCleanup(utilsfactory_patcher.stop)
|
||||
|
||||
self.context = 'context'
|
||||
self.ops = nvgre_ops.HyperVNvgreOps([])
|
||||
|
|
|
@ -19,13 +19,12 @@ Unit tests for the Hyper-V Security Groups Driver.
|
|||
|
||||
import mock
|
||||
from os_win import exceptions
|
||||
from os_win import utilsfactory
|
||||
|
||||
from hyperv.neutron import security_groups_driver as sg_driver
|
||||
from hyperv.tests import base
|
||||
|
||||
|
||||
class SecurityGroupRuleTestHelper(base.BaseTestCase):
|
||||
class SecurityGroupRuleTestHelper(base.HyperVBaseTestCase):
|
||||
_FAKE_DIRECTION = 'egress'
|
||||
_FAKE_ETHERTYPE = 'IPv4'
|
||||
_FAKE_ETHERTYPE_IPV6 = 'IPv6'
|
||||
|
@ -68,9 +67,6 @@ class TestHyperVSecurityGroupsDriver(SecurityGroupRuleTestHelper):
|
|||
|
||||
def setUp(self):
|
||||
super(TestHyperVSecurityGroupsDriver, self).setUp()
|
||||
utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class')
|
||||
utilsfactory_patcher.start()
|
||||
self.addCleanup(utilsfactory_patcher.stop)
|
||||
|
||||
self._driver = sg_driver.HyperVSecurityGroupsDriver()
|
||||
self._driver._utils = mock.MagicMock()
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
# Copyright 2017 Cloudbase Solutions Srl
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Unit tests for the Hyper-V Trunk Driver.
|
||||
"""
|
||||
|
||||
import mock
|
||||
from neutron.api.rpc.callbacks import events
|
||||
from neutron.api.rpc.handlers import resources_rpc
|
||||
from neutron.services.trunk import constants as t_const
|
||||
from os_win import constants as os_win_const
|
||||
import oslo_messaging
|
||||
import testtools
|
||||
|
||||
from hyperv.neutron import trunk_driver
|
||||
from hyperv.tests import base
|
||||
|
||||
|
||||
class TestHyperVTrunkDriver(base.HyperVBaseTestCase):
|
||||
|
||||
@mock.patch.object(trunk_driver.trunk_rpc, 'TrunkStub',
|
||||
lambda *args, **kwargs: None)
|
||||
@mock.patch.object(trunk_driver.trunk_rpc.TrunkSkeleton, '__init__',
|
||||
lambda *args, **kwargs: None)
|
||||
def setUp(self):
|
||||
super(TestHyperVTrunkDriver, self).setUp()
|
||||
|
||||
self.trunk_driver = trunk_driver.HyperVTrunkDriver(
|
||||
mock.sentinel.context)
|
||||
self.trunk_driver._utils = mock.MagicMock()
|
||||
self.trunk_driver._trunk_rpc = mock.MagicMock()
|
||||
|
||||
def test_handle_trunks_deleted(self):
|
||||
mock_trunk = mock.MagicMock()
|
||||
self.trunk_driver._trunks[mock_trunk.id] = mock_trunk
|
||||
|
||||
self.trunk_driver.handle_trunks([mock_trunk], events.DELETED)
|
||||
self.assertNotIn(mock_trunk.id, self.trunk_driver._trunks)
|
||||
|
||||
@mock.patch.object(trunk_driver.HyperVTrunkDriver, '_setup_trunk')
|
||||
def test_handle_trunks_created(self, mock_setup_trunk):
|
||||
sub_ports = []
|
||||
mock_trunk = mock.MagicMock(sub_ports=sub_ports)
|
||||
|
||||
self.trunk_driver.handle_trunks([mock_trunk], events.CREATED)
|
||||
|
||||
self.assertEqual(mock_trunk, self.trunk_driver._trunks[mock_trunk.id])
|
||||
mock_setup_trunk.assert_called_once_with(mock_trunk)
|
||||
|
||||
@mock.patch.object(trunk_driver.HyperVTrunkDriver, '_set_port_vlan')
|
||||
@mock.patch.object(trunk_driver.HyperVTrunkDriver, '_fetch_trunk')
|
||||
def test_bind_vlan_port_not_trunk(self, mock_fetch_trunk, mock_set_vlan):
|
||||
mock_fetch_trunk.return_value = None
|
||||
|
||||
self.trunk_driver.bind_vlan_port(mock.sentinel.port_id,
|
||||
mock.sentinel.segmentation_id)
|
||||
|
||||
mock_fetch_trunk.assert_called_once_with(mock.sentinel.port_id)
|
||||
mock_set_vlan.assert_called_once_with(mock.sentinel.port_id,
|
||||
mock.sentinel.segmentation_id)
|
||||
|
||||
@mock.patch.object(trunk_driver.HyperVTrunkDriver, '_setup_trunk')
|
||||
@mock.patch.object(trunk_driver.HyperVTrunkDriver, '_fetch_trunk')
|
||||
def test_bind_vlan_port(self, mock_fetch_trunk, mock_setup_trunk):
|
||||
self.trunk_driver.bind_vlan_port(mock.sentinel.port_id,
|
||||
mock.sentinel.segmentation_id)
|
||||
|
||||
mock_fetch_trunk.assert_called_once_with(mock.sentinel.port_id)
|
||||
mock_setup_trunk.assert_called_once_with(mock_fetch_trunk.return_value,
|
||||
mock.sentinel.segmentation_id)
|
||||
|
||||
def test_fetch_trunk(self):
|
||||
mock_trunk = (
|
||||
self.trunk_driver._trunk_rpc.get_trunk_details.return_value)
|
||||
|
||||
trunk = self.trunk_driver._fetch_trunk(mock.sentinel.port_id,
|
||||
mock.sentinel.context)
|
||||
|
||||
self.assertEqual(mock_trunk, trunk)
|
||||
self.assertEqual(mock_trunk, self.trunk_driver._trunks[mock_trunk.id])
|
||||
self.trunk_driver._trunk_rpc.get_trunk_details.assert_called_once_with(
|
||||
mock.sentinel.context, mock.sentinel.port_id)
|
||||
|
||||
def test_fetch_trunk_resource_not_found(self):
|
||||
self.trunk_driver._trunk_rpc.get_trunk_details.side_effect = (
|
||||
resources_rpc.ResourceNotFound)
|
||||
|
||||
trunk = self.trunk_driver._fetch_trunk(mock.sentinel.port_id)
|
||||
self.assertIsNone(trunk)
|
||||
|
||||
def test_fetch_trunk_resource_remote_error(self):
|
||||
self.trunk_driver._trunk_rpc.get_trunk_details.side_effect = (
|
||||
oslo_messaging.RemoteError('expected CallbackNotFound'))
|
||||
|
||||
trunk = self.trunk_driver._fetch_trunk(mock.sentinel.port_id)
|
||||
self.assertIsNone(trunk)
|
||||
|
||||
def test_fetch_trunk_resource_remote_error_reraised(self):
|
||||
self.trunk_driver._trunk_rpc.get_trunk_details.side_effect = (
|
||||
oslo_messaging.RemoteError)
|
||||
|
||||
self.assertRaises(oslo_messaging.RemoteError,
|
||||
self.trunk_driver._fetch_trunk,
|
||||
mock.sentinel.port_id)
|
||||
|
||||
@mock.patch.object(trunk_driver.HyperVTrunkDriver, '_set_port_vlan')
|
||||
def test_setup_trunk(self, mock_set_vlan):
|
||||
mock_subport = mock.MagicMock()
|
||||
mock_trunk = mock.MagicMock(sub_ports=[mock_subport])
|
||||
trunk_rpc = self.trunk_driver._trunk_rpc
|
||||
trunk_rpc.update_trunk_status.side_effect = [
|
||||
testtools.ExpectedException, None]
|
||||
|
||||
self.trunk_driver._setup_trunk(mock_trunk, mock.sentinel.vlan_id)
|
||||
|
||||
trunk_rpc.update_subport_bindings.assert_called_once_with(
|
||||
self.trunk_driver._context, [mock_subport])
|
||||
mock_set_vlan.assert_called_once_with(
|
||||
mock_trunk.port_id, mock.sentinel.vlan_id,
|
||||
[mock_subport.segmentation_id])
|
||||
mock_set_vlan.has_calls([
|
||||
mock.call(self.trunk_driver._context, mock_trunk.id, status)
|
||||
for status in [t_const.ACTIVE_STATUS, t_const.DEGRADED_STATUS]])
|
||||
|
||||
def _check_set_port_vlan(self, vlan_trunk, operation_mode):
|
||||
self.trunk_driver._set_port_vlan(mock.sentinel.port_id,
|
||||
mock.sentinel.vlan_id,
|
||||
vlan_trunk)
|
||||
|
||||
self.trunk_driver._utils.set_vswitch_port_vlan_id(
|
||||
mock.sentinel.vlan_id, mock.sentinel.port_id,
|
||||
operation_mode=operation_mode,
|
||||
vlan_trunk=vlan_trunk)
|
||||
|
||||
def test_set_port_vlan_trunk_mode(self):
|
||||
self._check_set_port_vlan(mock.sentinel.vlan_trunk,
|
||||
os_win_const.VLAN_MODE_TRUNK)
|
||||
|
||||
def test_set_port_vlan_access_mode(self):
|
||||
self._check_set_port_vlan(None, os_win_const.VLAN_MODE_ACCESS)
|
Loading…
Reference in New Issue