Merge "NSXP: Trunk support"
This commit is contained in:
commit
d2a0caeb60
@ -360,6 +360,15 @@ Add octavia and python-octaviaclient repos as external repositories and configur
|
||||
[controller_worker]
|
||||
network_driver = allowed_address_pairs_driver
|
||||
|
||||
Trunk Driver
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Enable trunk service and configure following flags in ``local.conf``::
|
||||
|
||||
[[local]|[localrc]]
|
||||
# Trunk plugin NSX-P driver config
|
||||
ENABLED_SERVICES+=,q-trunk
|
||||
Q_SERVICE_PLUGIN_CLASSES+=,trunk
|
||||
|
||||
Neutron VPNaaS
|
||||
~~~~~~~~~~~~~~
|
||||
|
@ -92,6 +92,7 @@ from vmware_nsx.services.lbaas.octavia import octavia_listener
|
||||
from vmware_nsx.services.qos.common import utils as qos_com_utils
|
||||
from vmware_nsx.services.qos.nsx_v3 import driver as qos_driver
|
||||
from vmware_nsx.services.qos.nsx_v3 import pol_utils as qos_utils
|
||||
from vmware_nsx.services.trunk.nsx_p import driver as trunk_driver
|
||||
|
||||
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
|
||||
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
|
||||
@ -233,6 +234,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
# Init QoS
|
||||
qos_driver.register(qos_utils.PolicyQosNotificationsHandler())
|
||||
|
||||
# Register NSXP trunk driver to support trunk extensions
|
||||
self.trunk_driver = trunk_driver.NsxpTrunkDriver.create(self)
|
||||
|
||||
registry.subscribe(self.spawn_complete,
|
||||
resources.PROCESS,
|
||||
events.AFTER_SPAWN)
|
||||
|
0
vmware_nsx/services/trunk/nsx_p/__init__.py
Normal file
0
vmware_nsx/services/trunk/nsx_p/__init__.py
Normal file
241
vmware_nsx/services/trunk/nsx_p/driver.py
Normal file
241
vmware_nsx/services/trunk/nsx_p/driver.py
Normal file
@ -0,0 +1,241 @@
|
||||
# Copyright 2019 VMware, Inc.
|
||||
# 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 oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from neutron.services.trunk.drivers import base
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources
|
||||
from neutron_lib.services.trunk import constants as trunk_consts
|
||||
|
||||
from vmware_nsx._i18n import _
|
||||
from vmware_nsx.common import exceptions as nsx_exc
|
||||
from vmware_nsx.common import utils as nsx_utils
|
||||
from vmware_nsx.extensions import projectpluginmap
|
||||
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
|
||||
from vmware_nsxlib.v3.policy import constants as p_constants
|
||||
from vmware_nsxlib.v3 import utils as nsxlib_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
SUPPORTED_INTERFACES = (
|
||||
portbindings.VIF_TYPE_OVS,
|
||||
)
|
||||
SUPPORTED_SEGMENTATION_TYPES = (
|
||||
trunk_consts.SEGMENTATION_TYPE_VLAN,
|
||||
)
|
||||
|
||||
DRIVER_NAME = 'vmware_nsxp_trunk'
|
||||
TRUNK_ID_TAG_NAME = 'os-neutron-trunk-id'
|
||||
|
||||
|
||||
class NsxpTrunkHandler(object):
|
||||
def __init__(self, plugin_driver):
|
||||
self.plugin_driver = plugin_driver
|
||||
|
||||
def _get_port_tags_and_network(self, context, port_id):
|
||||
port = self.plugin_driver.get_port(context, port_id)
|
||||
segment_id = self.plugin_driver._get_network_nsx_segment_id(
|
||||
context, port['network_id'])
|
||||
lport = self.plugin_driver.nsxpolicy.segment_port.get(
|
||||
segment_id, port_id)
|
||||
|
||||
return segment_id, lport.get('tags', [])
|
||||
|
||||
def _update_tags(self, port_id, tags, tags_update, is_delete=False):
|
||||
if is_delete:
|
||||
tags = [tag for tag in tags if tag not in tags_update]
|
||||
else:
|
||||
for tag in tags:
|
||||
for tag_u in tags_update:
|
||||
if tag_u['scope'] == tag['scope']:
|
||||
tag['tag'] = tag_u['tag']
|
||||
tags_update.remove(tag_u)
|
||||
break
|
||||
|
||||
tags.extend(
|
||||
[tag for tag in tags_update if tag not in tags])
|
||||
if len(tags) > nsxlib_utils.MAX_TAGS:
|
||||
LOG.warning("Cannot add external tags to port %s: "
|
||||
"too many tags", port_id)
|
||||
return tags
|
||||
|
||||
def _set_subports(self, context, parent_port_id, subports):
|
||||
for subport in subports:
|
||||
# Update port with parent port for backend.
|
||||
|
||||
# Set properties for VLAN trunking
|
||||
if subport.segmentation_type == nsx_utils.NsxV3NetworkTypes.VLAN:
|
||||
seg_id = subport.segmentation_id
|
||||
else:
|
||||
msg = (_("Cannot create a subport %s with no segmentation"
|
||||
" id") % subport.port_id)
|
||||
LOG.error(msg)
|
||||
raise nsx_exc.NsxPluginException(err_msg=msg)
|
||||
|
||||
tags_update = [{'scope': TRUNK_ID_TAG_NAME,
|
||||
'tag': subport.trunk_id}]
|
||||
|
||||
segment_id, tags = self._get_port_tags_and_network(
|
||||
context, subport.port_id)
|
||||
|
||||
tags = self._update_tags(
|
||||
subport.port_id, tags, tags_update, is_delete=False)
|
||||
|
||||
# Update logical port in the backend to set/unset parent port
|
||||
try:
|
||||
self.plugin_driver.nsxpolicy.segment_port.attach(
|
||||
segment_id,
|
||||
subport.port_id,
|
||||
p_constants.ATTACHMENT_CHILD,
|
||||
subport.port_id,
|
||||
context_id=parent_port_id,
|
||||
traffic_tag=seg_id,
|
||||
tags=tags)
|
||||
|
||||
except nsxlib_exc.ManagerError as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to update subport for attachment "
|
||||
"type. Exception is %s", e)
|
||||
|
||||
def _unset_subports(self, context, subports):
|
||||
for subport in subports:
|
||||
# Update port and remove parent port attachment in the backend
|
||||
# Unset the parent port properties from child port
|
||||
|
||||
tags_update = [{'scope': TRUNK_ID_TAG_NAME,
|
||||
'tag': subport.trunk_id}]
|
||||
|
||||
segment_id, tags = self._get_port_tags_and_network(
|
||||
context, subport.port_id)
|
||||
|
||||
tags = self._update_tags(
|
||||
subport.port_id, tags, tags_update, is_delete=True)
|
||||
|
||||
# Update logical port in the backend to set/unset parent port
|
||||
try:
|
||||
self.plugin_driver.nsxpolicy.segment_port.detach(
|
||||
segment_id, subport.port_id, tags=tags)
|
||||
|
||||
except nsxlib_exc.ManagerError as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to update subport for attachment "
|
||||
"type. Exception is %s", e)
|
||||
|
||||
def trunk_created(self, context, trunk):
|
||||
tags_update = [{'scope': TRUNK_ID_TAG_NAME, 'tag': trunk.id}]
|
||||
segment_id, tags = self._get_port_tags_and_network(
|
||||
context, trunk.port_id)
|
||||
|
||||
tags = self._update_tags(
|
||||
trunk.port_id, tags, tags_update, is_delete=False)
|
||||
|
||||
try:
|
||||
self.plugin_driver.nsxpolicy.segment_port.attach(
|
||||
segment_id,
|
||||
trunk.port_id,
|
||||
vif_id=trunk.port_id,
|
||||
attachment_type=p_constants.ATTACHMENT_PARENT,
|
||||
tags=tags)
|
||||
except Exception as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Parent port attachment for trunk %(trunk)s failed "
|
||||
"with error %(e)s", {'trunk': trunk.id, 'e': e})
|
||||
|
||||
if trunk.sub_ports:
|
||||
self.subports_added(context, trunk, trunk.sub_ports)
|
||||
|
||||
def trunk_deleted(self, context, trunk):
|
||||
tags_update = [{'scope': TRUNK_ID_TAG_NAME, 'tag': trunk.id}]
|
||||
|
||||
segment_id, tags = self._get_port_tags_and_network(
|
||||
context, trunk.port_id)
|
||||
|
||||
tags = self._update_tags(
|
||||
trunk.port_id, tags, tags_update, is_delete=True)
|
||||
|
||||
try:
|
||||
self.plugin_driver.nsxpolicy.segment_port.detach(
|
||||
segment_id, trunk.port_id, tags=tags)
|
||||
except Exception as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Parent port detachment for trunk %(trunk)s failed "
|
||||
"with error %(e)s", {'trunk': trunk.id, 'e': e})
|
||||
|
||||
self.subports_deleted(context, trunk, trunk.sub_ports)
|
||||
|
||||
def subports_added(self, context, trunk, subports):
|
||||
try:
|
||||
self._set_subports(context, trunk.port_id, subports)
|
||||
trunk.update(status=trunk_consts.TRUNK_ACTIVE_STATUS)
|
||||
except (nsxlib_exc.ManagerError, nsxlib_exc.ResourceNotFound):
|
||||
trunk.update(status=trunk_consts.TRUNK_ERROR_STATUS)
|
||||
|
||||
def subports_deleted(self, context, trunk, subports):
|
||||
try:
|
||||
self._unset_subports(context, subports)
|
||||
except (nsxlib_exc.ManagerError, nsxlib_exc.ResourceNotFound):
|
||||
trunk.update(status=trunk_consts.TRUNK_ERROR_STATUS)
|
||||
|
||||
def trunk_event(self, resource, event, trunk_plugin, payload):
|
||||
if event == events.AFTER_CREATE:
|
||||
self.trunk_created(payload.context, payload.current_trunk)
|
||||
elif event == events.AFTER_DELETE:
|
||||
self.trunk_deleted(payload.context, payload.original_trunk)
|
||||
|
||||
def subport_event(self, resource, event, trunk_plugin, payload):
|
||||
if event == events.AFTER_CREATE:
|
||||
self.subports_added(
|
||||
payload.context, payload.original_trunk, payload.subports)
|
||||
elif event == events.AFTER_DELETE:
|
||||
self.subports_deleted(
|
||||
payload.context, payload.original_trunk, payload.subports)
|
||||
|
||||
|
||||
class NsxpTrunkDriver(base.DriverBase):
|
||||
"""Driver to implement neutron's trunk extensions."""
|
||||
|
||||
@property
|
||||
def is_loaded(self):
|
||||
try:
|
||||
plugin_type = self.plugin_driver.plugin_type()
|
||||
return plugin_type == projectpluginmap.NsxPlugins.NSX_P
|
||||
except cfg.NoSuchOptError:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def create(cls, plugin_driver):
|
||||
cls.plugin_driver = plugin_driver
|
||||
return cls(DRIVER_NAME, SUPPORTED_INTERFACES,
|
||||
SUPPORTED_SEGMENTATION_TYPES,
|
||||
agent_type=None, can_trunk_bound_port=True)
|
||||
|
||||
@registry.receives(resources.TRUNK_PLUGIN, [events.AFTER_INIT])
|
||||
def register(self, resource, event, trigger, payload=None):
|
||||
super(NsxpTrunkDriver, self).register(
|
||||
resource, event, trigger, payload=payload)
|
||||
self._handler = NsxpTrunkHandler(self.plugin_driver)
|
||||
for event in (events.AFTER_CREATE, events.AFTER_DELETE):
|
||||
registry.subscribe(self._handler.trunk_event,
|
||||
resources.TRUNK,
|
||||
event)
|
||||
registry.subscribe(self._handler.subport_event,
|
||||
resources.SUBPORTS,
|
||||
event)
|
||||
LOG.debug("VMware NSXP trunk driver initialized.")
|
262
vmware_nsx/tests/unit/services/trunk/test_nsxp_driver.py
Normal file
262
vmware_nsx/tests/unit/services/trunk/test_nsxp_driver.py
Normal file
@ -0,0 +1,262 @@
|
||||
# Copyright (c) 2016 VMware, Inc.
|
||||
#
|
||||
# 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
|
||||
|
||||
from neutron.tests import base
|
||||
|
||||
from neutron_lib import context
|
||||
from oslo_utils import importutils
|
||||
|
||||
from vmware_nsx.extensions import projectpluginmap
|
||||
from vmware_nsx.services.trunk.nsx_p import driver as trunk_driver
|
||||
from vmware_nsx.tests.unit.nsx_p import test_plugin as test_nsx_p_plugin
|
||||
|
||||
PLUGIN_NAME = 'vmware_nsx.plugins.nsx_p.plugin.NsxPolicyPlugin'
|
||||
|
||||
|
||||
class TestNsxpTrunkHandler(test_nsx_p_plugin.NsxPPluginTestCaseMixin,
|
||||
base.BaseTestCase):
|
||||
|
||||
def _get_port_tags_and_network(self, context, port_id):
|
||||
return 'net_' + port_id[-1:], []
|
||||
|
||||
def setUp(self):
|
||||
super(TestNsxpTrunkHandler, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self.core_plugin = importutils.import_object(PLUGIN_NAME)
|
||||
self.handler = trunk_driver.NsxpTrunkHandler(self.core_plugin)
|
||||
self.handler._get_port_tags_and_network = mock.Mock(
|
||||
side_effect=self._get_port_tags_and_network)
|
||||
self.trunk_1 = mock.Mock()
|
||||
self.trunk_1.port_id = "parent_port_1"
|
||||
self.trunk_1.id = "trunk_1_id"
|
||||
|
||||
self.trunk_2 = mock.Mock()
|
||||
self.trunk_2.port_id = "parent_port_2"
|
||||
|
||||
self.sub_port_a = mock.Mock()
|
||||
self.sub_port_a.segmentation_id = 40
|
||||
self.sub_port_a.trunk_id = "trunk-1"
|
||||
self.sub_port_a.port_id = "sub_port_a"
|
||||
self.sub_port_a.segmentation_type = 'vlan'
|
||||
|
||||
self.sub_port_b = mock.Mock()
|
||||
self.sub_port_b.segmentation_id = 41
|
||||
self.sub_port_b.trunk_id = "trunk-2"
|
||||
self.sub_port_b.port_id = "sub_port_b"
|
||||
self.sub_port_b.segmentation_type = 'vlan'
|
||||
|
||||
self.sub_port_c = mock.Mock()
|
||||
self.sub_port_c.segmentation_id = 43
|
||||
self.sub_port_c.trunk_id = "trunk-2"
|
||||
self.sub_port_c.port_id = "sub_port_c"
|
||||
self.sub_port_c.segmentation_type = 'vlan'
|
||||
|
||||
def test_trunk_created(self):
|
||||
# Create trunk with no subport
|
||||
self.trunk_1.sub_ports = []
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'attach') as m_attach:
|
||||
self.handler.trunk_created(self.context, self.trunk_1)
|
||||
m_attach.assert_called_with(
|
||||
'net_1', self.trunk_1.port_id, attachment_type='PARENT',
|
||||
tags=[{'tag': self.trunk_1.id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
vif_id=self.trunk_1.port_id)
|
||||
|
||||
# Create trunk with 1 subport
|
||||
self.trunk_1.sub_ports = [self.sub_port_a]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'attach') as m_attach:
|
||||
self.handler.trunk_created(self.context, self.trunk_1)
|
||||
calls = [
|
||||
mock.call.m_attach(
|
||||
'net_1', self.trunk_1.port_id, attachment_type='PARENT',
|
||||
tags=[{'tag': self.trunk_1.id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
vif_id=self.trunk_1.port_id),
|
||||
mock.call.m_attach(
|
||||
'net_a', self.sub_port_a.port_id, 'CHILD',
|
||||
self.sub_port_a.port_id,
|
||||
context_id=self.trunk_1.port_id,
|
||||
tags=[{'tag': self.sub_port_a.trunk_id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
traffic_tag=self.sub_port_a.segmentation_id)]
|
||||
m_attach.assert_has_calls(calls, any_order=True)
|
||||
|
||||
# Create trunk with multiple subports
|
||||
self.trunk_2.sub_ports = [self.sub_port_b, self.sub_port_c]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'attach') as m_attach:
|
||||
self.handler.trunk_created(self.context, self.trunk_2)
|
||||
calls = [
|
||||
mock.call.m_attach(
|
||||
'net_2', self.trunk_2.port_id, attachment_type='PARENT',
|
||||
tags=[{'tag': self.trunk_2.id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
vif_id=self.trunk_2.port_id),
|
||||
mock.call.m_attach(
|
||||
'net_b', self.sub_port_b.port_id, 'CHILD',
|
||||
self.sub_port_b.port_id,
|
||||
context_id=self.trunk_2.port_id,
|
||||
tags=[{'tag': self.sub_port_b.trunk_id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
traffic_tag=self.sub_port_b.segmentation_id),
|
||||
mock.call.m_attach(
|
||||
'net_c', self.sub_port_c.port_id, 'CHILD',
|
||||
self.sub_port_c.port_id,
|
||||
context_id=self.trunk_2.port_id,
|
||||
tags=[{'tag': self.sub_port_c.trunk_id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
traffic_tag=self.sub_port_c.segmentation_id)]
|
||||
m_attach.assert_has_calls(calls, any_order=True)
|
||||
|
||||
def test_trunk_deleted(self):
|
||||
# Delete trunk with no subport
|
||||
self.trunk_1.sub_ports = []
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'detach') as m_detach:
|
||||
self.handler.trunk_deleted(self.context, self.trunk_1)
|
||||
m_detach.assert_called_with(
|
||||
'net_1', self.trunk_1.port_id, tags=mock.ANY)
|
||||
|
||||
# Delete trunk with 1 subport
|
||||
self.trunk_1.sub_ports = [self.sub_port_a]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'detach') as m_detach:
|
||||
self.handler.trunk_deleted(self.context, self.trunk_1)
|
||||
calls = [
|
||||
mock.call.m_detach(
|
||||
'net_1', self.trunk_1.port_id, tags=mock.ANY),
|
||||
mock.call.m_detach(
|
||||
'net_a', self.sub_port_a.port_id, tags=mock.ANY)]
|
||||
m_detach.assert_has_calls(calls, any_order=True)
|
||||
|
||||
# Delete trunk with multiple subports
|
||||
self.trunk_2.sub_ports = [self.sub_port_b, self.sub_port_c]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'detach') as m_detach:
|
||||
self.handler.trunk_deleted(self.context, self.trunk_2)
|
||||
calls = [
|
||||
mock.call.m_detach(
|
||||
'net_2', self.trunk_2.port_id, tags=mock.ANY),
|
||||
mock.call.m_detach(
|
||||
'net_b', self.sub_port_b.port_id, tags=mock.ANY),
|
||||
mock.call.m_detach(
|
||||
'net_c', self.sub_port_c.port_id, tags=mock.ANY)]
|
||||
m_detach.assert_has_calls(calls, any_order=True)
|
||||
|
||||
def test_subports_added(self):
|
||||
# Update trunk with no subport
|
||||
sub_ports = []
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'attach') as m_attach:
|
||||
self.handler.subports_added(self.context, self.trunk_1, sub_ports)
|
||||
m_attach.assert_not_called()
|
||||
|
||||
# Update trunk with 1 subport
|
||||
sub_ports = [self.sub_port_a]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'attach') as m_attach:
|
||||
self.handler.subports_added(self.context, self.trunk_1, sub_ports)
|
||||
m_attach.assert_called_with(
|
||||
'net_a', self.sub_port_a.port_id, 'CHILD',
|
||||
self.sub_port_a.port_id,
|
||||
context_id=self.trunk_1.port_id,
|
||||
tags=[{'tag': self.sub_port_a.trunk_id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
traffic_tag=self.sub_port_a.segmentation_id)
|
||||
|
||||
# Update trunk with multiple subports
|
||||
sub_ports = [self.sub_port_b, self.sub_port_c]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'attach') as m_attach:
|
||||
self.handler.subports_added(self.context, self.trunk_2, sub_ports)
|
||||
calls = [
|
||||
mock.call.m_attach(
|
||||
'net_b', self.sub_port_b.port_id, 'CHILD',
|
||||
self.sub_port_b.port_id,
|
||||
context_id=self.trunk_2.port_id,
|
||||
tags=[{'tag': self.sub_port_b.trunk_id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
traffic_tag=self.sub_port_b.segmentation_id),
|
||||
mock.call.m_attach(
|
||||
'net_c', self.sub_port_c.port_id, 'CHILD',
|
||||
self.sub_port_c.port_id,
|
||||
context_id=self.trunk_2.port_id,
|
||||
tags=[{'tag': self.sub_port_c.trunk_id,
|
||||
'scope': 'os-neutron-trunk-id'}],
|
||||
traffic_tag=self.sub_port_c.segmentation_id)]
|
||||
m_attach.assert_has_calls(calls, any_order=True)
|
||||
|
||||
def test_subports_deleted(self):
|
||||
# Update trunk to remove no subport
|
||||
sub_ports = []
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'detach') as m_detach:
|
||||
self.handler.subports_deleted(
|
||||
self.context, self.trunk_1, sub_ports)
|
||||
m_detach.assert_not_called()
|
||||
|
||||
# Update trunk to remove 1 subport
|
||||
sub_ports = [self.sub_port_a]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'detach') as m_detach:
|
||||
self.handler.subports_deleted(
|
||||
self.context, self.trunk_1, sub_ports)
|
||||
m_detach.assert_called_with(
|
||||
'net_a', self.sub_port_a.port_id, tags=mock.ANY)
|
||||
|
||||
# Update trunk to remove multiple subports
|
||||
sub_ports = [self.sub_port_b, self.sub_port_c]
|
||||
with mock.patch.object(
|
||||
self.handler.plugin_driver.nsxpolicy.segment_port,
|
||||
'detach') as m_detach:
|
||||
self.handler.subports_deleted(
|
||||
self.context, self.trunk_2, sub_ports)
|
||||
calls = [
|
||||
mock.call.m_detach(
|
||||
'net_b', self.sub_port_b.port_id, tags=mock.ANY),
|
||||
mock.call.m_detach(
|
||||
'net_c', self.sub_port_c.port_id, tags=mock.ANY)]
|
||||
m_detach.assert_has_calls(calls, any_order=True)
|
||||
|
||||
|
||||
class TestNsxpTrunkDriver(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestNsxpTrunkDriver, self).setUp()
|
||||
|
||||
def test_is_loaded(self):
|
||||
core_plugin = mock.Mock()
|
||||
driver = trunk_driver.NsxpTrunkDriver.create(core_plugin)
|
||||
with mock.patch.object(core_plugin, 'plugin_type',
|
||||
return_value=projectpluginmap.NsxPlugins.NSX_P):
|
||||
self.assertTrue(driver.is_loaded)
|
||||
|
||||
with mock.patch.object(core_plugin, 'plugin_type',
|
||||
return_value=projectpluginmap.NsxPlugins.NSX_T):
|
||||
self.assertFalse(driver.is_loaded)
|
Loading…
Reference in New Issue
Block a user