[NetApp] Enables configuring NFS transfer limits

From this this change, shares and share groups can be created
upon share servers configured with specific NFS max transfer
limits. An administrator is now able to set the share type
extra-specs `netapp:udp_max_xfer_size` and
`netapp:tcp_max_xfer_size`. While creating a share server or
providing a share server to a share or a share group, the NetApp
driver will consider these extra-specs to decide whether to create
or reuse a share server.

Share server now contains the details:nfs_config field, which
stores the server NFS configuration dictionary. In case the server
does not have a NFS configuration requirement, it saves the
default NFS values, retrieved at the driver startup. A server
without details:nfs_config is considered as using the default one.

The share server manage operation was modified to also retrieve
its NFS max transfer configurations.

The share manage operation was modified to check whether the NFS
max transfer extra-specs are matching the share server configured
values.

It relies on ONTAP features available only in versions equal and
greater than ``9.4``.

Implements: bp netapp-share-server-nfs-modify
Change-Id: Iaddb771ae28ec59dd125af0bf638f591f5662bfc
Depends-On: I8daf919a764075998be95c5845807bec37104c78
This commit is contained in:
Felipe Rodrigues 2020-07-22 13:10:04 +00:00
parent 636f255546
commit 78ca06d6b8
11 changed files with 1013 additions and 34 deletions

View File

@ -70,6 +70,7 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
ontapi_1_2x = (1, 20) <= ontapi_version < (1, 30)
ontapi_1_30 = ontapi_version >= (1, 30)
ontapi_1_110 = ontapi_version >= (1, 110)
ontapi_1_140 = ontapi_version >= (1, 140)
ontapi_1_150 = ontapi_version >= (1, 150)
self.features.add_feature('SNAPMIRROR_V2', supported=ontapi_1_20)
@ -83,6 +84,8 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
self.features.add_feature('ADVANCED_DISK_PARTITIONING',
supported=ontapi_1_30)
self.features.add_feature('FLEXVOL_ENCRYPTION', supported=ontapi_1_110)
self.features.add_feature('TRANSFER_LIMIT_NFS_CONFIG',
supported=ontapi_1_140)
self.features.add_feature('CIFS_DC_ADD_SKIP_CHECK',
supported=ontapi_1_150)
@ -1352,10 +1355,14 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
raise exception.NetAppException(msg % security_service['type'])
@na_utils.trace
def enable_nfs(self, versions):
def enable_nfs(self, versions, nfs_config=None):
"""Enables NFS on Vserver."""
self.send_request('nfs-enable')
self._enable_nfs_protocols(versions)
if nfs_config:
self._configure_nfs(nfs_config)
self._create_default_nfs_export_rules()
@na_utils.trace
@ -1372,6 +1379,11 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
}
self.send_request('nfs-service-modify', nfs_service_modify_args)
@na_utils.trace
def _configure_nfs(self, nfs_config):
"""Sets the nfs configuraton"""
self.send_request('nfs-service-modify', nfs_config)
@na_utils.trace
def _create_default_nfs_export_rules(self):
"""Create the default export rule for the NFS service."""
@ -4033,3 +4045,44 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
'destination-vserver': destination_vserver,
}
self.send_request('volume-rehost', api_args)
@na_utils.trace
def get_nfs_config(self, desired_args, vserver):
"""Gets the NFS config of the given vserver with the desired params"""
api_args = {
'query': {
'nfs-info': {
'vserver': vserver,
},
},
}
nfs_info = {}
for arg in desired_args:
nfs_info[arg] = None
if nfs_info:
api_args['desired-attributes'] = {'nfs-info': nfs_info}
result = self.send_request('nfs-service-get-iter', api_args)
child_elem = result.get_child_by_name('attributes-list')
return self.parse_nfs_config(child_elem, desired_args)
@na_utils.trace
def get_nfs_config_default(self, desired_args):
"""Gets the default NFS config with the desired params"""
result = self.send_request('nfs-service-get-create-defaults', None)
child_elem = result.get_child_by_name('defaults')
return self.parse_nfs_config(child_elem, desired_args)
@na_utils.trace
def parse_nfs_config(self, parent_elem, desired_args):
"""Parse the get NFS config operation returning the desired params"""
nfs_info_elem = parent_elem.get_child_by_name('nfs-info')
nfs_config = {}
for arg in desired_args:
nfs_config[arg] = nfs_info_elem.get_child_content(arg)
return nfs_config

View File

@ -286,3 +286,18 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
def get_share_status(self, share_instance, share_server=None):
return self.library.get_share_status(share_instance, share_server)
def choose_share_server_compatible_with_share(self, context,
share_servers, share,
snapshot=None,
share_group=None):
return self.library.choose_share_server_compatible_with_share(
context, share_servers, share, snapshot=snapshot,
share_group=share_group)
def choose_share_server_compatible_with_share_group(
self, context, share_servers, share_group_ref,
share_group_snapshot=None):
return self.library.choose_share_server_compatible_with_share_group(
context, share_servers, share_group_ref,
share_group_snapshot=share_group_snapshot)

View File

@ -283,3 +283,13 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
def get_share_status(self, share_instance, share_server=None):
return self.library.get_share_status(share_instance, share_server)
def choose_share_server_compatible_with_share(self, context, share_servers,
share, snapshot=None,
share_group=None):
raise NotImplementedError
def choose_share_server_compatible_with_share_group(
self, context, share_servers, share_group_ref,
share_group_snapshot=None):
raise NotImplementedError

View File

@ -108,6 +108,13 @@ class NetAppCmodeFileStorageLibrary(object):
SIZE_DEPENDENT_QOS_SPECS = {'maxiopspergib', 'maxbpspergib'}
# Maps the NFS config used by share-servers
NFS_CONFIG_EXTRA_SPECS_MAP = {
'netapp:tcp_max_xfer_size': 'tcp-max-xfer-size',
'netapp:udp_max_xfer_size': 'udp-max-xfer-size',
}
def __init__(self, driver_name, **kwargs):
na_utils.validate_driver_instantiation(**kwargs)
@ -132,6 +139,8 @@ class NetAppCmodeFileStorageLibrary(object):
self._have_cluster_creds = None
self._revert_to_snapshot_support = False
self._cluster_info = {}
self._default_nfs_config = None
self.is_nfs_config_supported = False
self._app_version = kwargs.get('app_version', 'unknown')
@ -153,6 +162,17 @@ class NetAppCmodeFileStorageLibrary(object):
# Performance monitoring library
self._perf_library = performance.PerformanceLibrary(self._client)
# NOTE(felipe_rodrigues): In case adding a parameter that can be
# configured in old versions too, the "is_nfs_config_supported" should
# be removed (always supporting), adding the logic of skipping the
# transfer limit parameters when building the server nfs_config.
if self._client.features.TRANSFER_LIMIT_NFS_CONFIG:
self.is_nfs_config_supported = True
self._default_nfs_config = self._client.get_nfs_config_default(
list(self.NFS_CONFIG_EXTRA_SPECS_MAP.values()))
LOG.debug('The default NFS configuration: %s',
self._default_nfs_config)
@na_utils.trace
def _set_cluster_info(self):
self._cluster_info['nve_support'] = (
@ -929,7 +949,7 @@ class NetAppCmodeFileStorageLibrary(object):
return dict(zip(provisioning_args, provisioning_values))
@na_utils.trace
def _get_string_provisioning_options(self, specs, string_specs_map):
def get_string_provisioning_options(self, specs, string_specs_map):
"""Given extra specs, return corresponding client library kwargs.
Build a full set of client library provisioning kwargs, filling in a
@ -1027,7 +1047,7 @@ class NetAppCmodeFileStorageLibrary(object):
boolean_args = self._get_boolean_provisioning_options(
specs, self.BOOLEAN_QUALIFIED_EXTRA_SPECS_MAP)
string_args = self._get_string_provisioning_options(
string_args = self.get_string_provisioning_options(
specs, self.STRING_QUALIFIED_EXTRA_SPECS_MAP)
result = boolean_args.copy()
result.update(string_args)
@ -2330,6 +2350,12 @@ class NetAppCmodeFileStorageLibrary(object):
destination_share)
self._check_extra_specs_validity(
destination_share, extra_specs)
# NOTE (felipe_rodrigues): NetApp only can migrate within the
# same server, so it does not need to check that the
# destination share has the same NFS config as the destination
# server.
# TODO(gouthamr): Check whether QoS min-throughputs can be
# honored on the destination aggregate when supported.
self._check_aggregate_extra_specs_validity(

View File

@ -33,6 +33,7 @@ from manila.share.drivers.netapp.dataontap.client import client_cmode
from manila.share.drivers.netapp.dataontap.cluster_mode import data_motion
from manila.share.drivers.netapp.dataontap.cluster_mode import lib_base
from manila.share.drivers.netapp import utils as na_utils
from manila.share import share_types
from manila.share import utils as share_utils
from manila import utils
@ -123,12 +124,19 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
@na_utils.trace
def setup_server(self, network_info, metadata=None):
"""Creates and configures new Vserver."""
vlan = network_info['segmentation_id']
ports = {}
for network_allocation in network_info['network_allocations']:
ports[network_allocation['id']] = network_allocation['ip_address']
nfs_config = self._default_nfs_config
if (self.is_nfs_config_supported and metadata and
'share_type_id' in metadata):
extra_specs = share_types.get_share_type_extra_specs(
metadata['share_type_id'])
self._check_nfs_config_extra_specs_validity(extra_specs)
nfs_config = self._get_nfs_config_provisioning_options(extra_specs)
@utils.synchronized('netapp-VLAN-%s' % vlan, external=True)
def setup_server_with_lock():
LOG.debug('Creating server %s', network_info['server_id'])
@ -137,11 +145,15 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
vserver_name = self._get_vserver_name(network_info['server_id'])
server_details = {
'vserver_name': vserver_name,
'ports': jsonutils.dumps(ports)
'ports': jsonutils.dumps(ports),
}
if self.is_nfs_config_supported:
server_details['nfs_config'] = jsonutils.dumps(nfs_config)
try:
self._create_vserver(vserver_name, network_info)
self._create_vserver(vserver_name, network_info,
nfs_config=nfs_config)
except Exception as e:
e.detail_data = {'server_details': server_details}
raise
@ -150,6 +162,38 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
return setup_server_with_lock()
@na_utils.trace
def _check_nfs_config_extra_specs_validity(self, extra_specs):
"""Check if the nfs config extra_spec has valid values."""
int_extra_specs = ['netapp:tcp_max_xfer_size',
'netapp:udp_max_xfer_size']
for key in int_extra_specs:
if key in extra_specs:
self._check_if_extra_spec_is_positive(
extra_specs[key], key)
@na_utils.trace
def _check_if_extra_spec_is_positive(self, value, key):
"""Check if extra_spec has a valid positive int value."""
if int(value) < 0:
args = {'value': value, 'key': key}
msg = _('Invalid value "%(value)s" for extra_spec "%(key)s" '
'used by share server setup.')
raise exception.NetAppException(msg % args)
@na_utils.trace
def _get_nfs_config_provisioning_options(self, specs):
"""Return the nfs config provisioning option."""
nfs_config = self.get_string_provisioning_options(
specs, self.NFS_CONFIG_EXTRA_SPECS_MAP)
# Changes the no set config to the default value
for k, v in nfs_config.items():
if v is None:
nfs_config[k] = self._default_nfs_config[k]
return nfs_config
@na_utils.trace
def _validate_network_type(self, network_info):
"""Raises exception if the segmentation type is incorrect."""
@ -164,7 +208,7 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
return self.configuration.netapp_vserver_name_template % server_id
@na_utils.trace
def _create_vserver(self, vserver_name, network_info):
def _create_vserver(self, vserver_name, network_info, nfs_config=None):
"""Creates Vserver with given parameters if it doesn't exist."""
if self._client.vserver_exists(vserver_name):
@ -205,7 +249,8 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
network_info)
vserver_client.enable_nfs(
self.configuration.netapp_enabled_share_protocols)
self.configuration.netapp_enabled_share_protocols,
nfs_config=nfs_config)
security_services = network_info.get('security_services')
if security_services:
@ -327,7 +372,6 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
if not vserver_client.network_interface_exists(
vserver_name, node_name, port, ip_address, netmask, vlan):
self._client.create_network_interface(
ip_address, netmask, vlan, node_name, port, vserver_name,
lif_name, ipspace_name, mtu)
@ -508,11 +552,19 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
"""Manages a vserver by renaming it and returning backend_details."""
new_vserver_name = self._get_vserver_name(share_server['id'])
old_vserver_name = self._get_correct_vserver_old_name(identifier)
if new_vserver_name != old_vserver_name:
self._client.rename_vserver(old_vserver_name, new_vserver_name)
backend_details = {'vserver_name': new_vserver_name}
backend_details = {
'vserver_name': new_vserver_name,
}
if self.is_nfs_config_supported:
nfs_config = self._client.get_nfs_config(
list(self.NFS_CONFIG_EXTRA_SPECS_MAP.values()),
new_vserver_name)
backend_details['nfs_config'] = jsonutils.dumps(nfs_config)
return new_vserver_name, backend_details
def unmanage_server(self, server_details, security_services=None):
@ -607,5 +659,143 @@ class NetAppCmodeMultiSVMFileStorageLibrary(
return (super(NetAppCmodeMultiSVMFileStorageLibrary, self)
.create_share_from_snapshot(
context, share, snapshot, share_server=share_server,
parent_share=parent_share))
context, share, snapshot, share_server=share_server,
parent_share=parent_share))
@na_utils.trace
def _is_share_server_compatible(self, share_server, expected_nfs_config):
"""Check if the share server has the given nfs config
The None and the default_nfs_config should be considered
as the same configuration.
"""
nfs_config = share_server.get('backend_details', {}).get('nfs_config')
share_server_nfs = jsonutils.loads(nfs_config) if nfs_config else None
if share_server_nfs == expected_nfs_config:
return True
elif (share_server_nfs is None and
expected_nfs_config == self._default_nfs_config):
return True
elif (expected_nfs_config is None and
share_server_nfs == self._default_nfs_config):
return True
return False
def choose_share_server_compatible_with_share(self, context, share_servers,
share, snapshot=None,
share_group=None):
"""Method that allows driver to choose share server for provided share.
If compatible share-server is not found, method should return None.
:param context: Current context
:param share_servers: list with share-server models
:param share: share model
:param snapshot: snapshot model
:param share_group: ShareGroup model with shares
:returns: share-server or None
"""
if not share_servers:
# No share server to reuse
return None
nfs_config = None
if self.is_nfs_config_supported:
extra_specs = share_types.get_extra_specs_from_share(share)
nfs_config = self._get_nfs_config_provisioning_options(extra_specs)
for share_server in share_servers:
if self._check_reuse_share_server(share_server, nfs_config,
share_group=share_group):
return share_server
return None
@na_utils.trace
def _check_reuse_share_server(self, share_server, nfs_config,
share_group=None):
"""Check whether the share_server can be reused or not."""
if (share_group and share_group.get('share_server_id') !=
share_server['id']):
return False
if self.is_nfs_config_supported:
# NOTE(felipe_rodrigues): Do not check that the share nfs_config
# matches with the group nfs_config, because the API guarantees
# that the share type is an element of the group types.
return self._is_share_server_compatible(share_server, nfs_config)
return True
@na_utils.trace
def choose_share_server_compatible_with_share_group(
self, context, share_servers, share_group_ref,
share_group_snapshot=None):
"""Choose the server compatible with group.
If the NFS configuration is supported, it will check that the group
types agree for the NFS extra-specs values.
"""
if not share_servers:
# No share server to reuse
return None
nfs_config = None
if self.is_nfs_config_supported:
nfs_config = self._get_nfs_config_share_group(share_group_ref)
for share_server in share_servers:
if self._check_reuse_share_server(share_server, nfs_config):
return share_server
return None
@na_utils.trace
def _get_nfs_config_share_group(self, share_group_ref):
"""Get the NFS config of the share group.
In case the group types do not agree for the NFS config, it throws an
exception.
"""
nfs_config = None
first = True
for st in share_group_ref.get('share_types', []):
extra_specs = share_types.get_share_type_extra_specs(
st['share_type_id'])
if first:
self._check_nfs_config_extra_specs_validity(extra_specs)
nfs_config = self._get_nfs_config_provisioning_options(
extra_specs)
first = False
continue
type_nfs_config = self._get_nfs_config_provisioning_options(
extra_specs)
if nfs_config != type_nfs_config:
msg = _("The specified share_types cannot have "
"conflicting values for the NFS configuration "
"extra-specs.")
raise exception.InvalidInput(reason=msg)
return nfs_config
@na_utils.trace
def manage_existing(self, share, driver_options, share_server=None):
# In case NFS config is supported, the share's nfs_config must be the
# same as the server
if share_server and self.is_nfs_config_supported:
extra_specs = share_types.get_extra_specs_from_share(share)
nfs_config = self._get_nfs_config_provisioning_options(extra_specs)
if not self._is_share_server_compatible(share_server, nfs_config):
args = {'server_id': share_server['id']}
msg = _('Invalid NFS configuration for the server '
'%(server_id)s . The extra-specs must match the '
'values of NFS of the server.')
raise exception.NetAppException(msg % args)
return (super(NetAppCmodeMultiSVMFileStorageLibrary, self).
manage_existing(share, driver_options,
share_server=share_server))

View File

@ -2409,6 +2409,43 @@ VOLUME_MOVE_GET_ITER_RESULT = etree.XML("""
'vserver': VSERVER_NAME,
})
NFS_INFO_STR = """
<nfs-info>
<is-rquota-enabled>false</is-rquota-enabled>
<is-tcp-enabled>true</is-tcp-enabled>
<is-v3-hide-snapshot>false</is-v3-hide-snapshot>
<ntfs-unix-security-ops>use_export_policy</ntfs-unix-security-ops>
<permitted-enc-types>
<string>des</string>
<string>des3</string>
<string>aes_128</string>
<string>aes_256</string>
</permitted-enc-types>
<tcp-max-xfer-size>65536</tcp-max-xfer-size>
<udp-max-xfer-size>32768</udp-max-xfer-size>
<v3-search-unconverted-filename>false</v3-search-unconverted-filename>
<v4-inherited-acl-preserve>false</v4-inherited-acl-preserve>
</nfs-info>
"""
NFS_INFO_DEFAULT_TREE = etree.XML(NFS_INFO_STR)
NFS_CONFIG_DEFAULT_RESULT = etree.XML("""
<results status="passed">
<defaults>
%s
</defaults>
</results>
""" % NFS_INFO_STR)
NFS_CONFIG_SERVER_RESULT = etree.XML("""
<results status="passed">
<attributes-list>
%s
</attributes-list>
</results>
""" % NFS_INFO_STR)
PERF_OBJECT_COUNTER_TOTAL_CP_MSECS_LABELS = [
'SETUP', 'PRE_P0', 'P0_SNAP_DEL', 'P1_CLEAN', 'P1_QUOTA', 'IPU_DISK_ADD',
'P2V_INOFILE', 'P2V_INO_PUB', 'P2V_INO_PRI', 'P2V_FSINFO', 'P2V_DLOG1',

View File

@ -2398,18 +2398,24 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_has_calls([
mock.call('vserver-modify', vserver_modify_args)])
def test_enable_nfs(self):
@ddt.data({'tcp-max-xfer-size': 10000}, {}, None)
def test_enable_nfs(self, nfs_config):
self.mock_object(self.client, 'send_request')
self.mock_object(self.client, '_enable_nfs_protocols')
self.mock_object(self.client, '_create_default_nfs_export_rules')
self.mock_object(self.client, '_configure_nfs')
self.client.enable_nfs(fake.NFS_VERSIONS)
self.client.enable_nfs(fake.NFS_VERSIONS, nfs_config)
self.client.send_request.assert_called_once_with('nfs-enable')
self.client._enable_nfs_protocols.assert_called_once_with(
fake.NFS_VERSIONS)
self.client._create_default_nfs_export_rules.assert_called_once_with()
if nfs_config:
self.client._configure_nfs.assert_called_once_with(nfs_config)
else:
self.client._configure_nfs.assert_not_called()
@ddt.data((True, True, True), (True, False, False), (False, True, True))
@ddt.unpack
@ -2435,6 +2441,17 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_called_once_with(
'nfs-service-modify', nfs_service_modify_args)
def test_configure_nfs(self):
fake_nfs = {
'tcp-max-xfer-size': 10000,
}
self.mock_object(self.client, 'send_request')
self.client._configure_nfs(fake_nfs)
self.client.send_request.assert_called_once_with(
'nfs-service-modify', fake_nfs)
def test_create_default_nfs_export_rules(self):
class CopyingMock(mock.Mock):
@ -6790,3 +6807,74 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_iter_request.assert_called_once_with(
'volume-get-iter', expected_api_args)
self.assertEqual(expected_snapshot_name, result)
def test_get_nfs_config(self):
api_args = {
'query': {
'nfs-info': {
'vserver': 'vserver',
},
},
'desired-attributes': {
'nfs-info': {
'field': None,
},
},
}
api_response = netapp_api.NaElement(
fake.NFS_CONFIG_SERVER_RESULT)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
self.mock_object(self.client,
'parse_nfs_config',
mock.Mock(return_value=None))
self.client.get_nfs_config(['field'], 'vserver')
self.client.send_request.assert_called_once_with(
'nfs-service-get-iter', api_args)
def test_get_nfs_config_default(self):
api_response = netapp_api.NaElement(
fake.NFS_CONFIG_DEFAULT_RESULT)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
self.mock_object(self.client,
'parse_nfs_config',
mock.Mock(return_value=None))
self.client.get_nfs_config_default(['field'])
self.client.send_request.assert_called_once_with(
'nfs-service-get-create-defaults', None)
@ddt.data(
{'nfs_info': fake.NFS_CONFIG_SERVER_RESULT,
'desired_args': ['tcp-max-xfer-size'],
'expected_nfs': {
'tcp-max-xfer-size': '65536',
}},
{'nfs_info': fake.NFS_CONFIG_SERVER_RESULT,
'desired_args': ['udp-max-xfer-size'],
'expected_nfs': {
'udp-max-xfer-size': '32768',
}},
{'nfs_info': fake.NFS_CONFIG_SERVER_RESULT,
'desired_args': ['tcp-max-xfer-size', 'udp-max-xfer-size'],
'expected_nfs': {
'tcp-max-xfer-size': '65536',
'udp-max-xfer-size': '32768',
}},
{'nfs_info': fake.NFS_CONFIG_SERVER_RESULT,
'desired_args': [],
'expected_nfs': {}})
@ddt.unpack
def test_parse_nfs_config(self, nfs_info, desired_args, expected_nfs):
parent_elem = netapp_api.NaElement(nfs_info).get_child_by_name(
'attributes-list')
nfs_config = self.client.parse_nfs_config(parent_elem, desired_args)
self.assertDictEqual(nfs_config, expected_nfs)

View File

@ -118,6 +118,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object(
self.library._client, 'check_for_cluster_credentials',
mock.Mock(return_value=True))
self.mock_object(
self.library._client, 'get_nfs_config_default',
mock.Mock(return_value=fake.NFS_CONFIG_DEFAULT))
self.mock_object(
self.library, '_check_snaprestore_license',
mock.Mock(return_value=True))
@ -125,12 +128,17 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.library,
'_get_licenses',
mock.Mock(return_value=fake.LICENSES))
mock_get_api_client.features.TRANSFER_LIMIT_NFS_CONFIG = True
self.library.do_setup(self.context)
self.assertEqual(fake.LICENSES, self.library._licenses)
mock_get_api_client.assert_called_once_with()
(self.library._client.check_for_cluster_credentials.
assert_called_once_with())
(self.library._client.get_nfs_config_default.
assert_called_once_with(
list(self.library.NFS_CONFIG_EXTRA_SPECS_MAP.values())))
self.assertEqual('fake_perf_library', self.library._perf_library)
self.mock_object(self.library._client,
'check_for_cluster_credentials',
@ -1481,14 +1489,14 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertEqual(expected, result)
def test_get_string_provisioning_options(self):
result = self.library._get_string_provisioning_options(
result = self.library.get_string_provisioning_options(
fake.STRING_EXTRA_SPEC,
self.library.STRING_QUALIFIED_EXTRA_SPECS_MAP)
self.assertEqual(fake.PROVISIONING_OPTIONS_STRING, result)
def test_get_string_provisioning_options_missing_spec(self):
result = self.library._get_string_provisioning_options(
result = self.library.get_string_provisioning_options(
fake.SHORT_STRING_EXTRA_SPEC,
self.library.STRING_QUALIFIED_EXTRA_SPECS_MAP)
@ -1496,7 +1504,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
result)
def test_get_string_provisioning_options_implicit_false(self):
result = self.library._get_string_provisioning_options(
result = self.library.get_string_provisioning_options(
fake.EMPTY_EXTRA_SPEC,
self.library.STRING_QUALIFIED_EXTRA_SPECS_MAP)

View File

@ -30,6 +30,7 @@ from manila.share.drivers.netapp.dataontap.cluster_mode import data_motion
from manila.share.drivers.netapp.dataontap.cluster_mode import lib_base
from manila.share.drivers.netapp.dataontap.cluster_mode import lib_multi_svm
from manila.share.drivers.netapp import utils as na_utils
from manila.share import share_types
from manila.share import utils as share_utils
from manila import test
from manila.tests.share.drivers.netapp.dataontap.client import fakes as c_fake
@ -84,6 +85,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.fake_new_client = mock.Mock()
self.fake_client = mock.Mock()
self.library._default_nfs_config = fake.NFS_CONFIG_DEFAULT
def test_check_for_setup_error_cluster_creds_no_vserver(self):
self.library._have_cluster_creds = True
@ -229,8 +231,11 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
}
self.assertEqual(expected, result)
@ddt.data('fake', fake.IDENTIFIER)
def test_manage_server(self, fake_vserver_name):
@ddt.data({'fake_vserver_name': fake, 'nfs_config_support': False},
{'fake_vserver_name': fake.IDENTIFIER,
'nfs_config_support': True})
@ddt.unpack
def test_manage_server(self, fake_vserver_name, nfs_config_support):
self.mock_object(context,
'get_admin_context',
@ -238,6 +243,10 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_get_vserver_name = self.mock_object(
self.library, '_get_vserver_name',
mock.Mock(return_value=fake_vserver_name))
self.library.is_nfs_config_supported = nfs_config_support
mock_get_nfs_config = self.mock_object(
self.library._client, 'get_nfs_config',
mock.Mock(return_value=fake.NFS_CONFIG_DEFAULT))
new_identifier, new_details = self.library.manage_server(
context, fake.SHARE_SERVER, fake.IDENTIFIER, {})
@ -245,6 +254,14 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_get_vserver_name.assert_called_once_with(fake.SHARE_SERVER['id'])
self.assertEqual(fake_vserver_name, new_details['vserver_name'])
self.assertEqual(fake_vserver_name, new_identifier)
if nfs_config_support:
mock_get_nfs_config.assert_called_once_with(
list(self.library.NFS_CONFIG_EXTRA_SPECS_MAP.values()),
fake_vserver_name)
self.assertEqual(jsonutils.dumps(fake.NFS_CONFIG_DEFAULT),
new_details['nfs_config'])
else:
mock_get_nfs_config.assert_not_called()
def test_get_share_server_network_info(self):
@ -318,20 +335,37 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertListEqual([fake.AGGREGATES[0]], result)
mock_list_non_root_aggregates.assert_called_once_with()
def test_setup_server(self):
@ddt.data({'nfs_config_support': False},
{'nfs_config_support': True,
'nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'nfs_config_support': True,
'nfs_config': fake.NFS_CONFIG_DEFAULT})
@ddt.unpack
def test_setup_server(self, nfs_config_support, nfs_config=None):
mock_get_vserver_name = self.mock_object(
self.library,
'_get_vserver_name',
mock.Mock(return_value=fake.VSERVER1))
mock_create_vserver = self.mock_object(self.library, '_create_vserver')
mock_validate_network_type = self.mock_object(
self.library,
'_validate_network_type')
self.library.is_nfs_config_supported = nfs_config_support
mock_get_extra_spec = self.mock_object(
share_types, "get_share_type_extra_specs",
mock.Mock(return_value=fake.EXTRA_SPEC))
mock_check_extra_spec = self.mock_object(
self.library,
'_check_nfs_config_extra_specs_validity',
mock.Mock())
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=nfs_config))
result = self.library.setup_server(fake.NETWORK_INFO)
result = self.library.setup_server(fake.NETWORK_INFO,
fake.SERVER_METADATA)
ports = {}
for network_allocation in fake.NETWORK_INFO['network_allocations']:
@ -340,11 +374,28 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertTrue(mock_validate_network_type.called)
self.assertTrue(mock_get_vserver_name.called)
self.assertTrue(mock_create_vserver.called)
self.assertDictEqual({'vserver_name': fake.VSERVER1,
'ports': jsonutils.dumps(ports)}, result)
if nfs_config_support:
mock_get_extra_spec.assert_called_once_with(
fake.SERVER_METADATA['share_type_id'])
mock_check_extra_spec.assert_called_once_with(
fake.EXTRA_SPEC)
mock_get_nfs_config.assert_called_once_with(
fake.EXTRA_SPEC)
else:
mock_get_extra_spec.assert_not_called()
mock_check_extra_spec.assert_not_called()
mock_get_nfs_config.assert_not_called()
expected = {
'vserver_name': fake.VSERVER1,
'ports': jsonutils.dumps(ports),
}
if nfs_config_support:
expected.update({'nfs_config': jsonutils.dumps(nfs_config)})
self.assertDictEqual(expected, result)
def test_setup_server_with_error(self):
self.library.is_nfs_config_supported = False
mock_get_vserver_name = self.mock_object(
self.library,
'_get_vserver_name',
@ -363,7 +414,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertRaises(
exception.ManilaException,
self.library.setup_server,
fake.NETWORK_INFO)
fake.NETWORK_INFO,
fake.SERVER_METADATA)
ports = {}
for network_allocation in fake.NETWORK_INFO['network_allocations']:
@ -372,9 +424,12 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertTrue(mock_validate_network_type.called)
self.assertTrue(mock_get_vserver_name.called)
self.assertTrue(mock_create_vserver.called)
self.assertDictEqual(
{'server_details': {'vserver_name': fake.VSERVER1,
'ports': jsonutils.dumps(ports)}},
{'server_details': {
'vserver_name': fake.VSERVER1,
'ports': jsonutils.dumps(ports),
}},
fake_exception.detail_data)
@ddt.data(
@ -442,7 +497,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.mock_object(self.library, '_create_vserver_admin_lif')
self.mock_object(self.library, '_create_vserver_routes')
self.library._create_vserver(vserver_name, fake.NETWORK_INFO)
self.library._create_vserver(vserver_name, fake.NETWORK_INFO,
fake.NFS_CONFIG_TCP_UDP_MAX)
get_ipspace_name_for_vlan_port.assert_called_once_with(
fake.CLUSTER_NODES[0],
@ -462,7 +518,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
vserver_name, vserver_client, fake.NETWORK_INFO, fake.IPSPACE)
self.library._create_vserver_routes.assert_called_once_with(
vserver_client, fake.NETWORK_INFO)
vserver_client.enable_nfs.assert_called_once_with(versions)
vserver_client.enable_nfs.assert_called_once_with(
versions, nfs_config=fake.NFS_CONFIG_TCP_UDP_MAX)
self.library._client.setup_security_services.assert_called_once_with(
fake.NETWORK_INFO['security_services'], vserver_client,
vserver_name)
@ -482,7 +539,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertRaises(exception.NetAppException,
self.library._create_vserver,
vserver_name,
fake.NETWORK_INFO)
fake.NETWORK_INFO,
fake.NFS_CONFIG_TCP_UDP_MAX)
@ddt.data(
{'lif_exception': netapp_api.NaApiError,
@ -534,7 +592,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertRaises(lif_exception,
self.library._create_vserver,
vserver_name,
fake.NETWORK_INFO)
fake.NETWORK_INFO,
fake.NFS_CONFIG_TCP_UDP_MAX)
self.library._get_api_client.assert_called_with(vserver=vserver_name)
self.assertTrue(self.library._client.create_vserver.called)
@ -1213,3 +1272,360 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_create_from_snap.assert_called_once_with(
None, fake.SHARE, fake.SNAPSHOT, share_server=fake.SHARE_SERVER,
parent_share=fake_parent_share)
def test_check_if_extra_spec_is_positive_with_negative_integer(self):
self.assertRaises(exception.NetAppException,
self.library._check_if_max_files_is_valid,
fake.SHARE, -1)
def test_check_if_extra_spec_is_positive_with_string(self):
self.assertRaises(ValueError,
self.library._check_if_max_files_is_valid,
fake.SHARE, 'abc')
def test_check_nfs_config_extra_specs_validity(self):
result = self.library._check_nfs_config_extra_specs_validity(
fake.EXTRA_SPEC)
self.assertIsNone(result)
def test_check_nfs_config_extra_specs_validity_empty_spec(self):
result = self.library._check_nfs_config_extra_specs_validity({})
self.assertIsNone(result)
@ddt.data(fake.INVALID_TCP_MAX_XFER_SIZE_EXTRA_SPEC,
fake.INVALID_UDP_MAX_XFER_SIZE_EXTRA_SPEC)
def test_check_nfs_config_extra_specs_validity_invalid_value(self,
extra_specs):
self.assertRaises(
exception.NetAppException,
self.library._check_nfs_config_extra_specs_validity,
extra_specs)
@ddt.data({}, fake.STRING_EXTRA_SPEC)
def test_get_nfs_config_provisioning_options_empty(self, extra_specs):
result = self.library._get_nfs_config_provisioning_options(
extra_specs)
self.assertDictEqual(result, fake.NFS_CONFIG_DEFAULT)
@ddt.data(
{'extra_specs': fake.NFS_CONFIG_TCP_MAX_DDT['extra_specs'],
'expected': fake.NFS_CONFIG_TCP_MAX_DDT['expected']},
{'extra_specs': fake.NFS_CONFIG_UDP_MAX_DDT['extra_specs'],
'expected': fake.NFS_CONFIG_UDP_MAX_DDT['expected']},
{'extra_specs': fake.NFS_CONFIG_TCP_UDP_MAX_DDT['extra_specs'],
'expected': fake.NFS_CONFIG_TCP_UDP_MAX_DDT['expected']},
)
@ddt.unpack
def test_get_nfs_config_provisioning_options_valid(self, extra_specs,
expected):
result = self.library._get_nfs_config_provisioning_options(
extra_specs)
self.assertDictEqual(expected, result)
@ddt.data({'fake_share_server': fake.SHARE_SERVER_NFS_TCP,
'expected_nfs_config': fake.NFS_CONFIG_TCP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NFS_UDP,
'expected_nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'expected_nfs_config': fake.NFS_CONFIG_TCP_UDP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NO_DETAILS,
'expected_nfs_config': fake.NFS_CONFIG_DEFAULT},
{'fake_share_server': fake.SHARE_SERVER_NFS_DEFAULT,
'expected_nfs_config': None},
{'fake_share_server': fake.SHARE_SERVER_NO_NFS_NONE,
'expected_nfs_config': fake.NFS_CONFIG_DEFAULT})
@ddt.unpack
def test_is_share_server_compatible_true(self, fake_share_server,
expected_nfs_config):
is_same = self.library._is_share_server_compatible(
fake_share_server, expected_nfs_config)
self.assertTrue(is_same)
@ddt.data({'fake_share_server': fake.SHARE_SERVER_NFS_TCP,
'expected_nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NFS_UDP,
'expected_nfs_config': fake.NFS_CONFIG_TCP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'expected_nfs_config': fake.NFS_CONFIG_TCP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'expected_nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'expected_nfs_config': None},
{'fake_share_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'expected_nfs_config': {}},
{'fake_share_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'expected_nfs_config': fake.NFS_CONFIG_DEFAULT},
{'fake_share_server': fake.SHARE_SERVER_NO_DETAILS,
'expected_nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NFS_DEFAULT,
'expected_nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'fake_share_server': fake.SHARE_SERVER_NO_NFS_NONE,
'expected_nfs_config': fake.NFS_CONFIG_TCP_MAX})
@ddt.unpack
def test_is_share_server_compatible_false(self, fake_share_server,
expected_nfs_config):
is_same = self.library._is_share_server_compatible(
fake_share_server, expected_nfs_config)
self.assertFalse(is_same)
@ddt.data(
{'expected_server': fake.SHARE_SERVER_NFS_TCP,
'share_group': {'share_server_id': fake.SHARE_SERVER_NFS_TCP['id']},
'nfs_config': fake.NFS_CONFIG_TCP_MAX},
{'expected_server': fake.SHARE_SERVER_NFS_UDP,
'share_group': {'share_server_id': fake.SHARE_SERVER_NFS_UDP['id']},
'nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'expected_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'share_group': {
'share_server_id': fake.SHARE_SERVER_NFS_TCP_UDP['id']},
'nfs_config': fake.NFS_CONFIG_TCP_UDP_MAX},
{'expected_server': fake.SHARE_SERVER_NFS_DEFAULT,
'share_group': {
'share_server_id': fake.SHARE_SERVER_NFS_DEFAULT['id']},
'nfs_config': fake.NFS_CONFIG_DEFAULT},
{'expected_server': None,
'share_group': {'share_server_id': 'invalid_id'},
'nfs_config': fake.NFS_CONFIG_TCP_MAX})
@ddt.unpack
def test_choose_share_server_compatible_with_share_group_and_nfs_config(
self, expected_server, share_group, nfs_config):
self.library.is_nfs_config_supported = True
mock_get_extra_spec = self.mock_object(
share_types, "get_extra_specs_from_share",
mock.Mock(return_value=fake.EXTRA_SPEC))
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=nfs_config))
server = self.library.choose_share_server_compatible_with_share(
None, fake.SHARE_SERVERS, fake.SHARE, None, share_group)
mock_get_extra_spec.assert_called_once_with(fake.SHARE)
mock_get_nfs_config.assert_called_once_with(fake.EXTRA_SPEC)
self.assertEqual(expected_server, server)
@ddt.data(
{'expected_server': fake.SHARE_SERVER_NO_NFS_NONE,
'share_group': {'share_server_id':
fake.SHARE_SERVER_NO_NFS_NONE['id']}},
{'expected_server': fake.SHARE_SERVER_NO_DETAILS,
'share_group': {'share_server_id':
fake.SHARE_SERVER_NO_DETAILS['id']}},
{'expected_server': fake.SHARE_SERVER_NO_DETAILS,
'share_group': {
'share_server_id': fake.SHARE_SERVER_NO_DETAILS['id']},
'nfs_config_support': False},
{'expected_server': None,
'share_group': {'share_server_id': 'invalid_id'}})
@ddt.unpack
def test_choose_share_server_compatible_with_share_group_only(
self, expected_server, share_group, nfs_config_support=True):
self.library.is_nfs_config_supported = nfs_config_support
mock_get_extra_spec = self.mock_object(
share_types, "get_extra_specs_from_share",
mock.Mock(return_value=fake.EMPTY_EXTRA_SPEC))
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=fake.NFS_CONFIG_DEFAULT))
server = self.library.choose_share_server_compatible_with_share(
None, fake.SHARE_SERVERS, fake.SHARE, None, share_group)
self.assertEqual(expected_server, server)
if nfs_config_support:
mock_get_extra_spec.assert_called_once_with(fake.SHARE)
mock_get_nfs_config.assert_called_once_with(fake.EMPTY_EXTRA_SPEC)
@ddt.data(
{'expected_server': fake.SHARE_SERVER_NFS_TCP,
'nfs_config': fake.NFS_CONFIG_TCP_MAX},
{'expected_server': fake.SHARE_SERVER_NFS_UDP,
'nfs_config': fake.NFS_CONFIG_UDP_MAX},
{'expected_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'nfs_config': fake.NFS_CONFIG_TCP_UDP_MAX},
{'expected_server': fake.SHARE_SERVER_NFS_DEFAULT,
'nfs_config': fake.NFS_CONFIG_DEFAULT},
{'expected_server': None,
'nfs_config': {'invalid': 'invalid'}},
{'expected_server': fake.SHARE_SERVER_NFS_TCP,
'nfs_config': None, 'nfs_config_support': False},
)
@ddt.unpack
def test_choose_share_server_compatible_with_share_nfs_config_only(
self, expected_server, nfs_config, nfs_config_support=True):
self.library.is_nfs_config_supported = nfs_config_support
mock_get_extra_spec = self.mock_object(
share_types, "get_extra_specs_from_share",
mock.Mock(return_value=fake.EXTRA_SPEC))
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=nfs_config))
server = self.library.choose_share_server_compatible_with_share(
None, fake.SHARE_SERVERS, fake.SHARE)
self.assertEqual(expected_server, server)
if nfs_config_support:
mock_get_extra_spec.assert_called_once_with(fake.SHARE)
mock_get_nfs_config.assert_called_once_with(fake.EXTRA_SPEC)
@ddt.data(
{'expected_server': fake.SHARE_SERVER_NO_DETAILS,
'share_servers': [
fake.SHARE_SERVER_NFS_TCP, fake.SHARE_SERVER_NO_DETAILS]},
{'expected_server': fake.SHARE_SERVER_NO_NFS_NONE,
'share_servers': [
fake.SHARE_SERVER_NFS_UDP, fake.SHARE_SERVER_NO_NFS_NONE]},
{'expected_server': fake.SHARE_SERVER_NFS_DEFAULT,
'share_servers': [
fake.SHARE_SERVER_NFS_UDP, fake.SHARE_SERVER_NFS_DEFAULT]},
{'expected_server': None,
'share_servers': [
fake.SHARE_SERVER_NFS_TCP, fake.SHARE_SERVER_NFS_UDP]},
{'expected_server': fake.SHARE_SERVER_NO_DETAILS,
'share_servers': [fake.SHARE_SERVER_NO_DETAILS],
'nfs_config_support': False}
)
@ddt.unpack
def test_choose_share_server_compatible_with_share_no_specification(
self, expected_server, share_servers, nfs_config_support=True):
self.library.is_nfs_config_supported = nfs_config_support
mock_get_extra_spec = self.mock_object(
share_types, "get_extra_specs_from_share",
mock.Mock(return_value=fake.EMPTY_EXTRA_SPEC))
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=fake.NFS_CONFIG_DEFAULT))
server = self.library.choose_share_server_compatible_with_share(
None, share_servers, fake.SHARE)
self.assertEqual(expected_server, server)
if nfs_config_support:
mock_get_extra_spec.assert_called_once_with(fake.SHARE)
mock_get_nfs_config.assert_called_once_with(fake.EMPTY_EXTRA_SPEC)
def test_manage_existing_error(self):
fake_server = {'id': 'id'}
fake_nfs_config = 'fake_nfs_config'
self.library.is_nfs_config_supported = True
mock_get_extra_spec = self.mock_object(
share_types, "get_extra_specs_from_share",
mock.Mock(return_value=fake.EXTRA_SPEC))
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=fake_nfs_config))
mock_is_compatible = self.mock_object(
self.library,
"_is_share_server_compatible",
mock.Mock(return_value=False))
self.assertRaises(exception.NetAppException,
self.library.manage_existing,
fake.SHARE, 'opts', fake_server)
mock_get_extra_spec.assert_called_once_with(fake.SHARE)
mock_get_nfs_config.assert_called_once_with(fake.EXTRA_SPEC)
mock_is_compatible.assert_called_once_with(fake_server,
fake_nfs_config)
def test_choose_share_server_compatible_with_share_group_no_share_server(
self):
server = self.library.choose_share_server_compatible_with_share_group(
None, [], fake.SHARE_GROUP_REF)
self.assertIsNone(server)
@ddt.data(
[fake.NFS_CONFIG_DEFAULT, fake.NFS_CONFIG_TCP_MAX],
[fake.NFS_CONFIG_TCP_MAX, fake.NFS_CONFIG_UDP_MAX],
[fake.NFS_CONFIG_TCP_UDP_MAX, fake.NFS_CONFIG_TCP_MAX],
[fake.NFS_CONFIG_DEFAULT, fake.NFS_CONFIG_TCP_UDP_MAX])
def test_choose_share_server_compatible_with_share_group_nfs_conflict(
self, nfs_config_list):
self.library.is_nfs_config_supported = True
self.mock_object(
share_types, "get_share_type_extra_specs",
mock.Mock(return_value=fake.EXTRA_SPEC))
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(side_effect=nfs_config_list))
mock_check_extra_spec = self.mock_object(
self.library,
'_check_nfs_config_extra_specs_validity',
mock.Mock())
self.assertRaises(exception.InvalidInput,
self.library.
choose_share_server_compatible_with_share_group,
None, fake.SHARE_SERVERS, fake.SHARE_GROUP_REF)
mock_get_nfs_config.assert_called_with(fake.EXTRA_SPEC)
mock_check_extra_spec.assert_called_once_with(fake.EXTRA_SPEC)
@ddt.data(
{'expected_server': fake.SHARE_SERVER_NO_DETAILS,
'nfs_config': fake.NFS_CONFIG_DEFAULT,
'share_servers': [
fake.SHARE_SERVER_NFS_TCP, fake.SHARE_SERVER_NO_DETAILS]},
{'expected_server': fake.SHARE_SERVER_NO_NFS_NONE,
'nfs_config': fake.NFS_CONFIG_DEFAULT,
'share_servers': [
fake.SHARE_SERVER_NFS_UDP, fake.SHARE_SERVER_NO_NFS_NONE]},
{'expected_server': fake.SHARE_SERVER_NFS_DEFAULT,
'nfs_config': fake.NFS_CONFIG_DEFAULT,
'share_servers': [
fake.SHARE_SERVER_NFS_UDP, fake.SHARE_SERVER_NFS_DEFAULT]},
{'expected_server': None,
'nfs_config': fake.NFS_CONFIG_DEFAULT,
'share_servers': [
fake.SHARE_SERVER_NFS_TCP, fake.SHARE_SERVER_NFS_UDP,
fake.SHARE_SERVER_NFS_TCP_UDP]},
{'expected_server': fake.SHARE_SERVER_NFS_TCP_UDP,
'nfs_config': fake.NFS_CONFIG_TCP_UDP_MAX,
'share_servers': [
fake.SHARE_SERVER_NFS_TCP, fake.SHARE_SERVER_NFS_UDP,
fake.SHARE_SERVER_NFS_DEFAULT, fake.SHARE_SERVER_NFS_TCP_UDP]},
{'expected_server': fake.NFS_CONFIG_DEFAULT,
'nfs_config': None,
'share_servers': [fake.NFS_CONFIG_DEFAULT],
'nfs_config_supported': False}
)
@ddt.unpack
def test_choose_share_server_compatible_with_share_group(
self, expected_server, nfs_config, share_servers,
nfs_config_supported=True):
self.library.is_nfs_config_supported = nfs_config_supported
self.mock_object(
share_types, "get_share_type_extra_specs",
mock.Mock(return_value=fake.EXTRA_SPEC))
mock_get_nfs_config = self.mock_object(
self.library,
"_get_nfs_config_provisioning_options",
mock.Mock(return_value=nfs_config))
mock_check_extra_spec = self.mock_object(
self.library,
'_check_nfs_config_extra_specs_validity',
mock.Mock())
server = self.library.choose_share_server_compatible_with_share_group(
None, share_servers, fake.SHARE_GROUP_REF)
if nfs_config_supported:
mock_get_nfs_config.assert_called_with(fake.EXTRA_SPEC)
mock_check_extra_spec.assert_called_once_with(fake.EXTRA_SPEC)
else:
mock_get_nfs_config.assert_not_called()
mock_check_extra_spec.assert_not_called()
self.assertEqual(expected_server, server)

View File

@ -14,6 +14,7 @@
# under the License.
import copy
from oslo_serialization import jsonutils
from manila.common import constants
import manila.tests.share.drivers.netapp.fakes as na_fakes
@ -173,6 +174,64 @@ EXTRA_SPEC = {
'netapp_disk_type': 'FCAL',
'netapp_raid_type': 'raid4',
'netapp_flexvol_encryption': 'true',
'netapp:tcp_max_xfer_size': 100,
'netapp:udp_max_xfer_size': 100,
}
NFS_CONFIG_DEFAULT = {
'tcp-max-xfer-size': 65536,
'udp-max-xfer-size': 32768,
}
NFS_CONFIG_TCP_MAX_DDT = {
'extra_specs': {
'netapp:tcp_max_xfer_size': 100,
},
'expected': {
'tcp-max-xfer-size': 100,
'udp-max-xfer-size': NFS_CONFIG_DEFAULT['udp-max-xfer-size'],
}
}
NFS_CONFIG_UDP_MAX_DDT = {
'extra_specs': {
'netapp:udp_max_xfer_size': 100,
},
'expected': {
'tcp-max-xfer-size': NFS_CONFIG_DEFAULT['tcp-max-xfer-size'],
'udp-max-xfer-size': 100,
}
}
NFS_CONFIG_TCP_UDP_MAX = {
'tcp-max-xfer-size': 100,
'udp-max-xfer-size': 100,
}
NFS_CONFIG_TCP_MAX = {
'tcp-max-xfer-size': 100,
'udp-max-xfer-size': NFS_CONFIG_DEFAULT['udp-max-xfer-size'],
}
NFS_CONFIG_UDP_MAX = {
'tcp-max-xfer-size': NFS_CONFIG_DEFAULT['tcp-max-xfer-size'],
'udp-max-xfer-size': 100,
}
NFS_CONFIG_TCP_UDP_MAX_DDT = {
'extra_specs': {
'netapp:tcp_max_xfer_size': 100,
'netapp:udp_max_xfer_size': 100,
},
'expected': NFS_CONFIG_TCP_UDP_MAX,
}
SHARE_GROUP_REF = {
'share_types': [
{'share_type_id': 'id1'},
{'share_type_id': 'id2'},
{'share_type_id': 'id3'},
],
}
EXTRA_SPEC_WITH_QOS = copy.deepcopy(EXTRA_SPEC)
@ -270,6 +329,14 @@ INVALID_MAX_FILE_EXTRA_SPEC = {
'netapp:max_files': -1,
}
INVALID_TCP_MAX_XFER_SIZE_EXTRA_SPEC = {
'netapp:tcp_max_xfer_size': -1,
}
INVALID_UDP_MAX_XFER_SIZE_EXTRA_SPEC = {
'netapp:udp_max_xfer_size': -1,
}
EMPTY_EXTRA_SPEC = {}
SHARE_TYPE = {
@ -383,6 +450,58 @@ SHARE_SERVER_2 = {
ADMIN_NETWORK_ALLOCATIONS),
}
SHARE_SERVER_NFS_TCP = {
'id': 'fake_nfs_id_tcp',
'backend_details': {
'vserver_name': VSERVER2,
'nfs_config': jsonutils.dumps(NFS_CONFIG_TCP_MAX),
},
}
SHARE_SERVER_NFS_UDP = {
'id': 'fake_nfs_id_udp',
'backend_details': {
'vserver_name': VSERVER2,
'nfs_config': jsonutils.dumps(NFS_CONFIG_UDP_MAX),
},
}
SHARE_SERVER_NFS_TCP_UDP = {
'id': 'fake_nfs_id_tcp_udp',
'backend_details': {
'vserver_name': VSERVER2,
'nfs_config': jsonutils.dumps(NFS_CONFIG_TCP_UDP_MAX),
},
}
SHARE_SERVER_NO_NFS_NONE = {
'id': 'fake_no_nfs_id_none',
'backend_details': {
'vserver_name': VSERVER2,
},
}
SHARE_SERVER_NO_DETAILS = {
'id': 'id_no_datails',
}
SHARE_SERVER_NFS_DEFAULT = {
'id': 'fake_id_nfs_default',
'backend_details': {
'vserver_name': VSERVER2,
'nfs_config': jsonutils.dumps(NFS_CONFIG_DEFAULT),
},
}
SHARE_SERVERS = [
SHARE_SERVER_NFS_TCP,
SHARE_SERVER_NFS_UDP,
SHARE_SERVER_NFS_TCP_UDP,
SHARE_SERVER_NFS_DEFAULT,
SHARE_SERVER_NO_NFS_NONE,
SHARE_SERVER_NO_DETAILS,
]
VSERVER_PEER = [{
'vserver': VSERVER1,
'peer-vserver': VSERVER2,
@ -1205,6 +1324,11 @@ PROCESSOR_INSTANCE_UUIDS = [
]
PROCESSOR_INSTANCE_NAMES = ['processor0', 'processor1']
SERVER_METADATA = {
'share_type_id': 'fake_id',
'host': 'fake_host',
}
PROCESSOR_COUNTERS = [
{
'node-name': 'cluster1-01',

View File

@ -0,0 +1,12 @@
---
features:
- |
For NetApp ONTAP driver, administrators are now able to set share servers
max NFS transfer limits. These limits can be configured by setting the
`netapp:tcp_max_xfer_size` and `netapp:udp_max_xfer_size` extra-specs.
The driver will consider these limits while deciding to create or reuse
share servers. While bringing a share under Manila management, the driver
will check if the share type extra-specs values match the share server
configured NFS limits. This change does not have effect in DHSS=False
environments and relies on ONTAP features available only in versions equal
to and greater than ``9.4``.