diff --git a/manila/share/drivers/netapp/dataontap/client/client_cmode.py b/manila/share/drivers/netapp/dataontap/client/client_cmode.py
index 6f0db79c3b..9d1e5c1504 100644
--- a/manila/share/drivers/netapp/dataontap/client/client_cmode.py
+++ b/manila/share/drivers/netapp/dataontap/client/client_cmode.py
@@ -916,6 +916,12 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
api_args = {'path': '/vol/%s' % volume_name}
self.send_request('sis-enable', api_args)
+ @na_utils.trace
+ def disable_dedup(self, volume_name):
+ """Disable deduplication on volume."""
+ api_args = {'path': '/vol/%s' % volume_name}
+ self.send_request('sis-disable', api_args)
+
@na_utils.trace
def enable_compression(self, volume_name):
"""Enable compression on volume."""
@@ -925,6 +931,45 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
}
self.send_request('sis-set-config', api_args)
+ @na_utils.trace
+ def disable_compression(self, volume_name):
+ """Disable compression on volume."""
+ api_args = {
+ 'path': '/vol/%s' % volume_name,
+ 'enable-compression': 'false'
+ }
+ self.send_request('sis-set-config', api_args)
+
+ @na_utils.trace
+ def get_volume_efficiency_status(self, volume_name):
+ """Get dedupe & compression status for a volume."""
+ api_args = {
+ 'query': {
+ 'sis-status-info': {
+ 'path': '/vol/%s' % volume_name,
+ },
+ },
+ 'desired-attributes': {
+ 'sis-status-info': {
+ 'state': None,
+ 'is-compression-enabled': None,
+ },
+ },
+ }
+ result = self.send_request('sis-get-iter', api_args)
+
+ attributes_list = result.get_child_by_name(
+ 'attributes-list') or netapp_api.NaElement('none')
+ sis_status_info = attributes_list.get_child_by_name(
+ 'sis-status-info') or netapp_api.NaElement('none')
+
+ return {
+ 'dedupe': True if 'enabled' == sis_status_info.get_child_content(
+ 'state') else False,
+ 'compression': True if 'true' == sis_status_info.get_child_content(
+ 'is-compression-enabled') else False,
+ }
+
@na_utils.trace
def set_volume_max_files(self, volume_name, max_files):
"""Set flexvol file limit."""
@@ -976,6 +1021,78 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
errors[0].get_child_content('error-code'),
errors[0].get_child_content('error-message'))
+ @na_utils.trace
+ def set_volume_name(self, volume_name, new_volume_name):
+ """Set flexvol name."""
+ api_args = {
+ 'volume': volume_name,
+ 'new-volume-name': new_volume_name,
+ }
+ self.send_request('volume-rename', api_args)
+
+ @na_utils.trace
+ def manage_volume(self, aggregate_name, volume_name,
+ thin_provisioned=False, snapshot_policy=None,
+ language=None, dedup_enabled=False,
+ compression_enabled=False, max_files=None):
+ """Update volume as needed to bring under management as a share."""
+ api_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': aggregate_name,
+ 'name': volume_name,
+ },
+ },
+ },
+ 'attributes': {
+ 'volume-attributes': {
+ 'volume-inode-attributes': {},
+ 'volume-language-attributes': {},
+ 'volume-snapshot-attributes': {},
+ 'volume-space-attributes': {
+ 'space-guarantee': ('none' if thin_provisioned else
+ 'volume')
+ },
+ },
+ },
+ }
+ if language:
+ api_args['attributes']['volume-attributes'][
+ 'volume-language-attributes']['language'] = language
+ if max_files:
+ api_args['attributes']['volume-attributes'][
+ 'volume-inode-attributes']['files-total'] = max_files
+ if snapshot_policy:
+ api_args['attributes']['volume-attributes'][
+ 'volume-snapshot-attributes'][
+ 'snapshot-policy'] = snapshot_policy
+
+ self.send_request('volume-modify-iter', api_args)
+
+ # Efficiency options must be handled separately
+ self.update_volume_efficiency_attributes(volume_name,
+ dedup_enabled,
+ compression_enabled)
+
+ @na_utils.trace
+ def update_volume_efficiency_attributes(self, volume_name, dedup_enabled,
+ compression_enabled):
+ """Update dedupe & compression attributes to match desired values."""
+ efficiency_status = self.get_volume_efficiency_status(volume_name)
+
+ if efficiency_status['compression'] != compression_enabled:
+ if compression_enabled:
+ self.enable_compression(volume_name)
+ else:
+ self.disable_compression(volume_name)
+
+ if efficiency_status['dedupe'] != dedup_enabled:
+ if dedup_enabled:
+ self.enable_dedup(volume_name)
+ else:
+ self.disable_dedup(volume_name)
+
@na_utils.trace
def volume_exists(self, volume_name):
"""Checks if volume exists."""
@@ -1039,6 +1156,159 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
return aggregate
+ @na_utils.trace
+ def volume_has_luns(self, volume_name):
+ """Checks if volume has LUNs."""
+ LOG.debug('Checking if volume %s has LUNs', volume_name)
+
+ api_args = {
+ 'query': {
+ 'lun-info': {
+ 'volume': volume_name,
+ },
+ },
+ 'desired-attributes': {
+ 'lun-info': {
+ 'path': None,
+ },
+ },
+ }
+ result = self.send_request('lun-get-iter', api_args)
+ return self._has_records(result)
+
+ @na_utils.trace
+ def volume_has_junctioned_volumes(self, volume_name):
+ """Checks if volume has volumes mounted beneath its junction path."""
+ junction_path = self.get_volume_junction_path(volume_name)
+ if not junction_path:
+ return False
+
+ api_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'junction-path': junction_path + '/*',
+ },
+ },
+ },
+ 'desired-attributes': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'name': None,
+ },
+ },
+ },
+ }
+ result = self.send_request('volume-get-iter', api_args)
+ return self._has_records(result)
+
+ @na_utils.trace
+ def get_volume_at_junction_path(self, junction_path):
+ """Returns the volume with the specified junction path, if present."""
+ if not junction_path:
+ return None
+
+ api_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'junction-path': junction_path,
+ },
+ },
+ },
+ 'desired-attributes': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': None,
+ 'junction-path': None,
+ 'name': None,
+ 'type': None,
+ 'style': None,
+ },
+ 'volume-space-attributes': {
+ 'size': None,
+ }
+ },
+ },
+ }
+ result = self.send_request('volume-get-iter', api_args)
+ if not self._has_records(result):
+ return None
+
+ attributes_list = result.get_child_by_name(
+ 'attributes-list') or netapp_api.NaElement('none')
+ volume_attributes = attributes_list.get_child_by_name(
+ 'volume-attributes') or netapp_api.NaElement('none')
+ volume_id_attributes = volume_attributes.get_child_by_name(
+ 'volume-id-attributes') or netapp_api.NaElement('none')
+ volume_space_attributes = volume_attributes.get_child_by_name(
+ 'volume-space-attributes') or netapp_api.NaElement('none')
+
+ volume = {
+ 'aggregate': volume_id_attributes.get_child_content(
+ 'containing-aggregate-name'),
+ 'junction-path': volume_id_attributes.get_child_content(
+ 'junction-path'),
+ 'name': volume_id_attributes.get_child_content('name'),
+ 'type': volume_id_attributes.get_child_content('type'),
+ 'style': volume_id_attributes.get_child_content('style'),
+ 'size': volume_space_attributes.get_child_content('size'),
+ }
+ return volume
+
+ @na_utils.trace
+ def get_volume_to_manage(self, aggregate_name, volume_name):
+ """Get flexvol to be managed by Manila."""
+
+ api_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': aggregate_name,
+ 'name': volume_name,
+ },
+ },
+ },
+ 'desired-attributes': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': None,
+ 'junction-path': None,
+ 'name': None,
+ 'type': None,
+ 'style': None,
+ },
+ 'volume-space-attributes': {
+ 'size': None,
+ }
+ },
+ },
+ }
+ result = self.send_request('volume-get-iter', api_args)
+ if not self._has_records(result):
+ return None
+
+ attributes_list = result.get_child_by_name(
+ 'attributes-list') or netapp_api.NaElement('none')
+ volume_attributes = attributes_list.get_child_by_name(
+ 'volume-attributes') or netapp_api.NaElement('none')
+ volume_id_attributes = volume_attributes.get_child_by_name(
+ 'volume-id-attributes') or netapp_api.NaElement('none')
+ volume_space_attributes = volume_attributes.get_child_by_name(
+ 'volume-space-attributes') or netapp_api.NaElement('none')
+
+ volume = {
+ 'aggregate': volume_id_attributes.get_child_content(
+ 'containing-aggregate-name'),
+ 'junction-path': volume_id_attributes.get_child_content(
+ 'junction-path'),
+ 'name': volume_id_attributes.get_child_content('name'),
+ 'type': volume_id_attributes.get_child_content('type'),
+ 'style': volume_id_attributes.get_child_content('style'),
+ 'size': volume_space_attributes.get_child_content('size'),
+ }
+ return volume
+
@na_utils.trace
def create_volume_clone(self, volume_name, parent_volume_name,
parent_snapshot_name=None):
@@ -1067,6 +1337,16 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
result = self.send_request('volume-get-volume-path', api_args)
return result.get_child_content('junction')
+ @na_utils.trace
+ def mount_volume(self, volume_name, junction_path=None):
+ """Mounts a volume on a junction path."""
+ api_args = {
+ 'volume-name': volume_name,
+ 'junction-path': (junction_path if junction_path
+ else '/%s' % volume_name)
+ }
+ self.send_request('volume-mount', api_args)
+
@na_utils.trace
def offline_volume(self, volume_name):
"""Offlines a volume."""
diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py
index b4730d8084..bce85b2c6e 100644
--- a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py
+++ b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_multi_svm.py
@@ -70,6 +70,12 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
def ensure_share(self, context, share, **kwargs):
pass
+ def manage_existing(self, share, driver_options):
+ raise NotImplementedError
+
+ def unmanage(self, share):
+ raise NotImplementedError
+
def allow_access(self, context, share, access, **kwargs):
self.library.allow_access(context, share, access, **kwargs)
diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py
index 418d9d48db..c9531e0c97 100644
--- a/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py
+++ b/manila/share/drivers/netapp/dataontap/cluster_mode/drv_single_svm.py
@@ -70,6 +70,12 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
def ensure_share(self, context, share, **kwargs):
pass
+ def manage_existing(self, share, driver_options):
+ return self.library.manage_existing(share, driver_options)
+
+ def unmanage(self, share):
+ self.library.unmanage(share)
+
def allow_access(self, context, share, access, **kwargs):
self.library.allow_access(context, share, access, **kwargs)
diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py
index fb06418ed9..cdb70f4aeb 100644
--- a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py
+++ b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py
@@ -20,6 +20,7 @@ single-SVM or multi-SVM functionality needed by the cDOT Manila drivers.
"""
import copy
+import math
import socket
import time
@@ -66,6 +67,7 @@ class NetAppCmodeFileStorageLibrary(object):
self.driver_name = driver_name
+ self.private_storage = kwargs['private_storage']
self.configuration = kwargs['configuration']
self.configuration.append_config_values(na_opts.netapp_connection_opts)
self.configuration.append_config_values(na_opts.netapp_basicauth_opts)
@@ -483,6 +485,23 @@ class NetAppCmodeFileStorageLibrary(object):
result.update(string_args)
return result
+ @na_utils.trace
+ def _check_aggregate_extra_specs_validity(self, aggregate_name, specs):
+
+ for specs_key in ('netapp_disk_type', 'netapp_raid_type'):
+ aggr_value = self._ssc_stats.get(aggregate_name, {}).get(specs_key)
+ specs_value = specs.get(specs_key)
+
+ if aggr_value and specs_value and aggr_value != specs_value:
+ msg = _('Invalid value "%(value)s" for extra_spec "%(key)s" '
+ 'in aggregate %(aggr)s.')
+ msg_args = {
+ 'value': specs_value,
+ 'key': specs_key,
+ 'aggr': aggregate_name
+ }
+ raise exception.NetAppException(msg % msg_args)
+
@na_utils.trace
def _allocate_container_from_snapshot(self, share, snapshot,
vserver_client):
@@ -631,6 +650,103 @@ class NetAppCmodeFileStorageLibrary(object):
raise exception.ShareSnapshotIsBusy(snapshot_name=snapshot_name)
+ @na_utils.trace
+ def manage_existing(self, share, driver_options):
+ vserver, vserver_client = self._get_vserver(share_server=None)
+ share_size = self._manage_container(share, vserver_client)
+ export_locations = self._create_export(share, vserver, vserver_client)
+ return {'size': share_size, 'export_locations': export_locations}
+
+ @na_utils.trace
+ def unmanage(self, share):
+ pass
+
+ @na_utils.trace
+ def _manage_container(self, share, vserver_client):
+ """Bring existing volume under management as a share."""
+
+ protocol_helper = self._get_helper(share)
+ protocol_helper.set_client(vserver_client)
+
+ volume_name = protocol_helper.get_share_name_for_share(share)
+ if not volume_name:
+ msg = _('Volume could not be determined from export location '
+ '%(export)s.')
+ msg_args = {'export': share['export_location']}
+ raise exception.ManageInvalidShare(reason=msg % msg_args)
+
+ share_name = self._get_valid_share_name(share['id'])
+ aggregate_name = share_utils.extract_host(share['host'], level='pool')
+
+ # Get existing volume info
+ volume = vserver_client.get_volume_to_manage(aggregate_name,
+ volume_name)
+ if not volume:
+ msg = _('Volume %(volume)s not found on aggregate %(aggr)s.')
+ msg_args = {'volume': volume_name, 'aggr': aggregate_name}
+ raise exception.ManageInvalidShare(reason=msg % msg_args)
+
+ # Ensure volume is manageable
+ self._validate_volume_for_manage(volume, vserver_client)
+
+ # Validate extra specs
+ extra_specs = share_types.get_extra_specs_from_share(share)
+ try:
+ self._check_extra_specs_validity(share, extra_specs)
+ self._check_aggregate_extra_specs_validity(aggregate_name,
+ extra_specs)
+ except exception.ManilaException as ex:
+ raise exception.ManageExistingShareTypeMismatch(
+ reason=six.text_type(ex))
+ provisioning_options = self._get_provisioning_options(extra_specs)
+
+ debug_args = {
+ 'share': share_name,
+ 'aggr': aggregate_name,
+ 'options': provisioning_options
+ }
+ LOG.debug('Managing share %(share)s on aggregate %(aggr)s with '
+ 'provisioning options %(options)s', debug_args)
+
+ # Rename & remount volume on new path
+ vserver_client.unmount_volume(volume_name)
+ vserver_client.set_volume_name(volume_name, share_name)
+ vserver_client.mount_volume(share_name)
+
+ # Modify volume to match extra specs
+ vserver_client.manage_volume(aggregate_name, share_name,
+ **provisioning_options)
+
+ # Save original volume info to private storage
+ original_data = {
+ 'original_name': volume['name'],
+ 'original_junction_path': volume['junction-path']
+ }
+ self.private_storage.update(share['id'], original_data)
+
+ # When calculating the size, round up to the next GB.
+ return int(math.ceil(float(volume['size']) / units.Gi))
+
+ @na_utils.trace
+ def _validate_volume_for_manage(self, volume, vserver_client):
+ """Ensure volume is a candidate for becoming a share."""
+
+ # Check volume info, extra specs validity
+ if volume['type'] != 'rw' or volume['style'] != 'flex':
+ msg = _('Volume %(volume)s must be a read-write flexible volume.')
+ msg_args = {'volume': volume['name']}
+ raise exception.ManageInvalidShare(reason=msg % msg_args)
+
+ if vserver_client.volume_has_luns(volume['name']):
+ msg = _('Volume %(volume)s must not contain LUNs.')
+ msg_args = {'volume': volume['name']}
+ raise exception.ManageInvalidShare(reason=msg % msg_args)
+
+ if vserver_client.volume_has_junctioned_volumes(volume['name']):
+ msg = _('Volume %(volume)s must not have junctioned volumes.')
+ msg_args = {'volume': volume['name']}
+ raise exception.ManageInvalidShare(reason=msg % msg_args)
+
@na_utils.trace
def extend_share(self, share, new_size, share_server=None):
"""Extends size of existing share."""
diff --git a/manila/share/drivers/netapp/dataontap/protocols/base.py b/manila/share/drivers/netapp/dataontap/protocols/base.py
index ac7e549505..c9fbd568f0 100644
--- a/manila/share/drivers/netapp/dataontap/protocols/base.py
+++ b/manila/share/drivers/netapp/dataontap/protocols/base.py
@@ -47,3 +47,7 @@ class NetAppBaseHelper(object):
@abc.abstractmethod
def get_target(self, share):
"""Returns host where the share located."""
+
+ @abc.abstractmethod
+ def get_share_name_for_share(self, share):
+ """Returns the flexvol name that hosts a share."""
\ No newline at end of file
diff --git a/manila/share/drivers/netapp/dataontap/protocols/cifs_cmode.py b/manila/share/drivers/netapp/dataontap/protocols/cifs_cmode.py
index 14e016c693..8f4b8e6a9e 100644
--- a/manila/share/drivers/netapp/dataontap/protocols/cifs_cmode.py
+++ b/manila/share/drivers/netapp/dataontap/protocols/cifs_cmode.py
@@ -88,6 +88,12 @@ class NetAppCmodeCIFSHelper(base.NetAppBaseHelper):
"""Returns OnTap target IP based on share export location."""
return self._get_export_location(share)[0]
+ @na_utils.trace
+ def get_share_name_for_share(self, share):
+ """Returns the flexvol name that hosts a share."""
+ _, share_name = self._get_export_location(share)
+ return share_name
+
@staticmethod
def _get_export_location(share):
"""Returns host ip and share name for a given CIFS share."""
diff --git a/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py b/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py
index 55464ae04b..f6c77398c6 100644
--- a/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py
+++ b/manila/share/drivers/netapp/dataontap/protocols/nfs_cmode.py
@@ -84,11 +84,18 @@ class NetAppCmodeNFSHelper(base.NetAppBaseHelper):
"""Returns ID of target OnTap device based on export location."""
return self._get_export_location(share)[0]
+ @na_utils.trace
+ def get_share_name_for_share(self, share):
+ """Returns the flexvol name that hosts a share."""
+ _, volume_junction_path = self._get_export_location(share)
+ volume = self._client.get_volume_at_junction_path(volume_junction_path)
+ return volume.get('name') if volume else None
+
@staticmethod
def _get_export_location(share):
"""Returns IP address and export location of an NFS share."""
export_location = share['export_location'] or ':'
- return export_location.split(':')
+ return export_location.rsplit(':', 1)
@staticmethod
def _get_export_policy_name(share):
diff --git a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py
index 6983917be0..e336fff269 100644
--- a/manila/tests/share/drivers/netapp/dataontap/client/fakes.py
+++ b/manila/tests/share/drivers/netapp/dataontap/client/fakes.py
@@ -35,10 +35,13 @@ SHARE_AGGREGATE_NAMES = ('fake_aggr1', 'fake_aggr2')
SHARE_AGGREGATE_RAID_TYPES = ('raid4', 'raid_dp')
SHARE_AGGREGATE_DISK_TYPE = 'FCAL'
SHARE_NAME = 'fake_share'
+SHARE_SIZE = '1000000000'
SNAPSHOT_NAME = 'fake_snapshot'
PARENT_SHARE_NAME = 'fake_parent_share'
PARENT_SNAPSHOT_NAME = 'fake_parent_snapshot'
MAX_FILES = 5000
+LANGUAGE = 'fake_language'
+SNAPSHOT_POLICY_NAME = 'fake_snapshot_policy'
EXPORT_POLICY_NAME = 'fake_export_policy'
DELETED_EXPORT_POLICIES = {
VSERVER_NAME: [
@@ -1366,3 +1369,76 @@ DELETED_EXPORT_POLICY_GET_ITER_RESPONSE = etree.XML("""
'policy2': DELETED_EXPORT_POLICIES[VSERVER_NAME][1],
'policy3': DELETED_EXPORT_POLICIES[VSERVER_NAME_2][0],
})
+
+LUN_GET_ITER_RESPONSE = etree.XML("""
+
+
+
+ /vol/%(volume)s/fakelun
+
+ %(volume)s
+ %(vserver)s
+
+
+ 1
+
+""" % {
+ 'vserver': VSERVER_NAME,
+ 'volume': SHARE_NAME,
+})
+
+VOLUME_GET_ITER_JUNCTIONED_VOLUMES_RESPONSE = etree.XML("""
+
+
+
+
+ fake_volume
+ test
+
+
+
+ 1
+
+""")
+
+VOLUME_GET_ITER_VOLUME_TO_MANAGE_RESPONSE = etree.XML("""
+
+
+
+
+ %(aggr)s
+ /%(volume)s
+ %(volume)s
+ %(vserver)s
+
+ rw
+
+
+ %(size)s
+
+
+
+ 1
+
+""" % {
+ 'aggr': SHARE_AGGREGATE_NAME,
+ 'vserver': VSERVER_NAME,
+ 'volume': SHARE_NAME,
+ 'size': SHARE_SIZE,
+})
+
+SIS_GET_ITER_RESPONSE = etree.XML("""
+
+
+
+ true
+ /vol/%(volume)s
+ enabled
+ %(vserver)s
+
+
+
+""" % {
+ 'vserver': VSERVER_NAME,
+ 'volume': SHARE_NAME,
+})
diff --git a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py
index 5844576bb7..e6f7956d50 100644
--- a/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py
+++ b/manila/tests/share/drivers/netapp/dataontap/client/test_client_cmode.py
@@ -1668,6 +1668,96 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.enable_dedup.assert_called_once_with(fake.SHARE_NAME)
self.client.enable_compression.assert_called_once_with(fake.SHARE_NAME)
+ def test_enable_dedup(self):
+
+ self.mock_object(self.client, 'send_request')
+
+ self.client.enable_dedup(fake.SHARE_NAME)
+
+ sis_enable_args = {'path': '/vol/%s' % fake.SHARE_NAME}
+
+ self.client.send_request.assert_called_once_with('sis-enable',
+ sis_enable_args)
+
+ def test_disable_dedup(self):
+
+ self.mock_object(self.client, 'send_request')
+
+ self.client.disable_dedup(fake.SHARE_NAME)
+
+ sis_disable_args = {'path': '/vol/%s' % fake.SHARE_NAME}
+
+ self.client.send_request.assert_called_once_with('sis-disable',
+ sis_disable_args)
+
+ def test_enable_compression(self):
+
+ self.mock_object(self.client, 'send_request')
+
+ self.client.enable_compression(fake.SHARE_NAME)
+
+ sis_set_config_args = {
+ 'path': '/vol/%s' % fake.SHARE_NAME,
+ 'enable-compression': 'true'
+ }
+
+ self.client.send_request.assert_called_once_with('sis-set-config',
+ sis_set_config_args)
+
+ def test_disable_compression(self):
+
+ self.mock_object(self.client, 'send_request')
+
+ self.client.disable_compression(fake.SHARE_NAME)
+
+ sis_set_config_args = {
+ 'path': '/vol/%s' % fake.SHARE_NAME,
+ 'enable-compression': 'false'
+ }
+
+ self.client.send_request.assert_called_once_with('sis-set-config',
+ sis_set_config_args)
+
+ def test_get_volume_efficiency_status(self):
+
+ api_response = netapp_api.NaElement(fake.SIS_GET_ITER_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ result = self.client.get_volume_efficiency_status(fake.SHARE_NAME)
+
+ sis_get_iter_args = {
+ 'query': {
+ 'sis-status-info': {
+ 'path': '/vol/%s' % fake.SHARE_NAME,
+ },
+ },
+ 'desired-attributes': {
+ 'sis-status-info': {
+ 'state': None,
+ 'is-compression-enabled': None,
+ },
+ },
+ }
+ self.client.send_request.assert_has_calls([
+ mock.call('sis-get-iter', sis_get_iter_args)])
+
+ expected = {'dedupe': True, 'compression': True}
+ self.assertDictEqual(expected, result)
+
+ def test_get_volume_efficiency_status_not_found(self):
+
+ api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ result = self.client.get_volume_efficiency_status(fake.SHARE_NAME)
+
+ expected = {'dedupe': False, 'compression': False}
+ self.assertDictEqual(expected, result)
+
def test_set_volume_max_files(self):
self.mock_object(self.client, 'send_request')
@@ -1694,30 +1784,154 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.send_request.assert_called_once_with(
'volume-modify-iter', volume_modify_iter_api_args)
- def test_enable_dedup(self):
+ def test_set_volume_name(self):
self.mock_object(self.client, 'send_request')
- self.client.enable_dedup(fake.SHARE_NAME)
+ self.client.set_volume_name(fake.SHARE_NAME, 'new_name')
- sis_enable_args = {'path': '/vol/%s' % fake.SHARE_NAME}
-
- self.client.send_request.assert_called_once_with('sis-enable',
- sis_enable_args)
-
- def test_enable_compression(self):
-
- self.mock_object(self.client, 'send_request')
-
- self.client.enable_compression(fake.SHARE_NAME)
-
- sis_set_config_args = {
- 'path': '/vol/%s' % fake.SHARE_NAME,
- 'enable-compression': 'true'
+ volume_rename_api_args = {
+ 'volume': fake.SHARE_NAME,
+ 'new-volume-name': 'new_name',
}
- self.client.send_request.assert_called_once_with('sis-set-config',
- sis_set_config_args)
+ self.client.send_request.assert_called_once_with(
+ 'volume-rename', volume_rename_api_args)
+
+ def test_manage_volume_no_optional_args(self):
+
+ self.mock_object(self.client, 'send_request')
+ mock_update_volume_efficiency_attributes = self.mock_object(
+ self.client, 'update_volume_efficiency_attributes')
+
+ self.client.manage_volume(fake.SHARE_AGGREGATE_NAME, fake.SHARE_NAME)
+
+ volume_modify_iter_api_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': fake.SHARE_AGGREGATE_NAME,
+ 'name': fake.SHARE_NAME,
+ },
+ },
+ },
+ 'attributes': {
+ 'volume-attributes': {
+ 'volume-inode-attributes': {},
+ 'volume-language-attributes': {},
+ 'volume-snapshot-attributes': {},
+ 'volume-space-attributes': {
+ 'space-guarantee': 'volume',
+ },
+ },
+ },
+ }
+
+ self.client.send_request.assert_called_once_with(
+ 'volume-modify-iter', volume_modify_iter_api_args)
+ mock_update_volume_efficiency_attributes.assert_called_once_with(
+ fake.SHARE_NAME, False, False)
+
+ def test_manage_volume_all_optional_args(self):
+
+ self.mock_object(self.client, 'send_request')
+ mock_update_volume_efficiency_attributes = self.mock_object(
+ self.client, 'update_volume_efficiency_attributes')
+
+ self.client.manage_volume(fake.SHARE_AGGREGATE_NAME,
+ fake.SHARE_NAME,
+ thin_provisioned=True,
+ snapshot_policy=fake.SNAPSHOT_POLICY_NAME,
+ language=fake.LANGUAGE,
+ dedup_enabled=True,
+ compression_enabled=False,
+ max_files=fake.MAX_FILES)
+
+ volume_modify_iter_api_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': fake.SHARE_AGGREGATE_NAME,
+ 'name': fake.SHARE_NAME,
+ },
+ },
+ },
+ 'attributes': {
+ 'volume-attributes': {
+ 'volume-inode-attributes': {
+ 'files-total': fake.MAX_FILES,
+ },
+ 'volume-language-attributes': {
+ 'language': fake.LANGUAGE,
+ },
+ 'volume-snapshot-attributes': {
+ 'snapshot-policy': fake.SNAPSHOT_POLICY_NAME,
+ },
+ 'volume-space-attributes': {
+ 'space-guarantee': 'none',
+ },
+ },
+ },
+ }
+
+ self.client.send_request.assert_called_once_with(
+ 'volume-modify-iter', volume_modify_iter_api_args)
+ mock_update_volume_efficiency_attributes.assert_called_once_with(
+ fake.SHARE_NAME, True, False)
+
+ @ddt.data(
+ {'existing': (True, True), 'desired': (True, True)},
+ {'existing': (True, True), 'desired': (False, False)},
+ {'existing': (True, True), 'desired': (True, False)},
+ {'existing': (True, False), 'desired': (True, False)},
+ {'existing': (True, False), 'desired': (False, False)},
+ {'existing': (True, False), 'desired': (True, True)},
+ {'existing': (False, False), 'desired': (False, False)},
+ {'existing': (False, False), 'desired': (True, False)},
+ {'existing': (False, False), 'desired': (True, True)},
+ )
+ @ddt.unpack
+ def test_update_volume_efficiency_attributes(self, existing, desired):
+
+ existing_dedupe = existing[0]
+ existing_compression = existing[1]
+ desired_dedupe = desired[0]
+ desired_compression = desired[1]
+
+ self.mock_object(
+ self.client,
+ 'get_volume_efficiency_status',
+ mock.Mock(return_value={'dedupe': existing_dedupe,
+ 'compression': existing_compression}))
+ mock_enable_compression = self.mock_object(self.client,
+ 'enable_compression')
+ mock_disable_compression = self.mock_object(self.client,
+ 'disable_compression')
+ mock_enable_dedup = self.mock_object(self.client, 'enable_dedup')
+ mock_disable_dedup = self.mock_object(self.client, 'disable_dedup')
+
+ self.client.update_volume_efficiency_attributes(
+ fake.SHARE_NAME, desired_dedupe, desired_compression)
+
+ if existing_dedupe == desired_dedupe:
+ self.assertFalse(mock_enable_dedup.called)
+ self.assertFalse(mock_disable_dedup.called)
+ elif existing_dedupe and not desired_dedupe:
+ self.assertFalse(mock_enable_dedup.called)
+ self.assertTrue(mock_disable_dedup.called)
+ elif not existing_dedupe and desired_dedupe:
+ self.assertTrue(mock_enable_dedup.called)
+ self.assertFalse(mock_disable_dedup.called)
+
+ if existing_compression == desired_compression:
+ self.assertFalse(mock_enable_compression.called)
+ self.assertFalse(mock_disable_compression.called)
+ elif existing_compression and not desired_compression:
+ self.assertFalse(mock_enable_compression.called)
+ self.assertTrue(mock_disable_compression.called)
+ elif not existing_compression and desired_compression:
+ self.assertTrue(mock_enable_compression.called)
+ self.assertFalse(mock_disable_compression.called)
def test_set_volume_size(self):
@@ -1842,6 +2056,227 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.client.get_aggregate_for_volume,
fake.SHARE_NAME)
+ def test_volume_has_luns(self):
+
+ api_response = netapp_api.NaElement(fake.LUN_GET_ITER_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ result = self.client.volume_has_luns(fake.SHARE_NAME)
+
+ lun_get_iter_args = {
+ 'query': {
+ 'lun-info': {
+ 'volume': fake.SHARE_NAME,
+ },
+ },
+ 'desired-attributes': {
+ 'lun-info': {
+ 'path': None,
+ },
+ },
+ }
+
+ self.client.send_request.assert_has_calls([
+ mock.call('lun-get-iter', lun_get_iter_args)])
+ self.assertTrue(result)
+
+ def test_volume_has_luns_not_found(self):
+
+ api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ result = self.client.volume_has_luns(fake.SHARE_NAME)
+
+ self.assertFalse(result)
+
+ def test_volume_has_junctioned_volumes(self):
+
+ api_response = netapp_api.NaElement(
+ fake.VOLUME_GET_ITER_JUNCTIONED_VOLUMES_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ fake_junction_path = '/%s' % fake.SHARE_NAME
+ self.mock_object(self.client,
+ 'get_volume_junction_path',
+ mock.Mock(return_value=fake_junction_path))
+
+ result = self.client.volume_has_junctioned_volumes(fake.SHARE_NAME)
+
+ volume_get_iter_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'junction-path': fake_junction_path + '/*',
+ },
+ },
+ },
+ 'desired-attributes': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'name': None,
+ },
+ },
+ },
+ }
+ self.client.send_request.assert_has_calls([
+ mock.call('volume-get-iter', volume_get_iter_args)])
+ self.assertTrue(result)
+
+ def test_volume_has_junctioned_volumes_no_junction_path(self):
+
+ self.mock_object(self.client,
+ 'get_volume_junction_path',
+ mock.Mock(return_value=''))
+
+ result = self.client.volume_has_junctioned_volumes(fake.SHARE_NAME)
+
+ self.assertFalse(result)
+
+ def test_volume_has_junctioned_volumes_not_found(self):
+
+ api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ fake_junction_path = '/%s' % fake.SHARE_NAME
+ self.mock_object(self.client,
+ 'get_volume_junction_path',
+ mock.Mock(return_value=fake_junction_path))
+
+ result = self.client.volume_has_junctioned_volumes(fake.SHARE_NAME)
+
+ self.assertFalse(result)
+
+ def test_get_volume_at_junction_path(self):
+
+ api_response = netapp_api.NaElement(
+ fake.VOLUME_GET_ITER_VOLUME_TO_MANAGE_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+ fake_junction_path = '/%s' % fake.SHARE_NAME
+
+ result = self.client.get_volume_at_junction_path(fake_junction_path)
+
+ volume_get_iter_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'junction-path': fake_junction_path,
+ },
+ },
+ },
+ 'desired-attributes': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': None,
+ 'junction-path': None,
+ 'name': None,
+ 'type': None,
+ 'style': None,
+ },
+ 'volume-space-attributes': {
+ 'size': None,
+ }
+ },
+ },
+ }
+ expected = {
+ 'aggregate': fake.SHARE_AGGREGATE_NAME,
+ 'junction-path': fake_junction_path,
+ 'name': fake.SHARE_NAME,
+ 'type': 'rw',
+ 'style': 'flex',
+ 'size': fake.SHARE_SIZE,
+ }
+ self.client.send_request.assert_has_calls([
+ mock.call('volume-get-iter', volume_get_iter_args)])
+ self.assertDictEqual(expected, result)
+
+ def test_get_volume_at_junction_path_not_specified(self):
+
+ result = self.client.get_volume_at_junction_path(None)
+
+ self.assertIsNone(result)
+
+ def test_get_volume_at_junction_path_not_found(self):
+
+ api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+ fake_junction_path = '/%s' % fake.SHARE_NAME
+
+ result = self.client.get_volume_at_junction_path(fake_junction_path)
+
+ self.assertIsNone(result)
+
+ def test_get_volume_to_manage(self):
+
+ api_response = netapp_api.NaElement(
+ fake.VOLUME_GET_ITER_VOLUME_TO_MANAGE_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ result = self.client.get_volume_to_manage(fake.SHARE_AGGREGATE_NAME,
+ fake.SHARE_NAME)
+
+ volume_get_iter_args = {
+ 'query': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': fake.SHARE_AGGREGATE_NAME,
+ 'name': fake.SHARE_NAME,
+ },
+ },
+ },
+ 'desired-attributes': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': None,
+ 'junction-path': None,
+ 'name': None,
+ 'type': None,
+ 'style': None,
+ },
+ 'volume-space-attributes': {
+ 'size': None,
+ }
+ },
+ },
+ }
+ expected = {
+ 'aggregate': fake.SHARE_AGGREGATE_NAME,
+ 'junction-path': '/%s' % fake.SHARE_NAME,
+ 'name': fake.SHARE_NAME,
+ 'type': 'rw',
+ 'style': 'flex',
+ 'size': fake.SHARE_SIZE,
+ }
+ self.client.send_request.assert_has_calls([
+ mock.call('volume-get-iter', volume_get_iter_args)])
+ self.assertDictEqual(expected, result)
+
+ def test_get_volume_to_manage_not_found(self):
+
+ api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
+ self.mock_object(self.client,
+ 'send_request',
+ mock.Mock(return_value=api_response))
+
+ result = self.client.get_volume_to_manage(fake.SHARE_AGGREGATE_NAME,
+ fake.SHARE_NAME)
+
+ self.assertIsNone(result)
+
def test_create_volume_clone(self):
self.mock_object(self.client, 'send_request')
@@ -1910,6 +2345,35 @@ class NetAppClientCmodeTestCase(test.TestCase):
mock.call('volume-get-volume-path', volume_get_volume_path_args)])
self.assertEqual(fake.VOLUME_JUNCTION_PATH_CIFS, result)
+ def test_mount_volume_default_junction_path(self):
+
+ self.mock_object(self.client, 'send_request')
+
+ self.client.mount_volume(fake.SHARE_NAME)
+
+ volume_mount_args = {
+ 'volume-name': fake.SHARE_NAME,
+ 'junction-path': '/%s' % fake.SHARE_NAME,
+ }
+
+ self.client.send_request.assert_has_calls([
+ mock.call('volume-mount', volume_mount_args)])
+
+ def test_mount_volume(self):
+
+ self.mock_object(self.client, 'send_request')
+ fake_path = '/fake_path'
+
+ self.client.mount_volume(fake.SHARE_NAME, junction_path=fake_path)
+
+ volume_mount_args = {
+ 'volume-name': fake.SHARE_NAME,
+ 'junction-path': fake_path,
+ }
+
+ self.client.send_request.assert_has_calls([
+ mock.call('volume-mount', volume_mount_args)])
+
def test_offline_volume(self):
self.mock_object(self.client, 'send_request')
diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py
index c5ca8415fa..7942136d64 100644
--- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py
+++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py
@@ -17,6 +17,7 @@ Unit tests for the NetApp Data ONTAP cDOT base storage driver library.
"""
import copy
+import math
import socket
import time
@@ -24,6 +25,7 @@ import ddt
import mock
from oslo_log import log
from oslo_service import loopingcall
+from oslo_utils import units
from manila import exception
from manila.share.drivers.netapp.dataontap.client import client_cmode
@@ -63,6 +65,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
kwargs = {
'configuration': fake.get_config_cmode(),
+ 'private_storage': mock.Mock(),
'app_version': fake.APP_VERSION
}
self.library = lib_base.NetAppCmodeFileStorageLibrary(fake.DRIVER_NAME,
@@ -730,6 +733,26 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake_share,
vserver_client)
+ def test_check_aggregate_extra_specs_validity(self):
+
+ self.library._have_cluster_creds = True
+ self.library._ssc_stats = fake.SSC_INFO
+
+ result = self.library._check_aggregate_extra_specs_validity(
+ fake.AGGREGATES[0], fake.EXTRA_SPEC)
+
+ self.assertIsNone(result)
+
+ def test_check_aggregate_extra_specs_validity_no_match(self):
+
+ self.library._have_cluster_creds = True
+ self.library._ssc_stats = fake.SSC_INFO
+
+ self.assertRaises(exception.NetAppException,
+ self.library._check_aggregate_extra_specs_validity,
+ fake.AGGREGATES[1],
+ fake.EXTRA_SPEC)
+
def test_allocate_container_from_snapshot(self):
vserver_client = mock.Mock()
@@ -1086,6 +1109,231 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_sleep.assert_has_calls([mock.call(3)] * 20)
self.assertEqual(20, lib_base.LOG.debug.call_count)
+ def test_manage_existing(self):
+
+ vserver_client = mock.Mock()
+ self.mock_object(self.library,
+ '_get_vserver',
+ mock.Mock(return_value=(fake.VSERVER1,
+ vserver_client)))
+ mock_manage_container = self.mock_object(
+ self.library,
+ '_manage_container',
+ mock.Mock(return_value=fake.SHARE_SIZE))
+ mock_create_export = self.mock_object(
+ self.library,
+ '_create_export',
+ mock.Mock(return_value=fake.NFS_EXPORTS))
+
+ result = self.library.manage_existing(fake.SHARE, {})
+
+ expected = {
+ 'size': fake.SHARE_SIZE,
+ 'export_locations': fake.NFS_EXPORTS
+ }
+ mock_manage_container.assert_called_once_with(fake.SHARE,
+ vserver_client)
+ mock_create_export.assert_called_once_with(fake.SHARE,
+ fake.VSERVER1,
+ vserver_client)
+ self.assertDictEqual(expected, result)
+
+ def test_unmanage(self):
+
+ result = self.library.unmanage(fake.SHARE)
+
+ self.assertIsNone(result)
+
+ def test_manage_container(self):
+
+ vserver_client = mock.Mock()
+
+ share_to_manage = copy.deepcopy(fake.SHARE)
+ share_to_manage['export_location'] = fake.EXPORT_LOCATION
+
+ mock_helper = mock.Mock()
+ mock_helper.get_share_name_for_share.return_value = fake.FLEXVOL_NAME
+ self.mock_object(self.library,
+ '_get_helper',
+ mock.Mock(return_value=mock_helper))
+
+ mock_get_volume_to_manage = self.mock_object(
+ vserver_client,
+ 'get_volume_to_manage',
+ mock.Mock(return_value=fake.FLEXVOL_TO_MANAGE))
+ mock_validate_volume_for_manage = self.mock_object(
+ self.library,
+ '_validate_volume_for_manage')
+ self.mock_object(share_types,
+ 'get_extra_specs_from_share',
+ mock.Mock(return_value=fake.EXTRA_SPEC))
+ mock_check_extra_specs_validity = self.mock_object(
+ self.library,
+ '_check_extra_specs_validity')
+ mock_check_aggregate_extra_specs_validity = self.mock_object(
+ self.library,
+ '_check_aggregate_extra_specs_validity')
+
+ result = self.library._manage_container(share_to_manage,
+ vserver_client)
+
+ mock_get_volume_to_manage.assert_called_once_with(
+ fake.POOL_NAME, fake.FLEXVOL_NAME)
+ mock_validate_volume_for_manage.assert_called_once_with(
+ fake.FLEXVOL_TO_MANAGE, vserver_client)
+ mock_check_extra_specs_validity.assert_called_once_with(
+ share_to_manage, fake.EXTRA_SPEC)
+ mock_check_aggregate_extra_specs_validity.assert_called_once_with(
+ fake.POOL_NAME, fake.EXTRA_SPEC)
+ vserver_client.unmount_volume.assert_called_once_with(
+ fake.FLEXVOL_NAME)
+ vserver_client.set_volume_name.assert_called_once_with(
+ fake.FLEXVOL_NAME, fake.SHARE_NAME)
+ vserver_client.mount_volume.assert_called_once_with(
+ fake.SHARE_NAME)
+ vserver_client.manage_volume.assert_called_once_with(
+ fake.POOL_NAME, fake.SHARE_NAME,
+ **self.library._get_provisioning_options(fake.EXTRA_SPEC))
+
+ original_data = {
+ 'original_name': fake.FLEXVOL_TO_MANAGE['name'],
+ 'original_junction_path': fake.FLEXVOL_TO_MANAGE['junction-path'],
+ }
+ self.library.private_storage.update.assert_called_once_with(
+ fake.SHARE['id'], original_data)
+
+ expected_size = int(
+ math.ceil(float(fake.FLEXVOL_TO_MANAGE['size']) / units.Gi))
+ self.assertEqual(expected_size, result)
+
+ def test_manage_container_invalid_export_location(self):
+
+ vserver_client = mock.Mock()
+
+ share_to_manage = copy.deepcopy(fake.SHARE)
+ share_to_manage['export_location'] = fake.EXPORT_LOCATION
+
+ mock_helper = mock.Mock()
+ mock_helper.get_share_name_for_share.return_value = None
+ self.mock_object(self.library,
+ '_get_helper',
+ mock.Mock(return_value=mock_helper))
+
+ self.assertRaises(exception.ManageInvalidShare,
+ self.library._manage_container,
+ share_to_manage,
+ vserver_client)
+
+ def test_manage_container_not_found(self):
+
+ vserver_client = mock.Mock()
+
+ share_to_manage = copy.deepcopy(fake.SHARE)
+ share_to_manage['export_location'] = fake.EXPORT_LOCATION
+
+ mock_helper = mock.Mock()
+ mock_helper.get_share_name_for_share.return_value = fake.FLEXVOL_NAME
+ self.mock_object(self.library,
+ '_get_helper',
+ mock.Mock(return_value=mock_helper))
+
+ self.mock_object(vserver_client,
+ 'get_volume_to_manage',
+ mock.Mock(return_value=None))
+
+ self.assertRaises(exception.ManageInvalidShare,
+ self.library._manage_container,
+ share_to_manage,
+ vserver_client)
+
+ def test_manage_container_invalid_extra_specs(self):
+
+ vserver_client = mock.Mock()
+
+ share_to_manage = copy.deepcopy(fake.SHARE)
+ share_to_manage['export_location'] = fake.EXPORT_LOCATION
+
+ mock_helper = mock.Mock()
+ mock_helper.get_share_name_for_share.return_value = fake.FLEXVOL_NAME
+ self.mock_object(self.library,
+ '_get_helper',
+ mock.Mock(return_value=mock_helper))
+
+ self.mock_object(vserver_client,
+ 'get_volume_to_manage',
+ mock.Mock(return_value=fake.FLEXVOL_TO_MANAGE))
+ self.mock_object(self.library, '_validate_volume_for_manage')
+ self.mock_object(share_types,
+ 'get_extra_specs_from_share',
+ mock.Mock(return_value=fake.EXTRA_SPEC))
+ self.mock_object(self.library,
+ '_check_extra_specs_validity',
+ mock.Mock(side_effect=exception.NetAppException))
+
+ self.assertRaises(exception.ManageExistingShareTypeMismatch,
+ self.library._manage_container,
+ share_to_manage,
+ vserver_client)
+
+ def test_validate_volume_for_manage(self):
+
+ vserver_client = mock.Mock()
+ vserver_client.volume_has_luns = mock.Mock(return_value=False)
+ vserver_client.volume_has_junctioned_volumes = mock.Mock(
+ return_value=False)
+
+ result = self.library._validate_volume_for_manage(
+ fake.FLEXVOL_TO_MANAGE, vserver_client)
+
+ self.assertIsNone(result)
+
+ @ddt.data({
+ 'attribute': 'type',
+ 'value': 'dp',
+ }, {
+ 'attribute': 'style',
+ 'value': 'infinitevol',
+ })
+ @ddt.unpack
+ def test_validate_volume_for_manage_invalid_volume(self, attribute, value):
+
+ flexvol_to_manage = copy.deepcopy(fake.FLEXVOL_TO_MANAGE)
+ flexvol_to_manage[attribute] = value
+
+ vserver_client = mock.Mock()
+ vserver_client.volume_has_luns = mock.Mock(return_value=False)
+ vserver_client.volume_has_junctioned_volumes = mock.Mock(
+ return_value=False)
+
+ self.assertRaises(exception.ManageInvalidShare,
+ self.library._validate_volume_for_manage,
+ flexvol_to_manage,
+ vserver_client)
+
+ def test_validate_volume_for_manage_luns_present(self):
+
+ vserver_client = mock.Mock()
+ vserver_client.volume_has_luns = mock.Mock(return_value=True)
+ vserver_client.volume_has_junctioned_volumes = mock.Mock(
+ return_value=False)
+
+ self.assertRaises(exception.ManageInvalidShare,
+ self.library._validate_volume_for_manage,
+ fake.FLEXVOL_TO_MANAGE,
+ vserver_client)
+
+ def test_validate_volume_for_manage_junctioned_volumes_present(self):
+
+ vserver_client = mock.Mock()
+ vserver_client.volume_has_luns = mock.Mock(return_value=False)
+ vserver_client.volume_has_junctioned_volumes = mock.Mock(
+ return_value=True)
+
+ self.assertRaises(exception.ManageInvalidShare,
+ self.library._validate_volume_for_manage,
+ fake.FLEXVOL_TO_MANAGE,
+ vserver_client)
+
def test_extend_share(self):
vserver_client = mock.Mock()
diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py
index 96c68fa771..705d1a9c13 100644
--- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py
+++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_multi_svm.py
@@ -51,6 +51,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
kwargs = {
'configuration': fake.get_config_cmode(),
+ 'private_storage': mock.Mock(),
'app_version': fake.APP_VERSION
}
diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_single_svm.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_single_svm.py
index fb432e4d1b..e683c0dcaa 100644
--- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_single_svm.py
+++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_single_svm.py
@@ -44,6 +44,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
kwargs = {
'configuration': config,
+ 'private_storage': mock.Mock(),
'app_version': fake.APP_VERSION
}
diff --git a/manila/tests/share/drivers/netapp/dataontap/fakes.py b/manila/tests/share/drivers/netapp/dataontap/fakes.py
index 5d5c5af025..19c4877a36 100644
--- a/manila/tests/share/drivers/netapp/dataontap/fakes.py
+++ b/manila/tests/share/drivers/netapp/dataontap/fakes.py
@@ -31,6 +31,9 @@ VOLUME_NAME_TEMPLATE = 'share_%(share_id)s'
VSERVER_NAME_TEMPLATE = 'os_%s'
AGGREGATE_NAME_SEARCH_PATTERN = '(.*)'
SHARE_NAME = 'share_7cf7c200_d3af_4e05_b87e_9167c95dfcad'
+FLEXVOL_NAME = 'fake_volume'
+JUNCTION_PATH = '/%s' % FLEXVOL_NAME
+EXPORT_LOCATION = '%s:%s' % (HOST_NAME, JUNCTION_PATH)
SNAPSHOT_NAME = 'fake_snapshot'
SHARE_SIZE = 10
TENANT_ID = '24cb2448-13d8-4f41-afd9-eff5c4fd2a57'
@@ -73,7 +76,16 @@ SHARE = {
'share_server_id': '7e6a2cc8-871f-4b1d-8364-5aad0f98da86',
'network_info': {
'network_allocations': [{'ip_address': 'ip'}]
- }
+ },
+}
+
+FLEXVOL_TO_MANAGE = {
+ 'aggregate': POOL_NAME,
+ 'junction-path': '/%s' % FLEXVOL_NAME,
+ 'name': FLEXVOL_NAME,
+ 'type': 'rw',
+ 'style': 'flex',
+ 'size': '1610612736', # rounds down to 1 GB
}
EXTRA_SPEC = {
@@ -83,6 +95,8 @@ EXTRA_SPEC = {
'netapp:dedup': 'True',
'netapp:compression': 'false',
'netapp:max_files': 5000,
+ 'netapp_disk_type': 'FCAL',
+ 'netapp_raid_type': 'raid4',
}
PROVISIONING_OPTIONS = {
diff --git a/manila/tests/share/drivers/netapp/dataontap/protocols/fakes.py b/manila/tests/share/drivers/netapp/dataontap/protocols/fakes.py
index 708eaeaac5..be18b56150 100644
--- a/manila/tests/share/drivers/netapp/dataontap/protocols/fakes.py
+++ b/manila/tests/share/drivers/netapp/dataontap/protocols/fakes.py
@@ -45,3 +45,7 @@ USER_ACCESS = {
'access_to': 'fake_user',
'access_level': constants.ACCESS_LEVEL_RW,
}
+
+VOLUME = {
+ 'name': SHARE_NAME,
+}
diff --git a/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py b/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py
index b58b5a29b8..26fb731b18 100644
--- a/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py
+++ b/manila/tests/share/drivers/netapp/dataontap/protocols/test_cifs_cmode.py
@@ -189,6 +189,12 @@ class NetAppClusteredCIFSHelperTestCase(test.TestCase):
target = self.helper.get_target({'export_location': ''})
self.assertEqual('', target)
+ def test_get_share_name_for_share(self):
+
+ share_name = self.helper.get_share_name_for_share(fake.CIFS_SHARE)
+
+ self.assertEqual(fake.SHARE_NAME, share_name)
+
@ddt.data(
{
'location': r'\\%s\%s' % (fake.SHARE_ADDRESS_1, fake.SHARE_NAME),
diff --git a/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py b/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py
index 66459776de..c13508b571 100644
--- a/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py
+++ b/manila/tests/share/drivers/netapp/dataontap/protocols/test_nfs_cmode.py
@@ -172,6 +172,27 @@ class NetAppClusteredNFSHelperTestCase(test.TestCase):
target = self.helper.get_target(fake.NFS_SHARE)
self.assertEqual(fake.SHARE_ADDRESS_1, target)
+ def test_get_share_name_for_share(self):
+
+ self.mock_client.get_volume_at_junction_path.return_value = (
+ fake.VOLUME)
+
+ share_name = self.helper.get_share_name_for_share(fake.NFS_SHARE)
+
+ self.assertEqual(fake.SHARE_NAME, share_name)
+ self.mock_client.get_volume_at_junction_path.assert_called_once_with(
+ fake.NFS_SHARE_PATH)
+
+ def test_get_share_name_for_share_not_found(self):
+
+ self.mock_client.get_volume_at_junction_path.return_value = None
+
+ share_name = self.helper.get_share_name_for_share(fake.NFS_SHARE)
+
+ self.assertIsNone(share_name)
+ self.mock_client.get_volume_at_junction_path.assert_called_once_with(
+ fake.NFS_SHARE_PATH)
+
def test_get_target_missing_location(self):
target = self.helper.get_target({'export_location': ''})
diff --git a/manila/tests/share/drivers/netapp/test_common.py b/manila/tests/share/drivers/netapp/test_common.py
index a1f2cc6986..8cf0cf0f77 100644
--- a/manila/tests/share/drivers/netapp/test_common.py
+++ b/manila/tests/share/drivers/netapp/test_common.py
@@ -124,7 +124,11 @@ class NetAppDriverFactoryTestCase(test.TestCase):
config = na_fakes.create_configuration()
config.local_conf.set_override('driver_handles_share_servers',
mode == na_common.MULTI_SVM)
- kwargs = {'configuration': config, 'app_version': 'fake_info'}
+ kwargs = {
+ 'configuration': config,
+ 'private_storage': mock.Mock(),
+ 'app_version': 'fake_info'
+ }
driver = na_common.NetAppDriver._create_driver(
family, mode, **kwargs)
@@ -136,7 +140,11 @@ class NetAppDriverFactoryTestCase(test.TestCase):
config = na_fakes.create_configuration()
config.local_conf.set_override('driver_handles_share_servers', True)
- kwargs = {'configuration': config, 'app_version': 'fake_info'}
+ kwargs = {
+ 'configuration': config,
+ 'private_storage': mock.Mock(),
+ 'app_version': 'fake_info'
+ }
driver = na_common.NetAppDriver._create_driver('ONTAP_CLUSTER',
na_common.MULTI_SVM,