Browse Source

Merge "[NetApp] Enables configuring NFS transfer limits"

changes/48/747048/5
Zuul 1 week ago
committed by Gerrit Code Review
parent
commit
1194910cde
11 changed files with 1013 additions and 34 deletions
  1. +54
    -1
      manila/share/drivers/netapp/dataontap/client/client_cmode.py
  2. +15
    -0
      manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py
  3. +10
    -0
      manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py
  4. +28
    -2
      manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py
  5. +200
    -10
      manila/share/drivers/netapp/dataontap/cluster_mode/lib_multi_svm.py
  6. +37
    -0
      manila/tests/share/drivers/netapp/dataontap/client/fakes.py
  7. +90
    -2
      manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py
  8. +11
    -3
      manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py
  9. +432
    -16
      manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py
  10. +124
    -0
      manila/tests/share/drivers/netapp/dataontap/fakes.py
  11. +12
    -0
      releasenotes/notes/netapp-cdot-multi-svm-configure-nfs-95c9154e1aa28751.yaml

+ 54
- 1
manila/share/drivers/netapp/dataontap/client/client_cmode.py 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

+ 15
- 0
manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py 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)

+ 10
- 0
manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py 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

+ 28
- 2
manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py 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(


+ 200
- 10
manila/share/drivers/netapp/dataontap/cluster_mode/lib_multi_svm.py 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))

+ 37
- 0
manila/tests/share/drivers/netapp/dataontap/client/fakes.py 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',


+ 90
- 2
manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py 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)

+ 11
- 3
manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py 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)



+ 432
- 16
manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py 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()

def test_setup_server_with_error(self):
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)

+ 124
- 0
manila/tests/share/drivers/netapp/dataontap/fakes.py 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',


+ 12
- 0
releasenotes/notes/netapp-cdot-multi-svm-configure-nfs-95c9154e1aa28751.yaml 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``.

Loading…
Cancel
Save