Change tenant network type usage for IB Fabric
This patch changes tenant network type usage for InfiniBand Fabric to vlan type. Add the indication of Fabric Type (Ethernet/InfiniBand) to the provider_network via the plugin configuration file. If physical network type is not specified for some provider network listed in the network_vlan_ranges, use default physical network type. Co-authored-by: Roey Chen <roeyc@mellanox.com> Change-Id: Id45acfb8234359a43303c2eee2205a44998c039a Closes-Bug: 1263638
This commit is contained in:
parent
4f6c33439b
commit
989246779d
@ -18,6 +18,21 @@
|
|||||||
# network_vlan_ranges =
|
# network_vlan_ranges =
|
||||||
# Example: network_vlan_ranges = default:1:100
|
# Example: network_vlan_ranges = default:1:100
|
||||||
|
|
||||||
|
# (ListOpt) Comma-separated list of
|
||||||
|
# <physical_network>:<physical_network_type> tuples mapping physical
|
||||||
|
# network names to physical network types. All physical
|
||||||
|
# networks listed in network_vlan_ranges should have
|
||||||
|
# mappings to appropriate physical network type.
|
||||||
|
# Type of the physical network can be either eth (Ethernet) or
|
||||||
|
# ib (InfiniBand). If empty, physical network eth type is assumed.
|
||||||
|
#
|
||||||
|
# physical_network_type_mappings =
|
||||||
|
# Example: physical_network_type_mappings = default:eth
|
||||||
|
|
||||||
|
# (StrOpt) Type of the physical network, can be either 'eth' or 'ib'
|
||||||
|
# The default value is 'eth'
|
||||||
|
# physical_network_type = eth
|
||||||
|
|
||||||
[eswitch]
|
[eswitch]
|
||||||
# (ListOpt) Comma-separated list of
|
# (ListOpt) Comma-separated list of
|
||||||
# <physical_network>:<physical_interface> tuples mapping physical
|
# <physical_network>:<physical_interface> tuples mapping physical
|
||||||
|
@ -37,7 +37,6 @@ from neutron.openstack.common.rpc import dispatcher
|
|||||||
from neutron.plugins.common import constants as p_const
|
from neutron.plugins.common import constants as p_const
|
||||||
from neutron.plugins.mlnx.agent import utils
|
from neutron.plugins.mlnx.agent import utils
|
||||||
from neutron.plugins.mlnx.common import config # noqa
|
from neutron.plugins.mlnx.common import config # noqa
|
||||||
from neutron.plugins.mlnx.common import constants
|
|
||||||
from neutron.plugins.mlnx.common import exceptions
|
from neutron.plugins.mlnx.common import exceptions
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -103,8 +102,7 @@ class EswitchManager(object):
|
|||||||
net_map = self.network_map[network_id]
|
net_map = self.network_map[network_id]
|
||||||
net_map['ports'].append({'port_id': port_id, 'port_mac': port_mac})
|
net_map['ports'].append({'port_id': port_id, 'port_mac': port_mac})
|
||||||
|
|
||||||
if network_type in (p_const.TYPE_VLAN,
|
if network_type == p_const.TYPE_VLAN:
|
||||||
constants.TYPE_IB):
|
|
||||||
LOG.info(_('Binding Segmentation ID %(seg_id)s'
|
LOG.info(_('Binding Segmentation ID %(seg_id)s'
|
||||||
'to eSwitch for vNIC mac_address %(mac)s'),
|
'to eSwitch for vNIC mac_address %(mac)s'),
|
||||||
{'seg_id': seg_id,
|
{'seg_id': seg_id,
|
||||||
@ -132,8 +130,6 @@ class EswitchManager(object):
|
|||||||
LOG.info(_("Provisioning network %s"), network_id)
|
LOG.info(_("Provisioning network %s"), network_id)
|
||||||
if network_type == p_const.TYPE_VLAN:
|
if network_type == p_const.TYPE_VLAN:
|
||||||
LOG.debug(_("Creating VLAN Network"))
|
LOG.debug(_("Creating VLAN Network"))
|
||||||
elif network_type == constants.TYPE_IB:
|
|
||||||
LOG.debug(_("Creating IB Network"))
|
|
||||||
else:
|
else:
|
||||||
LOG.error(_("Unknown network type %(network_type)s "
|
LOG.error(_("Unknown network type %(network_type)s "
|
||||||
"for network %(network_id)s"),
|
"for network %(network_id)s"),
|
||||||
|
@ -26,11 +26,18 @@ DEFAULT_INTERFACE_MAPPINGS = []
|
|||||||
vlan_opts = [
|
vlan_opts = [
|
||||||
cfg.StrOpt('tenant_network_type', default='vlan',
|
cfg.StrOpt('tenant_network_type', default='vlan',
|
||||||
help=_("Network type for tenant networks "
|
help=_("Network type for tenant networks "
|
||||||
"(local, ib, vlan, or none)")),
|
"(local, vlan, or none)")),
|
||||||
cfg.ListOpt('network_vlan_ranges',
|
cfg.ListOpt('network_vlan_ranges',
|
||||||
default=DEFAULT_VLAN_RANGES,
|
default=DEFAULT_VLAN_RANGES,
|
||||||
help=_("List of <physical_network>:<vlan_min>:<vlan_max> "
|
help=_("List of <physical_network>:<vlan_min>:<vlan_max> "
|
||||||
"or <physical_network>")),
|
"or <physical_network>")),
|
||||||
|
cfg.ListOpt('physical_network_type_mappings',
|
||||||
|
default=[],
|
||||||
|
help=_("List of <physical_network>:<physical_network_type> "
|
||||||
|
" with physical_network_type is either eth or ib")),
|
||||||
|
cfg.StrOpt('physical_network_type', default='eth',
|
||||||
|
help=_("Physical network type for provider network "
|
||||||
|
"(eth or ib)"))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,8 +18,9 @@
|
|||||||
LOCAL_VLAN_ID = -2
|
LOCAL_VLAN_ID = -2
|
||||||
FLAT_VLAN_ID = -1
|
FLAT_VLAN_ID = -1
|
||||||
|
|
||||||
# Values for network_type
|
# Values for physical network_type
|
||||||
TYPE_IB = 'ib'
|
TYPE_IB = 'ib'
|
||||||
|
TYPE_ETH = 'eth'
|
||||||
|
|
||||||
VIF_TYPE_DIRECT = 'mlnx_direct'
|
VIF_TYPE_DIRECT = 'mlnx_direct'
|
||||||
VIF_TYPE_HOSTDEV = 'hostdev'
|
VIF_TYPE_HOSTDEV = 'hostdev'
|
||||||
|
@ -96,7 +96,7 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Start Mellanox Neutron Plugin."""
|
"""Start Mellanox Neutron Plugin."""
|
||||||
super(MellanoxEswitchPlugin, self).__init__()
|
super(MellanoxEswitchPlugin, self).__init__()
|
||||||
self._parse_network_vlan_ranges()
|
self._parse_network_config()
|
||||||
db.sync_network_states(self.network_vlan_ranges)
|
db.sync_network_states(self.network_vlan_ranges)
|
||||||
self._set_tenant_network_type()
|
self._set_tenant_network_type()
|
||||||
self.vnic_type = cfg.CONF.ESWITCH.vnic_type
|
self.vnic_type = cfg.CONF.ESWITCH.vnic_type
|
||||||
@ -133,6 +133,41 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
l3_rpc_agent_api.L3AgentNotify
|
l3_rpc_agent_api.L3AgentNotify
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _parse_network_config(self):
|
||||||
|
self._parse_physical_network_types()
|
||||||
|
self._parse_network_vlan_ranges()
|
||||||
|
for network in self.network_vlan_ranges.keys():
|
||||||
|
if not self.phys_network_type_maps.get(network):
|
||||||
|
self.phys_network_type_maps[network] = self.physical_net_type
|
||||||
|
|
||||||
|
def _parse_physical_network_types(self):
|
||||||
|
"""Parse physical network types configuration.
|
||||||
|
|
||||||
|
Verify default physical network type is valid.
|
||||||
|
Parse physical network mappings.
|
||||||
|
"""
|
||||||
|
self.physical_net_type = cfg.CONF.MLNX.physical_network_type
|
||||||
|
if self.physical_net_type not in (constants.TYPE_ETH,
|
||||||
|
constants.TYPE_IB):
|
||||||
|
LOG.error(_("Invalid physical network type %(type)s."
|
||||||
|
"Server terminated!"), {'type': self.physical_net_type})
|
||||||
|
raise SystemExit(1)
|
||||||
|
try:
|
||||||
|
self.phys_network_type_maps = utils.parse_mappings(
|
||||||
|
cfg.CONF.MLNX.physical_network_type_mappings)
|
||||||
|
except ValueError as e:
|
||||||
|
LOG.error(_("Parsing physical_network_type failed: %s."
|
||||||
|
" Server terminated!"), e)
|
||||||
|
raise SystemExit(1)
|
||||||
|
for network, type in self.phys_network_type_maps.iteritems():
|
||||||
|
if type not in (constants.TYPE_ETH, constants.TYPE_IB):
|
||||||
|
LOG.error(_("Invalid physical network type %(type)s "
|
||||||
|
" for network %(net)s. Server terminated!"),
|
||||||
|
{'net': network, 'type': type})
|
||||||
|
raise SystemExit(1)
|
||||||
|
LOG.info(_("Physical Network type mappings: %s"),
|
||||||
|
self.phys_network_type_maps)
|
||||||
|
|
||||||
def _parse_network_vlan_ranges(self):
|
def _parse_network_vlan_ranges(self):
|
||||||
try:
|
try:
|
||||||
self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
|
self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
|
||||||
@ -142,14 +177,6 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
LOG.info(_("Network VLAN ranges: %s"), self.network_vlan_ranges)
|
LOG.info(_("Network VLAN ranges: %s"), self.network_vlan_ranges)
|
||||||
|
|
||||||
def _add_network_vlan_range(self, physical_network, vlan_min, vlan_max):
|
|
||||||
self._add_network(physical_network)
|
|
||||||
self.network_vlan_ranges[physical_network].append((vlan_min, vlan_max))
|
|
||||||
|
|
||||||
def _add_network(self, physical_network):
|
|
||||||
if physical_network not in self.network_vlan_ranges:
|
|
||||||
self.network_vlan_ranges[physical_network] = []
|
|
||||||
|
|
||||||
def _extend_network_dict_provider(self, context, network):
|
def _extend_network_dict_provider(self, context, network):
|
||||||
binding = db.get_network_binding(context.session, network['id'])
|
binding = db.get_network_binding(context.session, network['id'])
|
||||||
network[provider.NETWORK_TYPE] = binding.network_type
|
network[provider.NETWORK_TYPE] = binding.network_type
|
||||||
@ -166,7 +193,6 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
def _set_tenant_network_type(self):
|
def _set_tenant_network_type(self):
|
||||||
self.tenant_network_type = cfg.CONF.MLNX.tenant_network_type
|
self.tenant_network_type = cfg.CONF.MLNX.tenant_network_type
|
||||||
if self.tenant_network_type not in [svc_constants.TYPE_VLAN,
|
if self.tenant_network_type not in [svc_constants.TYPE_VLAN,
|
||||||
constants.TYPE_IB,
|
|
||||||
svc_constants.TYPE_LOCAL,
|
svc_constants.TYPE_LOCAL,
|
||||||
svc_constants.TYPE_NONE]:
|
svc_constants.TYPE_NONE]:
|
||||||
LOG.error(_("Invalid tenant_network_type: %s. "
|
LOG.error(_("Invalid tenant_network_type: %s. "
|
||||||
@ -194,7 +220,7 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
self._process_flat_net(segmentation_id_set)
|
self._process_flat_net(segmentation_id_set)
|
||||||
segmentation_id = constants.FLAT_VLAN_ID
|
segmentation_id = constants.FLAT_VLAN_ID
|
||||||
|
|
||||||
elif network_type in [svc_constants.TYPE_VLAN, constants.TYPE_IB]:
|
elif network_type == svc_constants.TYPE_VLAN:
|
||||||
self._process_vlan_net(segmentation_id, segmentation_id_set)
|
self._process_vlan_net(segmentation_id, segmentation_id_set)
|
||||||
|
|
||||||
elif network_type == svc_constants.TYPE_LOCAL:
|
elif network_type == svc_constants.TYPE_LOCAL:
|
||||||
@ -241,7 +267,6 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
physical_network,
|
physical_network,
|
||||||
physical_network_set):
|
physical_network_set):
|
||||||
if network_type in [svc_constants.TYPE_VLAN,
|
if network_type in [svc_constants.TYPE_VLAN,
|
||||||
constants.TYPE_IB,
|
|
||||||
svc_constants.TYPE_FLAT]:
|
svc_constants.TYPE_FLAT]:
|
||||||
if physical_network_set:
|
if physical_network_set:
|
||||||
if physical_network not in self.network_vlan_ranges:
|
if physical_network not in self.network_vlan_ranges:
|
||||||
@ -256,7 +281,10 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
return physical_network
|
return physical_network
|
||||||
|
|
||||||
def _check_port_binding_for_net_type(self, vnic_type, net_type):
|
def _check_port_binding_for_net_type(self, vnic_type, net_type):
|
||||||
if net_type == svc_constants.TYPE_VLAN:
|
"""
|
||||||
|
VIF_TYPE_DIRECT is valid only for Ethernet fabric
|
||||||
|
"""
|
||||||
|
if net_type == constants.TYPE_ETH:
|
||||||
return vnic_type in (constants.VIF_TYPE_DIRECT,
|
return vnic_type in (constants.VIF_TYPE_DIRECT,
|
||||||
constants.VIF_TYPE_HOSTDEV)
|
constants.VIF_TYPE_HOSTDEV)
|
||||||
elif net_type == constants.TYPE_IB:
|
elif net_type == constants.TYPE_IB:
|
||||||
@ -269,22 +297,23 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
net_binding = db.get_network_binding(context.session,
|
net_binding = db.get_network_binding(context.session,
|
||||||
attrs.get('network_id'))
|
attrs.get('network_id'))
|
||||||
net_type = net_binding.network_type
|
phy_net = net_binding.physical_network
|
||||||
|
|
||||||
if not binding_profile_set:
|
if not binding_profile_set:
|
||||||
return self.vnic_type
|
return self.vnic_type
|
||||||
if constants.VNIC_TYPE in binding_profile:
|
if constants.VNIC_TYPE in binding_profile:
|
||||||
vnic_type = binding_profile[constants.VNIC_TYPE]
|
vnic_type = binding_profile[constants.VNIC_TYPE]
|
||||||
|
phy_net_type = self.phys_network_type_maps[phy_net]
|
||||||
if vnic_type in (constants.VIF_TYPE_DIRECT,
|
if vnic_type in (constants.VIF_TYPE_DIRECT,
|
||||||
constants.VIF_TYPE_HOSTDEV):
|
constants.VIF_TYPE_HOSTDEV):
|
||||||
if self._check_port_binding_for_net_type(vnic_type,
|
if self._check_port_binding_for_net_type(vnic_type,
|
||||||
net_type):
|
phy_net_type):
|
||||||
self.base_binding_dict[portbindings.VIF_TYPE] = vnic_type
|
self.base_binding_dict[portbindings.VIF_TYPE] = vnic_type
|
||||||
return vnic_type
|
return vnic_type
|
||||||
else:
|
else:
|
||||||
msg = (_("Unsupported vnic type %(vnic_type)s "
|
msg = (_("Unsupported vnic type %(vnic_type)s "
|
||||||
"for network type %(net_type)s") %
|
"for physical network type %(net_type)s") %
|
||||||
{'vnic_type': vnic_type, 'net_type': net_type})
|
{'vnic_type': vnic_type, 'net_type': phy_net_type})
|
||||||
else:
|
else:
|
||||||
msg = _("Invalid vnic_type on port_create")
|
msg = _("Invalid vnic_type on port_create")
|
||||||
else:
|
else:
|
||||||
@ -307,15 +336,13 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
network_type = self.tenant_network_type
|
network_type = self.tenant_network_type
|
||||||
if network_type == svc_constants.TYPE_NONE:
|
if network_type == svc_constants.TYPE_NONE:
|
||||||
raise q_exc.TenantNetworksDisabled()
|
raise q_exc.TenantNetworksDisabled()
|
||||||
elif network_type in [svc_constants.TYPE_VLAN,
|
elif network_type == svc_constants.TYPE_VLAN:
|
||||||
constants.TYPE_IB]:
|
|
||||||
physical_network, vlan_id = db.reserve_network(session)
|
physical_network, vlan_id = db.reserve_network(session)
|
||||||
else: # TYPE_LOCAL
|
else: # TYPE_LOCAL
|
||||||
vlan_id = constants.LOCAL_VLAN_ID
|
vlan_id = constants.LOCAL_VLAN_ID
|
||||||
else:
|
else:
|
||||||
# provider network
|
# provider network
|
||||||
if network_type in [svc_constants.TYPE_VLAN,
|
if network_type in [svc_constants.TYPE_VLAN,
|
||||||
constants.TYPE_IB,
|
|
||||||
svc_constants.TYPE_FLAT]:
|
svc_constants.TYPE_FLAT]:
|
||||||
db.reserve_specific_network(session,
|
db.reserve_specific_network(session,
|
||||||
physical_network,
|
physical_network,
|
||||||
|
@ -29,6 +29,9 @@ class ConfigurationTest(base.BaseTestCase):
|
|||||||
cfg.CONF.MLNX.tenant_network_type)
|
cfg.CONF.MLNX.tenant_network_type)
|
||||||
self.assertEqual(1,
|
self.assertEqual(1,
|
||||||
len(cfg.CONF.MLNX.network_vlan_ranges))
|
len(cfg.CONF.MLNX.network_vlan_ranges))
|
||||||
|
self.assertEqual('eth',
|
||||||
|
cfg.CONF.MLNX.physical_network_type)
|
||||||
|
self.assertFalse(cfg.CONF.MLNX.physical_network_type_mappings)
|
||||||
self.assertEqual(0,
|
self.assertEqual(0,
|
||||||
len(cfg.CONF.ESWITCH.
|
len(cfg.CONF.ESWITCH.
|
||||||
physical_interface_mappings))
|
physical_interface_mappings))
|
||||||
|
89
neutron/tests/unit/mlnx/test_mlnx_plugin_config.py
Normal file
89
neutron/tests/unit/mlnx/test_mlnx_plugin_config.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# Copyright (c) 2014 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# 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 oslo.config import cfg
|
||||||
|
|
||||||
|
#NOTE this import loads tests required options
|
||||||
|
from neutron.plugins.mlnx.common import config # noqa
|
||||||
|
from neutron.plugins.mlnx.common import constants
|
||||||
|
from neutron.plugins.mlnx.mlnx_plugin import MellanoxEswitchPlugin
|
||||||
|
from neutron.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestMlnxPluginConfig(base.BaseTestCase):
|
||||||
|
expected_vlan_mappings = {'physnet1': [(1, 1000)],
|
||||||
|
'physnet2': [(1, 1000)]}
|
||||||
|
expected_network_types = {'physnet1': constants.TYPE_ETH,
|
||||||
|
'physnet2': constants.TYPE_IB}
|
||||||
|
config_vlan_ranges = ['physnet1:1:1000', 'physnet2:1:1000']
|
||||||
|
config_network_types = ['physnet1:eth', 'physnet2:ib']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestMlnxPluginConfig, self).setUp()
|
||||||
|
cfg.CONF.set_override('rpc_backend',
|
||||||
|
'neutron.openstack.common.rpc.impl_fake')
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='network_vlan_ranges',
|
||||||
|
override=self.config_vlan_ranges)
|
||||||
|
|
||||||
|
def _create_mlnx_plugin(self):
|
||||||
|
with mock.patch('neutron.plugins.mlnx.db.mlnx_db_v2'):
|
||||||
|
return MellanoxEswitchPlugin()
|
||||||
|
|
||||||
|
def _assert_expected_config(self):
|
||||||
|
plugin = self._create_mlnx_plugin()
|
||||||
|
self.assertEqual(plugin.network_vlan_ranges,
|
||||||
|
self.expected_vlan_mappings)
|
||||||
|
self.assertEqual(plugin.phys_network_type_maps,
|
||||||
|
self.expected_network_types)
|
||||||
|
|
||||||
|
def test_vlan_ranges_with_network_type(self):
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='physical_network_type_mappings',
|
||||||
|
override=self.config_network_types)
|
||||||
|
self._assert_expected_config()
|
||||||
|
|
||||||
|
def test_vlan_ranges_partial_network_type(self):
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='physical_network_type_mappings',
|
||||||
|
override=self.config_network_types[:1])
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='physical_network_type',
|
||||||
|
override=constants.TYPE_IB)
|
||||||
|
self._assert_expected_config()
|
||||||
|
|
||||||
|
def test_vlan_ranges_no_network_type(self):
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='physical_network_type',
|
||||||
|
override=constants.TYPE_IB)
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='physical_network_type_mappings',
|
||||||
|
override=[])
|
||||||
|
self.expected_network_types.update({'physnet1': constants.TYPE_IB})
|
||||||
|
self._assert_expected_config()
|
||||||
|
self.expected_network_types.update({'physnet1': constants.TYPE_ETH})
|
||||||
|
|
||||||
|
def test_parse_physical_network_mappings_invalid_type(self):
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='physical_network_type_mappings',
|
||||||
|
override=['physnet:invalid-type'])
|
||||||
|
self.assertRaises(SystemExit, self._create_mlnx_plugin)
|
||||||
|
|
||||||
|
def test_invalid_network_type(self):
|
||||||
|
cfg.CONF.set_override(group='MLNX',
|
||||||
|
name='physical_network_type',
|
||||||
|
override='invalid-type')
|
||||||
|
self.assertRaises(SystemExit, self._create_mlnx_plugin)
|
Loading…
Reference in New Issue
Block a user