613 lines
27 KiB
Python
613 lines
27 KiB
Python
# Copyright (c) 2014 Andrew Kerr. All rights reserved.
|
|
# Copyright (c) 2015 Tom Barron. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
"""
|
|
Mock unit tests for the NetApp cmode nfs storage driver
|
|
"""
|
|
|
|
import ddt
|
|
import mock
|
|
from os_brick.remotefs import remotefs as remotefs_brick
|
|
from oslo_service import loopingcall
|
|
from oslo_utils import units
|
|
|
|
from cinder import exception
|
|
from cinder import test
|
|
from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake
|
|
from cinder.tests.unit.volume.drivers.netapp import fakes as na_fakes
|
|
from cinder import utils
|
|
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
|
|
from cinder.volume.drivers.netapp.dataontap.client import client_cmode
|
|
from cinder.volume.drivers.netapp.dataontap import nfs_base
|
|
from cinder.volume.drivers.netapp.dataontap import nfs_cmode
|
|
from cinder.volume.drivers.netapp.dataontap import ssc_cmode
|
|
from cinder.volume.drivers.netapp import utils as na_utils
|
|
from cinder.volume.drivers import nfs
|
|
from cinder.volume import utils as volume_utils
|
|
|
|
|
|
@ddt.ddt
|
|
class NetAppCmodeNfsDriverTestCase(test.TestCase):
|
|
def setUp(self):
|
|
super(NetAppCmodeNfsDriverTestCase, self).setUp()
|
|
|
|
kwargs = {'configuration': self.get_config_cmode()}
|
|
|
|
with mock.patch.object(utils, 'get_root_helper',
|
|
return_value=mock.Mock()):
|
|
with mock.patch.object(remotefs_brick, 'RemoteFsClient',
|
|
return_value=mock.Mock()):
|
|
self.driver = nfs_cmode.NetAppCmodeNfsDriver(**kwargs)
|
|
self.driver._mounted_shares = [fake.NFS_SHARE]
|
|
self.driver.ssc_vols = True
|
|
self.driver.vserver = fake.VSERVER_NAME
|
|
self.driver.ssc_enabled = True
|
|
|
|
def get_config_cmode(self):
|
|
config = na_fakes.create_configuration_cmode()
|
|
config.netapp_storage_protocol = 'nfs'
|
|
config.netapp_login = 'admin'
|
|
config.netapp_password = 'pass'
|
|
config.netapp_server_hostname = '127.0.0.1'
|
|
config.netapp_transport_type = 'http'
|
|
config.netapp_server_port = '80'
|
|
config.netapp_vserver = fake.VSERVER_NAME
|
|
return config
|
|
|
|
@mock.patch.object(client_cmode, 'Client', mock.Mock())
|
|
@mock.patch.object(nfs.NfsDriver, 'do_setup')
|
|
@mock.patch.object(na_utils, 'check_flags')
|
|
def test_do_setup(self, mock_check_flags, mock_super_do_setup):
|
|
self.driver.do_setup(mock.Mock())
|
|
|
|
self.assertTrue(mock_check_flags.called)
|
|
self.assertTrue(mock_super_do_setup.called)
|
|
|
|
@ddt.data({'thin': True, 'nfs_sparsed_volumes': True},
|
|
{'thin': True, 'nfs_sparsed_volumes': False},
|
|
{'thin': False, 'nfs_sparsed_volumes': True},
|
|
{'thin': False, 'nfs_sparsed_volumes': False})
|
|
@ddt.unpack
|
|
def test_get_pool_stats(self, thin, nfs_sparsed_volumes):
|
|
|
|
class test_volume(object):
|
|
pass
|
|
|
|
test_volume = test_volume()
|
|
test_volume.id = {'vserver': 'openstack', 'name': 'vola'}
|
|
test_volume.aggr = {
|
|
'disk_type': 'SSD',
|
|
'ha_policy': 'cfo',
|
|
'junction': '/vola',
|
|
'name': 'aggr1',
|
|
'raid_type': 'raiddp',
|
|
}
|
|
test_volume.export = {'path': fake.NFS_SHARE}
|
|
test_volume.sis = {'dedup': False, 'compression': False}
|
|
test_volume.state = {
|
|
'status': 'online',
|
|
'vserver_root': False,
|
|
'junction_active': True,
|
|
}
|
|
test_volume.qos = {'qos_policy_group': None}
|
|
|
|
ssc_map = {
|
|
'mirrored': {},
|
|
'dedup': {},
|
|
'compression': {},
|
|
'thin': {test_volume if thin else None},
|
|
'all': [test_volume],
|
|
}
|
|
self.driver.ssc_vols = ssc_map
|
|
|
|
self.driver.configuration.nfs_sparsed_volumes = nfs_sparsed_volumes
|
|
|
|
netapp_thin = 'true' if thin else 'false'
|
|
netapp_thick = 'false' if thin else 'true'
|
|
|
|
thick = not thin and not nfs_sparsed_volumes
|
|
|
|
total_capacity_gb = na_utils.round_down(
|
|
fake.TOTAL_BYTES / units.Gi, '0.01')
|
|
free_capacity_gb = na_utils.round_down(
|
|
fake.AVAILABLE_BYTES / units.Gi, '0.01')
|
|
provisioned_capacity_gb = total_capacity_gb - free_capacity_gb
|
|
capacity = {
|
|
'reserved_percentage': fake.RESERVED_PERCENTAGE,
|
|
'max_over_subscription_ratio': fake.MAX_OVER_SUBSCRIPTION_RATIO,
|
|
'total_capacity_gb': total_capacity_gb,
|
|
'free_capacity_gb': free_capacity_gb,
|
|
'provisioned_capacity_gb': provisioned_capacity_gb,
|
|
}
|
|
self.mock_object(self.driver,
|
|
'_get_share_capacity_info',
|
|
mock.Mock(return_value=capacity))
|
|
|
|
result = self.driver._get_pool_stats()
|
|
|
|
expected = [{'pool_name': '192.168.99.24:/fake/export/path',
|
|
'netapp_unmirrored': 'true',
|
|
'QoS_support': True,
|
|
'thick_provisioning_support': thick,
|
|
'netapp_thick_provisioned': netapp_thick,
|
|
'netapp_nocompression': 'true',
|
|
'thin_provisioning_support': not thick,
|
|
'free_capacity_gb': 12.0,
|
|
'netapp_thin_provisioned': netapp_thin,
|
|
'total_capacity_gb': 4468.0,
|
|
'netapp_compression': 'false',
|
|
'netapp_mirrored': 'false',
|
|
'netapp_dedup': 'false',
|
|
'reserved_percentage': 7,
|
|
'netapp_raid_type': 'raiddp',
|
|
'netapp_disk_type': 'SSD',
|
|
'netapp_nodedup': 'true',
|
|
'reserved_percentage': 7,
|
|
'max_over_subscription_ratio': 19.0,
|
|
'provisioned_capacity_gb': 4456.0}]
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_check_for_setup_error(self):
|
|
super_check_for_setup_error = self.mock_object(
|
|
nfs_base.NetAppNfsDriver, 'check_for_setup_error')
|
|
mock_check_ssc_api_permissions = self.mock_object(
|
|
ssc_cmode, 'check_ssc_api_permissions')
|
|
mock_start_periodic_tasks = self.mock_object(
|
|
self.driver, '_start_periodic_tasks')
|
|
self.driver.zapi_client = mock.Mock()
|
|
|
|
self.driver.check_for_setup_error()
|
|
|
|
self.assertEqual(1, super_check_for_setup_error.call_count)
|
|
mock_check_ssc_api_permissions.assert_called_once_with(
|
|
self.driver.zapi_client)
|
|
self.assertEqual(1, mock_start_periodic_tasks.call_count)
|
|
|
|
def test_delete_volume(self):
|
|
fake_provider_location = 'fake_provider_location'
|
|
fake_volume = {'provider_location': fake_provider_location}
|
|
self.mock_object(self.driver, '_delete_backing_file_for_volume')
|
|
self.mock_object(na_utils, 'get_valid_qos_policy_group_info')
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_prov_deprov = self.mock_object(self.driver,
|
|
'_post_prov_deprov_in_ssc')
|
|
|
|
self.driver.delete_volume(fake_volume)
|
|
|
|
mock_prov_deprov.assert_called_once_with(fake_provider_location)
|
|
|
|
def test_delete_volume_exception_path(self):
|
|
fake_provider_location = 'fake_provider_location'
|
|
fake_volume = {'provider_location': fake_provider_location}
|
|
self.mock_object(self.driver, '_delete_backing_file_for_volume')
|
|
self.mock_object(na_utils, 'get_valid_qos_policy_group_info')
|
|
self.driver.zapi_client = mock.Mock(side_effect=[Exception])
|
|
mock_prov_deprov = self.mock_object(self.driver,
|
|
'_post_prov_deprov_in_ssc')
|
|
|
|
self.driver.delete_volume(fake_volume)
|
|
|
|
mock_prov_deprov.assert_called_once_with(fake_provider_location)
|
|
|
|
def test_delete_backing_file_for_volume(self):
|
|
mock_filer_delete = self.mock_object(self.driver,
|
|
'_delete_volume_on_filer')
|
|
mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
|
|
'delete_volume')
|
|
|
|
self.driver._delete_backing_file_for_volume(fake.NFS_VOLUME)
|
|
|
|
mock_filer_delete.assert_called_once_with(fake.NFS_VOLUME)
|
|
self.assertEqual(0, mock_super_delete.call_count)
|
|
|
|
def test_delete_backing_file_for_volume_exception_path(self):
|
|
mock_filer_delete = self.mock_object(self.driver,
|
|
'_delete_volume_on_filer')
|
|
mock_filer_delete.side_effect = [Exception]
|
|
mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
|
|
'delete_volume')
|
|
|
|
self.driver._delete_backing_file_for_volume(fake.NFS_VOLUME)
|
|
|
|
mock_filer_delete.assert_called_once_with(fake.NFS_VOLUME)
|
|
mock_super_delete.assert_called_once_with(fake.NFS_VOLUME)
|
|
|
|
def test_delete_volume_on_filer(self):
|
|
mock_get_vs_ip = self.mock_object(self.driver, '_get_export_ip_path')
|
|
mock_get_vs_ip.return_value = (fake.VSERVER_NAME, '/%s' % fake.FLEXVOL)
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_zapi_delete = self.driver.zapi_client.delete_file
|
|
|
|
self.driver._delete_volume_on_filer(fake.NFS_VOLUME)
|
|
|
|
mock_zapi_delete.assert_called_once_with(
|
|
'/vol/%s/%s' % (fake.FLEXVOL, fake.NFS_VOLUME['name']))
|
|
|
|
def test_delete_snapshot(self):
|
|
mock_get_location = self.mock_object(self.driver,
|
|
'_get_provider_location')
|
|
mock_get_location.return_value = fake.PROVIDER_LOCATION
|
|
|
|
mock_delete_backing = self.mock_object(
|
|
self.driver, '_delete_backing_file_for_snapshot')
|
|
|
|
mock_prov_deprov = self.mock_object(self.driver,
|
|
'_post_prov_deprov_in_ssc')
|
|
|
|
self.driver.delete_snapshot(fake.test_snapshot)
|
|
|
|
mock_delete_backing.assert_called_once_with(fake.test_snapshot)
|
|
mock_prov_deprov.assert_called_once_with(fake.PROVIDER_LOCATION)
|
|
|
|
def test_delete_backing_file_for_snapshot(self):
|
|
mock_filer_delete = self.mock_object(
|
|
self.driver, '_delete_snapshot_on_filer')
|
|
mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
|
|
'delete_snapshot')
|
|
|
|
self.driver._delete_backing_file_for_snapshot(fake.test_snapshot)
|
|
|
|
mock_filer_delete.assert_called_once_with(fake.test_snapshot)
|
|
self.assertEqual(0, mock_super_delete.call_count)
|
|
|
|
def test_delete_backing_file_for_snapshot_exception_path(self):
|
|
mock_filer_delete = self.mock_object(
|
|
self.driver, '_delete_snapshot_on_filer')
|
|
mock_filer_delete.side_effect = [Exception]
|
|
mock_super_delete = self.mock_object(nfs_base.NetAppNfsDriver,
|
|
'delete_snapshot')
|
|
|
|
self.driver._delete_backing_file_for_snapshot(fake.test_snapshot)
|
|
|
|
mock_filer_delete.assert_called_once_with(fake.test_snapshot)
|
|
mock_super_delete.assert_called_once_with(fake.test_snapshot)
|
|
|
|
def test_delete_snapshot_on_filer(self):
|
|
mock_get_vs_ip = self.mock_object(self.driver, '_get_export_ip_path')
|
|
mock_get_vs_ip.return_value = (fake.VSERVER_NAME, '/%s' % fake.FLEXVOL)
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_zapi_delete = self.driver.zapi_client.delete_file
|
|
|
|
self.driver._delete_snapshot_on_filer(fake.test_snapshot)
|
|
|
|
mock_zapi_delete.assert_called_once_with(
|
|
'/vol/%s/%s' % (fake.FLEXVOL, fake.test_snapshot['name']))
|
|
|
|
def test_do_qos_for_volume_no_exception(self):
|
|
|
|
mock_get_info = self.mock_object(na_utils,
|
|
'get_valid_qos_policy_group_info')
|
|
mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group
|
|
mock_set_policy = self.mock_object(self.driver,
|
|
'_set_qos_policy_group_on_volume')
|
|
mock_error_log = self.mock_object(nfs_cmode.LOG, 'error')
|
|
mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
|
|
mock_cleanup = self.mock_object(self.driver,
|
|
'_cleanup_volume_on_failure')
|
|
|
|
self.driver._do_qos_for_volume(fake.NFS_VOLUME, fake.EXTRA_SPECS)
|
|
|
|
mock_get_info.assert_has_calls([
|
|
mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)])
|
|
mock_provision_qos.assert_has_calls([
|
|
mock.call(fake.QOS_POLICY_GROUP_INFO)])
|
|
mock_set_policy.assert_has_calls([
|
|
mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)])
|
|
self.assertEqual(0, mock_error_log.call_count)
|
|
self.assertEqual(0, mock_debug_log.call_count)
|
|
self.assertEqual(0, mock_cleanup.call_count)
|
|
|
|
def test_do_qos_for_volume_exception_w_cleanup(self):
|
|
mock_get_info = self.mock_object(na_utils,
|
|
'get_valid_qos_policy_group_info')
|
|
mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group
|
|
mock_set_policy = self.mock_object(self.driver,
|
|
'_set_qos_policy_group_on_volume')
|
|
mock_set_policy.side_effect = netapp_api.NaApiError
|
|
mock_error_log = self.mock_object(nfs_cmode.LOG, 'error')
|
|
mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
|
|
mock_cleanup = self.mock_object(self.driver,
|
|
'_cleanup_volume_on_failure')
|
|
|
|
self.assertRaises(netapp_api.NaApiError,
|
|
self.driver._do_qos_for_volume,
|
|
fake.NFS_VOLUME,
|
|
fake.EXTRA_SPECS)
|
|
|
|
mock_get_info.assert_has_calls([
|
|
mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)])
|
|
mock_provision_qos.assert_has_calls([
|
|
mock.call(fake.QOS_POLICY_GROUP_INFO)])
|
|
mock_set_policy.assert_has_calls([
|
|
mock.call(fake.NFS_VOLUME, fake.QOS_POLICY_GROUP_INFO)])
|
|
self.assertEqual(1, mock_error_log.call_count)
|
|
self.assertEqual(1, mock_debug_log.call_count)
|
|
mock_cleanup.assert_has_calls([
|
|
mock.call(fake.NFS_VOLUME)])
|
|
|
|
def test_do_qos_for_volume_exception_no_cleanup(self):
|
|
|
|
mock_get_info = self.mock_object(na_utils,
|
|
'get_valid_qos_policy_group_info')
|
|
mock_get_info.side_effect = exception.Invalid
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_provision_qos = self.driver.zapi_client.provision_qos_policy_group
|
|
mock_set_policy = self.mock_object(self.driver,
|
|
'_set_qos_policy_group_on_volume')
|
|
mock_error_log = self.mock_object(nfs_cmode.LOG, 'error')
|
|
mock_debug_log = self.mock_object(nfs_cmode.LOG, 'debug')
|
|
mock_cleanup = self.mock_object(self.driver,
|
|
'_cleanup_volume_on_failure')
|
|
|
|
self.assertRaises(exception.Invalid, self.driver._do_qos_for_volume,
|
|
fake.NFS_VOLUME, fake.EXTRA_SPECS, cleanup=False)
|
|
|
|
mock_get_info.assert_has_calls([
|
|
mock.call(fake.NFS_VOLUME, fake.EXTRA_SPECS)])
|
|
self.assertEqual(0, mock_provision_qos.call_count)
|
|
self.assertEqual(0, mock_set_policy.call_count)
|
|
self.assertEqual(1, mock_error_log.call_count)
|
|
self.assertEqual(0, mock_debug_log.call_count)
|
|
self.assertEqual(0, mock_cleanup.call_count)
|
|
|
|
def test_set_qos_policy_group_on_volume(self):
|
|
|
|
mock_get_name_from_info = self.mock_object(
|
|
na_utils, 'get_qos_policy_group_name_from_info')
|
|
mock_get_name_from_info.return_value = fake.QOS_POLICY_GROUP_NAME
|
|
|
|
mock_extract_host = self.mock_object(volume_utils, 'extract_host')
|
|
mock_extract_host.return_value = fake.NFS_SHARE
|
|
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_get_flex_vol_name =\
|
|
self.driver.zapi_client.get_vol_by_junc_vserver
|
|
mock_get_flex_vol_name.return_value = fake.FLEXVOL
|
|
|
|
mock_file_assign_qos = self.driver.zapi_client.file_assign_qos
|
|
|
|
self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME,
|
|
fake.QOS_POLICY_GROUP_INFO)
|
|
|
|
mock_get_name_from_info.assert_has_calls([
|
|
mock.call(fake.QOS_POLICY_GROUP_INFO)])
|
|
mock_extract_host.assert_has_calls([
|
|
mock.call(fake.NFS_HOST_STRING, level='pool')])
|
|
mock_get_flex_vol_name.assert_has_calls([
|
|
mock.call(fake.VSERVER_NAME, fake.EXPORT_PATH)])
|
|
mock_file_assign_qos.assert_has_calls([
|
|
mock.call(fake.FLEXVOL, fake.QOS_POLICY_GROUP_NAME,
|
|
fake.NFS_VOLUME['name'])])
|
|
|
|
def test_set_qos_policy_group_on_volume_no_info(self):
|
|
|
|
mock_get_name_from_info = self.mock_object(
|
|
na_utils, 'get_qos_policy_group_name_from_info')
|
|
|
|
mock_extract_host = self.mock_object(volume_utils, 'extract_host')
|
|
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_get_flex_vol_name =\
|
|
self.driver.zapi_client.get_vol_by_junc_vserver
|
|
|
|
mock_file_assign_qos = self.driver.zapi_client.file_assign_qos
|
|
|
|
self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME,
|
|
None)
|
|
|
|
self.assertEqual(0, mock_get_name_from_info.call_count)
|
|
self.assertEqual(0, mock_extract_host.call_count)
|
|
self.assertEqual(0, mock_get_flex_vol_name.call_count)
|
|
self.assertEqual(0, mock_file_assign_qos.call_count)
|
|
|
|
def test_set_qos_policy_group_on_volume_no_name(self):
|
|
|
|
mock_get_name_from_info = self.mock_object(
|
|
na_utils, 'get_qos_policy_group_name_from_info')
|
|
mock_get_name_from_info.return_value = None
|
|
|
|
mock_extract_host = self.mock_object(volume_utils, 'extract_host')
|
|
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_get_flex_vol_name =\
|
|
self.driver.zapi_client.get_vol_by_junc_vserver
|
|
|
|
mock_file_assign_qos = self.driver.zapi_client.file_assign_qos
|
|
|
|
self.driver._set_qos_policy_group_on_volume(fake.NFS_VOLUME,
|
|
fake.QOS_POLICY_GROUP_INFO)
|
|
|
|
mock_get_name_from_info.assert_has_calls([
|
|
mock.call(fake.QOS_POLICY_GROUP_INFO)])
|
|
self.assertEqual(0, mock_extract_host.call_count)
|
|
self.assertEqual(0, mock_get_flex_vol_name.call_count)
|
|
self.assertEqual(0, mock_file_assign_qos.call_count)
|
|
|
|
def test_unmanage(self):
|
|
mock_get_info = self.mock_object(na_utils,
|
|
'get_valid_qos_policy_group_info')
|
|
mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO
|
|
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_mark_for_deletion =\
|
|
self.driver.zapi_client.mark_qos_policy_group_for_deletion
|
|
|
|
super_unmanage = self.mock_object(nfs_base.NetAppNfsDriver, 'unmanage')
|
|
|
|
self.driver.unmanage(fake.NFS_VOLUME)
|
|
|
|
mock_get_info.assert_has_calls([mock.call(fake.NFS_VOLUME)])
|
|
mock_mark_for_deletion.assert_has_calls([
|
|
mock.call(fake.QOS_POLICY_GROUP_INFO)])
|
|
super_unmanage.assert_has_calls([mock.call(fake.NFS_VOLUME)])
|
|
|
|
def test_unmanage_invalid_qos(self):
|
|
mock_get_info = self.mock_object(na_utils,
|
|
'get_valid_qos_policy_group_info')
|
|
mock_get_info.side_effect = exception.Invalid
|
|
|
|
super_unmanage = self.mock_object(nfs_base.NetAppNfsDriver, 'unmanage')
|
|
|
|
self.driver.unmanage(fake.NFS_VOLUME)
|
|
|
|
mock_get_info.assert_has_calls([mock.call(fake.NFS_VOLUME)])
|
|
super_unmanage.assert_has_calls([mock.call(fake.NFS_VOLUME)])
|
|
|
|
def test_create_volume(self):
|
|
self.mock_object(self.driver, '_ensure_shares_mounted')
|
|
self.mock_object(na_utils, 'get_volume_extra_specs')
|
|
self.mock_object(self.driver, '_do_create_volume')
|
|
self.mock_object(self.driver, '_do_qos_for_volume')
|
|
update_ssc = self.mock_object(self.driver, '_update_stale_vols')
|
|
self.mock_object(self.driver, '_get_vol_for_share')
|
|
expected = {'provider_location': fake.NFS_SHARE}
|
|
|
|
result = self.driver.create_volume(fake.NFS_VOLUME)
|
|
|
|
self.assertEqual(expected, result)
|
|
self.assertEqual(1, update_ssc.call_count)
|
|
|
|
def test_create_volume_exception(self):
|
|
self.mock_object(self.driver, '_ensure_shares_mounted')
|
|
self.mock_object(na_utils, 'get_volume_extra_specs')
|
|
mock_create = self.mock_object(self.driver, '_do_create_volume')
|
|
mock_create.side_effect = Exception
|
|
update_ssc = self.mock_object(self.driver, '_update_stale_vols')
|
|
self.mock_object(self.driver, '_get_vol_for_share')
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.create_volume,
|
|
fake.NFS_VOLUME)
|
|
|
|
self.assertEqual(1, update_ssc.call_count)
|
|
|
|
def test_start_periodic_tasks(self):
|
|
|
|
self.driver.zapi_client = mock.Mock()
|
|
mock_remove_unused_qos_policy_groups = self.mock_object(
|
|
self.driver.zapi_client,
|
|
'remove_unused_qos_policy_groups')
|
|
|
|
harvest_qos_periodic_task = mock.Mock()
|
|
mock_loopingcall = self.mock_object(
|
|
loopingcall,
|
|
'FixedIntervalLoopingCall',
|
|
mock.Mock(side_effect=[harvest_qos_periodic_task]))
|
|
|
|
self.driver._start_periodic_tasks()
|
|
|
|
mock_loopingcall.assert_has_calls([
|
|
mock.call(mock_remove_unused_qos_policy_groups)])
|
|
self.assertTrue(harvest_qos_periodic_task.start.called)
|
|
|
|
@ddt.data(
|
|
{'space': True, 'ssc': True, 'match': True, 'expected': True},
|
|
{'space': True, 'ssc': True, 'match': False, 'expected': False},
|
|
{'space': True, 'ssc': False, 'match': True, 'expected': True},
|
|
{'space': True, 'ssc': False, 'match': False, 'expected': True},
|
|
{'space': False, 'ssc': True, 'match': True, 'expected': False},
|
|
{'space': False, 'ssc': True, 'match': False, 'expected': False},
|
|
{'space': False, 'ssc': False, 'match': True, 'expected': False},
|
|
{'space': False, 'ssc': False, 'match': False, 'expected': False},
|
|
)
|
|
@ddt.unpack
|
|
@mock.patch.object(nfs_cmode.NetAppCmodeNfsDriver,
|
|
'_is_share_vol_type_match')
|
|
@mock.patch.object(nfs_cmode.NetAppCmodeNfsDriver,
|
|
'_share_has_space_for_clone')
|
|
@mock.patch.object(nfs_cmode.NetAppCmodeNfsDriver,
|
|
'_is_volume_thin_provisioned')
|
|
def test_is_share_clone_compatible(self,
|
|
mock_is_volume_thin_provisioned,
|
|
mock_share_has_space_for_clone,
|
|
mock_is_share_vol_type_match,
|
|
space, ssc, match, expected):
|
|
mock_share_has_space_for_clone.return_value = space
|
|
mock_is_share_vol_type_match.return_value = match
|
|
|
|
with mock.patch.object(self.driver, 'ssc_enabled', ssc):
|
|
result = self.driver._is_share_clone_compatible(fake.VOLUME,
|
|
fake.NFS_SHARE)
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data(
|
|
{'sparsed': True, 'ssc': True, 'vol_thin': True, 'expected': True},
|
|
{'sparsed': True, 'ssc': True, 'vol_thin': False, 'expected': True},
|
|
{'sparsed': True, 'ssc': False, 'vol_thin': True, 'expected': True},
|
|
{'sparsed': True, 'ssc': False, 'vol_thin': False, 'expected': True},
|
|
{'sparsed': False, 'ssc': True, 'vol_thin': True, 'expected': True},
|
|
{'sparsed': False, 'ssc': True, 'vol_thin': False, 'expected': False},
|
|
{'sparsed': False, 'ssc': False, 'vol_thin': True, 'expected': False},
|
|
{'sparsed': False, 'ssc': False, 'vol_thin': False, 'expected': False},
|
|
)
|
|
@ddt.unpack
|
|
def test_is_volume_thin_provisioned(
|
|
self, sparsed, ssc, vol_thin, expected):
|
|
fake_volume = object()
|
|
ssc_vols = {'thin': {fake_volume if vol_thin else None}}
|
|
|
|
with mock.patch.object(self.driver, 'ssc_enabled', ssc):
|
|
with mock.patch.object(self.driver, 'ssc_vols', ssc_vols):
|
|
with mock.patch.object(self.driver.configuration,
|
|
'nfs_sparsed_volumes',
|
|
sparsed):
|
|
result = self.driver._is_volume_thin_provisioned(
|
|
fake_volume)
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data(
|
|
{'ssc': True, 'share': fake.NFS_SHARE, 'vol': fake.test_volume},
|
|
{'ssc': True, 'share': fake.NFS_SHARE, 'vol': None},
|
|
{'ssc': True, 'share': None, 'vol': fake.test_volume},
|
|
{'ssc': True, 'share': None, 'vol': None},
|
|
{'ssc': False, 'share': fake.NFS_SHARE, 'vol': fake.test_volume},
|
|
{'ssc': False, 'share': fake.NFS_SHARE, 'vol': None},
|
|
{'ssc': False, 'share': None, 'vol': fake.test_volume},
|
|
{'ssc': False, 'share': None, 'vol': None},
|
|
)
|
|
@ddt.unpack
|
|
def test_post_prov_deprov_in_ssc(self, ssc, share, vol):
|
|
|
|
with mock.patch.object(self.driver, 'ssc_enabled', ssc):
|
|
with mock.patch.object(
|
|
self.driver, '_get_vol_for_share') as mock_get_vol:
|
|
with mock.patch.object(
|
|
self.driver, '_update_stale_vols') as mock_update:
|
|
mock_get_vol.return_value = vol
|
|
self.driver._post_prov_deprov_in_ssc(share)
|
|
|
|
if ssc and share and vol:
|
|
mock_update.assert_called_once_with(volume=vol)
|
|
else:
|
|
self.assertEqual(0, mock_update.call_count)
|
|
|
|
def test_get_vol_for_share(self):
|
|
fake_volume = fake.test_volume
|
|
ssc_vols = {'all': {fake_volume}}
|
|
|
|
with mock.patch.object(self.driver, 'ssc_vols', ssc_vols):
|
|
result = self.driver._get_vol_for_share(fake.NFS_SHARE)
|
|
|
|
self.assertEqual(fake.test_volume, result)
|
|
|
|
def test_get_vol_for_share_no_ssc_vols(self):
|
|
with mock.patch.object(self.driver, 'ssc_vols', None):
|
|
self.assertIsNone(self.driver._get_vol_for_share(fake.NFS_SHARE))
|