1183 lines
51 KiB
Python
1183 lines
51 KiB
Python
# Copyright (c) 2014 Andrew Kerr. All rights reserved.
|
|
# Copyright (c) 2015 Tom Barron. All rights reserved.
|
|
# 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.
|
|
"""Unit tests for the NetApp NFS storage driver."""
|
|
import copy
|
|
import os
|
|
import time
|
|
from unittest import mock
|
|
|
|
import ddt
|
|
from os_brick.remotefs import remotefs as remotefs_brick
|
|
from oslo_concurrency import processutils
|
|
from oslo_utils import units
|
|
|
|
from cinder import context
|
|
from cinder import exception
|
|
from cinder.objects import fields
|
|
from cinder.tests.unit import fake_snapshot
|
|
from cinder.tests.unit import fake_volume
|
|
from cinder.tests.unit import test
|
|
from cinder.tests.unit.volume.drivers.netapp.dataontap import fakes as fake
|
|
from cinder import utils
|
|
from cinder.volume.drivers.netapp.dataontap.client import api as netapp_api
|
|
from cinder.volume.drivers.netapp.dataontap import nfs_base
|
|
from cinder.volume.drivers.netapp.dataontap.utils import loopingcalls
|
|
from cinder.volume.drivers.netapp import utils as na_utils
|
|
from cinder.volume.drivers import nfs
|
|
from cinder.volume.drivers import remotefs
|
|
from cinder.volume import volume_utils
|
|
|
|
|
|
@ddt.ddt
|
|
class NetAppNfsDriverTestCase(test.TestCase):
|
|
def setUp(self):
|
|
super(NetAppNfsDriverTestCase, self).setUp()
|
|
configuration = mock.Mock()
|
|
configuration.reserved_percentage = 0
|
|
configuration.nfs_mount_point_base = '/mnt/test'
|
|
configuration.reserved_percentage = 0
|
|
configuration.max_over_subscription_ratio = 1.1
|
|
self.fake_nfs_export_1 = fake.NFS_EXPORT_1
|
|
self.fake_nfs_export_2 = fake.NFS_EXPORT_2
|
|
self.fake_mount_point = fake.MOUNT_POINT
|
|
self.ctxt = context.RequestContext('fake', 'fake', auth_token=True)
|
|
|
|
kwargs = {
|
|
'configuration': configuration,
|
|
'host': 'openstack@netappnfs',
|
|
}
|
|
|
|
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_base.NetAppNfsDriver(**kwargs)
|
|
self.driver.db = mock.Mock()
|
|
|
|
self.driver.zapi_client = mock.Mock()
|
|
self.zapi_client = self.driver.zapi_client
|
|
|
|
@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)
|
|
|
|
def test_get_share_capacity_info(self):
|
|
mock_get_capacity = self.mock_object(self.driver, '_get_capacity_info')
|
|
mock_get_capacity.return_value = fake.CAPACITY_VALUES
|
|
expected_total_capacity_gb = na_utils.round_down(
|
|
fake.TOTAL_BYTES / units.Gi, '0.01')
|
|
expected_free_capacity_gb = (na_utils.round_down(
|
|
fake.AVAILABLE_BYTES / units.Gi, '0.01'))
|
|
expected_reserved_percentage = round(
|
|
self.driver.configuration.reserved_percentage)
|
|
|
|
result = self.driver._get_share_capacity_info(fake.NFS_SHARE)
|
|
|
|
self.assertEqual(expected_total_capacity_gb,
|
|
result['total_capacity_gb'])
|
|
self.assertEqual(expected_free_capacity_gb,
|
|
result['free_capacity_gb'])
|
|
self.assertEqual(expected_reserved_percentage,
|
|
round(result['reserved_percentage']))
|
|
|
|
def test_get_capacity_info_ipv4_share(self):
|
|
expected = fake.CAPACITY_VALUES
|
|
get_capacity = self.driver.zapi_client.get_flexvol_capacity
|
|
get_capacity.return_value = fake.CAPACITIES
|
|
|
|
result = self.driver._get_capacity_info(fake.NFS_SHARE_IPV4)
|
|
|
|
self.assertEqual(expected, result)
|
|
get_capacity.assert_has_calls([
|
|
mock.call(flexvol_path=fake.EXPORT_PATH)])
|
|
|
|
def test_get_capacity_info_ipv6_share(self):
|
|
expected = fake.CAPACITY_VALUES
|
|
get_capacity = self.driver.zapi_client.get_flexvol_capacity
|
|
get_capacity.return_value = fake.CAPACITIES
|
|
|
|
result = self.driver._get_capacity_info(fake.NFS_SHARE_IPV6)
|
|
|
|
self.assertEqual(expected, result)
|
|
get_capacity.assert_has_calls([
|
|
mock.call(flexvol_path=fake.EXPORT_PATH)])
|
|
|
|
def test_get_pool(self):
|
|
pool = self.driver.get_pool({'provider_location': 'fake-share'})
|
|
|
|
self.assertEqual('fake-share', pool)
|
|
|
|
@ddt.data(None,
|
|
{'replication_status': fields.ReplicationStatus.ENABLED})
|
|
def test_create_volume(self, model_update):
|
|
self.mock_object(self.driver, '_ensure_shares_mounted')
|
|
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
|
|
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')
|
|
self.mock_object(self.driver, '_get_volume_model_update',
|
|
return_value=model_update)
|
|
expected = {'provider_location': fake.NFS_SHARE}
|
|
if model_update:
|
|
expected.update(model_update)
|
|
|
|
actual = self.driver.create_volume(fake.NFS_VOLUME)
|
|
|
|
self.assertEqual(expected, actual)
|
|
|
|
def test_create_volume_no_pool(self):
|
|
volume = copy.deepcopy(fake.NFS_VOLUME)
|
|
volume['host'] = '%s@%s' % (fake.HOST_NAME, fake.BACKEND_NAME)
|
|
self.mock_object(self.driver, '_ensure_shares_mounted')
|
|
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
|
|
|
|
self.assertRaises(exception.InvalidHost,
|
|
self.driver.create_volume,
|
|
volume)
|
|
|
|
def test_create_volume_exception(self):
|
|
self.mock_object(self.driver, '_ensure_shares_mounted')
|
|
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
|
|
self.mock_object(na_utils, 'get_volume_extra_specs')
|
|
mock_create = self.mock_object(self.driver, '_do_create_volume')
|
|
mock_create.side_effect = Exception
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.create_volume,
|
|
fake.NFS_VOLUME)
|
|
|
|
@ddt.data(None, {'key': 'value'})
|
|
def test_clone_source_to_destination_volume(self, model_update):
|
|
self.mock_object(self.driver, '_get_volume_location',
|
|
return_value=fake.POOL_NAME)
|
|
self.mock_object(na_utils, 'get_volume_extra_specs',
|
|
return_value=fake.EXTRA_SPECS)
|
|
self.mock_object(
|
|
self.driver,
|
|
'_clone_with_extension_check')
|
|
self.mock_object(self.driver, '_do_qos_for_volume')
|
|
self.mock_object(self.driver, '_get_volume_model_update',
|
|
return_value=model_update)
|
|
expected = {'provider_location': fake.POOL_NAME}
|
|
if model_update:
|
|
expected.update(model_update)
|
|
|
|
result = self.driver._clone_source_to_destination_volume(
|
|
fake.CLONE_SOURCE, fake.CLONE_DESTINATION)
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_clone_source_to_destination_volume_with_do_qos_exception(self):
|
|
self.mock_object(self.driver, '_get_volume_location',
|
|
return_value=fake.POOL_NAME)
|
|
self.mock_object(na_utils, 'get_volume_extra_specs',
|
|
return_value=fake.EXTRA_SPECS)
|
|
self.mock_object(
|
|
self.driver,
|
|
'_clone_with_extension_check')
|
|
self.mock_object(self.driver, '_do_qos_for_volume',
|
|
side_effect=Exception)
|
|
|
|
self.assertRaises(
|
|
exception.VolumeBackendAPIException,
|
|
self.driver._clone_source_to_destination_volume,
|
|
fake.CLONE_SOURCE,
|
|
fake.CLONE_DESTINATION)
|
|
|
|
def test_clone_with_extension_check_equal_sizes(self):
|
|
clone_source = copy.deepcopy(fake.CLONE_SOURCE)
|
|
clone_source['size'] = fake.VOLUME['size']
|
|
self.mock_object(self.driver, '_clone_backing_file_for_volume')
|
|
self.mock_object(self.driver, 'local_path')
|
|
mock_discover = self.mock_object(self.driver,
|
|
'_discover_file_till_timeout')
|
|
mock_discover.return_value = True
|
|
self.mock_object(self.driver, '_set_rw_permissions')
|
|
mock_extend_volume = self.mock_object(self.driver, 'extend_volume')
|
|
|
|
self.driver._clone_with_extension_check(clone_source, fake.NFS_VOLUME)
|
|
|
|
self.assertEqual(0, mock_extend_volume.call_count)
|
|
|
|
def test_clone_with_extension_check_unequal_sizes(self):
|
|
clone_source = copy.deepcopy(fake.CLONE_SOURCE)
|
|
clone_source['size'] = fake.VOLUME['size'] + 1
|
|
self.mock_object(self.driver, '_clone_backing_file_for_volume')
|
|
self.mock_object(self.driver, 'local_path')
|
|
mock_discover = self.mock_object(self.driver,
|
|
'_discover_file_till_timeout')
|
|
mock_discover.return_value = True
|
|
self.mock_object(self.driver, '_set_rw_permissions')
|
|
mock_extend_volume = self.mock_object(self.driver, 'extend_volume')
|
|
|
|
self.driver._clone_with_extension_check(clone_source, fake.NFS_VOLUME)
|
|
|
|
self.assertEqual(1, mock_extend_volume.call_count)
|
|
|
|
def test_clone_with_extension_check_extend_exception(self):
|
|
clone_source = copy.deepcopy(fake.CLONE_SOURCE)
|
|
clone_source['size'] = fake.VOLUME['size'] + 1
|
|
self.mock_object(self.driver, '_clone_backing_file_for_volume')
|
|
self.mock_object(self.driver, 'local_path')
|
|
mock_discover = self.mock_object(self.driver,
|
|
'_discover_file_till_timeout')
|
|
mock_discover.return_value = True
|
|
self.mock_object(self.driver, '_set_rw_permissions')
|
|
mock_extend_volume = self.mock_object(self.driver, 'extend_volume')
|
|
mock_extend_volume.side_effect = Exception
|
|
mock_cleanup = self.mock_object(self.driver,
|
|
'_cleanup_volume_on_failure')
|
|
|
|
self.assertRaises(exception.CinderException,
|
|
self.driver._clone_with_extension_check,
|
|
clone_source,
|
|
fake.NFS_VOLUME)
|
|
|
|
self.assertEqual(1, mock_cleanup.call_count)
|
|
|
|
def test_clone_with_extension_check_no_discovery(self):
|
|
self.mock_object(self.driver, '_clone_backing_file_for_volume')
|
|
self.mock_object(self.driver, 'local_path')
|
|
self.mock_object(self.driver, '_set_rw_permissions')
|
|
mock_discover = self.mock_object(self.driver,
|
|
'_discover_file_till_timeout')
|
|
mock_discover.return_value = False
|
|
|
|
self.assertRaises(exception.CinderException,
|
|
self.driver._clone_with_extension_check,
|
|
fake.CLONE_SOURCE,
|
|
fake.NFS_VOLUME)
|
|
|
|
@ddt.data(True, False)
|
|
def test_create_volume_from_snapshot(self, is_flexgroup):
|
|
provider_location = fake.POOL_NAME
|
|
volume = fake.VOLUME
|
|
expected_source = {
|
|
'name': fake.SNAPSHOT_NAME,
|
|
'size': fake.SIZE,
|
|
'id': fake.VOLUME_ID,
|
|
}
|
|
mock_clone_call = self.mock_object(
|
|
self.driver, '_clone_source_to_destination_volume',
|
|
return_value=provider_location)
|
|
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
|
|
self.mock_object(self.driver, '_is_flexgroup',
|
|
return_value=is_flexgroup)
|
|
self.mock_object(self.driver, '_is_flexgroup_clone_file_supported',
|
|
return_value=not is_flexgroup)
|
|
mock_super_create = self.mock_object(
|
|
nfs.NfsDriver, 'create_volume_from_snapshot',
|
|
return_value=provider_location)
|
|
mock_do_qos = self.mock_object(
|
|
self.driver, '_do_qos_for_file_flexgroup',
|
|
return_value=provider_location)
|
|
|
|
retval = self.driver.create_volume_from_snapshot(volume, fake.SNAPSHOT)
|
|
|
|
self.assertEqual(provider_location, retval)
|
|
if is_flexgroup:
|
|
mock_clone_call.assert_not_called()
|
|
mock_super_create.assert_called_once_with(volume, fake.SNAPSHOT)
|
|
mock_do_qos.assert_called_once_with(volume, provider_location)
|
|
else:
|
|
mock_clone_call.assert_called_once_with(expected_source, volume)
|
|
mock_do_qos.assert_not_called()
|
|
mock_super_create.not_called()
|
|
|
|
@ddt.data(True, False)
|
|
def test_create_cloned_volume(self, is_flexgroup):
|
|
provider_location = fake.POOL_NAME
|
|
volume = fake.VOLUME
|
|
src_vref = fake.CLONE_SOURCE
|
|
mock_clone_call = self.mock_object(
|
|
self.driver, '_clone_source_to_destination_volume',
|
|
return_value=provider_location)
|
|
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
|
|
self.mock_object(self.driver, '_is_flexgroup',
|
|
return_value=is_flexgroup)
|
|
self.mock_object(self.driver, '_is_flexgroup_clone_file_supported',
|
|
return_value=not is_flexgroup)
|
|
mock_super_create = self.mock_object(
|
|
nfs.NfsDriver, 'create_cloned_volume',
|
|
return_value=provider_location)
|
|
mock_do_qos = self.mock_object(
|
|
self.driver, '_do_qos_for_file_flexgroup',
|
|
return_value=provider_location)
|
|
|
|
result = self.driver.create_cloned_volume(volume, src_vref)
|
|
|
|
self.assertEqual(provider_location, result)
|
|
if is_flexgroup:
|
|
mock_clone_call.assert_not_called()
|
|
mock_super_create.assert_called_once_with(volume, src_vref)
|
|
mock_do_qos.assert_called_once_with(volume, provider_location)
|
|
else:
|
|
mock_clone_call.assert_called_once_with(src_vref, volume)
|
|
mock_do_qos.assert_not_called()
|
|
mock_super_create.not_called()
|
|
|
|
def test_do_qos_for_file_flexgroup(self):
|
|
volume = {'provider_location': 'fake'}
|
|
extra_specs = 'fake_extra'
|
|
model = {'provider_location': 'fake'}
|
|
vol_model = {'replication': 'fake'}
|
|
expected_model = {
|
|
'replication': vol_model['replication'],
|
|
'provider_location': model['provider_location'],
|
|
}
|
|
self.mock_object(self.driver, '_do_qos_for_volume')
|
|
self.mock_object(self.driver, '_get_volume_model_update',
|
|
return_value=vol_model)
|
|
mock_extra = self.mock_object(na_utils, 'get_volume_extra_specs',
|
|
return_value=extra_specs)
|
|
|
|
model_updated = self.driver._do_qos_for_file_flexgroup(volume, model)
|
|
|
|
self.assertEqual(model_updated, expected_model)
|
|
mock_extra.assert_called_once_with(volume)
|
|
|
|
def test_do_qos_for_file_flexgroup_error(self):
|
|
self.mock_object(na_utils, 'get_volume_extra_specs',
|
|
side_effect=exception.NotFound)
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver._do_qos_for_file_flexgroup,
|
|
fake.VOLUME, 'fake_model')
|
|
|
|
def test_do_qos_for_volume(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver._do_qos_for_volume,
|
|
fake.NFS_VOLUME,
|
|
fake.EXTRA_SPECS)
|
|
|
|
@ddt.data(True, False)
|
|
def test_create_snapshot(self, is_flexgroup):
|
|
self.mock_object(self.driver, '_is_flexgroup',
|
|
return_value=is_flexgroup)
|
|
self.mock_object(self.driver, '_is_flexgroup_clone_file_supported',
|
|
return_value=not is_flexgroup)
|
|
mock_clone_backing_file_for_volume = self.mock_object(
|
|
self.driver, '_clone_backing_file_for_volume')
|
|
mock_snap_flexgroup = self.mock_object(
|
|
self.driver, '_create_snapshot_for_flexgroup')
|
|
|
|
self.driver.create_snapshot(fake.SNAPSHOT)
|
|
|
|
if is_flexgroup:
|
|
mock_snap_flexgroup.assert_called_once_with(fake.SNAPSHOT)
|
|
mock_clone_backing_file_for_volume.assert_not_called()
|
|
else:
|
|
mock_snap_flexgroup.assert_not_called()
|
|
mock_clone_backing_file_for_volume.assert_called_once_with(
|
|
fake.SNAPSHOT['volume_name'], fake.SNAPSHOT['name'],
|
|
fake.SNAPSHOT['volume_id'], is_snapshot=True)
|
|
|
|
def test_create_snapshot_for_flexgroup(self):
|
|
source_vol = {
|
|
'id': fake.SNAPSHOT['volume_id'],
|
|
'name': fake.SNAPSHOT['volume_name'],
|
|
'volume_type_id': fake.SNAPSHOT['volume_type_id'],
|
|
}
|
|
snap_vol = {
|
|
'name': '%s.%s' % (fake.SNAPSHOT['volume_name'],
|
|
fake.SNAPSHOT['id']),
|
|
'host': fake.HOST_NAME,
|
|
}
|
|
mock_super_snapshot = self.mock_object(nfs.NfsDriver,
|
|
'create_snapshot')
|
|
mock_extra_specs = self.mock_object(na_utils,
|
|
'get_volume_extra_specs')
|
|
mock_extra_specs.return_value = fake.EXTRA_SPECS
|
|
mock_get_info = self.mock_object(na_utils,
|
|
'get_valid_qos_policy_group_info')
|
|
mock_get_info.return_value = fake.QOS_POLICY_GROUP_INFO
|
|
mock_get_host = self.mock_object(self.driver,
|
|
'_get_volume_host')
|
|
mock_get_host.return_value = fake.HOST_NAME
|
|
mock_set_policy = self.mock_object(self.driver,
|
|
'_set_qos_policy_group_on_volume')
|
|
|
|
self.driver._create_snapshot_for_flexgroup(fake.SNAPSHOT)
|
|
|
|
mock_super_snapshot.assert_has_calls([
|
|
mock.call(fake.SNAPSHOT)])
|
|
mock_get_host.assert_has_calls([
|
|
mock.call(source_vol['id'])])
|
|
mock_extra_specs.assert_has_calls([
|
|
mock.call(source_vol)])
|
|
mock_get_info.assert_has_calls([
|
|
mock.call(source_vol, fake.EXTRA_SPECS)])
|
|
mock_set_policy.assert_has_calls([
|
|
mock.call(snap_vol, fake.QOS_POLICY_GROUP_INFO, False)])
|
|
|
|
def test_create_snapshot_for_flexgroup_error(self):
|
|
self.mock_object(nfs.NfsDriver, 'create_snapshot',
|
|
side_effect=exception.NotFound)
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver._create_snapshot_for_flexgroup,
|
|
fake.SNAPSHOT)
|
|
|
|
def test_set_qos_policy_group_on_volume(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver._set_qos_policy_group_on_volume,
|
|
fake.NFS_VOLUME,
|
|
fake.QOS_POLICY_GROUP_INFO,
|
|
False)
|
|
|
|
@ddt.data(True, False)
|
|
def test_delete_snapshot(self, is_flexgroup):
|
|
updates = {
|
|
'name': fake.SNAPSHOT_NAME,
|
|
'volume_size': fake.SIZE,
|
|
'volume_id': fake.VOLUME_ID,
|
|
'volume_name': fake.VOLUME_NAME,
|
|
'busy': False,
|
|
}
|
|
snapshot = fake_snapshot.fake_snapshot_obj(self.ctxt, **updates)
|
|
self.mock_object(self.driver, '_delete_file')
|
|
self.mock_object(self.driver, '_is_flexgroup',
|
|
return_value=is_flexgroup)
|
|
self.mock_object(self.driver, '_is_flexgroup_clone_file_supported',
|
|
return_value=not is_flexgroup)
|
|
mock_super_delete = self.mock_object(nfs.NfsDriver,
|
|
'delete_snapshot')
|
|
|
|
self.driver.delete_snapshot(snapshot)
|
|
|
|
if is_flexgroup:
|
|
mock_super_delete.assert_called_once_with(snapshot)
|
|
self.driver._delete_file.assert_not_called()
|
|
else:
|
|
mock_super_delete.assert_not_called()
|
|
self.driver._delete_file.assert_called_once_with(
|
|
snapshot.volume_id, snapshot.name)
|
|
|
|
@ddt.data(fake.NFS_SHARE, fake.NFS_SHARE_IPV6)
|
|
def test__get_volume_location(self, provider):
|
|
volume_id = fake.VOLUME_ID
|
|
|
|
self.mock_object(self.driver, '_get_provider_location',
|
|
return_value=provider)
|
|
|
|
retval = self.driver._get_volume_location(volume_id)
|
|
|
|
self.assertEqual(provider, retval)
|
|
|
|
def test__clone_backing_file_for_volume(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver._clone_backing_file_for_volume,
|
|
fake.VOLUME_NAME, fake.CLONE_SOURCE_NAME,
|
|
fake.VOLUME_ID, share=None)
|
|
|
|
def test__is_flexgroup(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver._is_flexgroup)
|
|
|
|
def test__get_provider_location(self):
|
|
updates = {'provider_location': fake.PROVIDER_LOCATION}
|
|
volume = fake_volume.fake_volume_obj(self.ctxt, **updates)
|
|
self.mock_object(self.driver.db, 'volume_get', return_value=volume)
|
|
|
|
retval = self.driver._get_provider_location(fake.VOLUME_ID)
|
|
|
|
self.assertEqual(fake.PROVIDER_LOCATION, retval)
|
|
|
|
def test__get_volume_host(self):
|
|
updates = {'host': fake.HOST_NAME}
|
|
volume = fake_volume.fake_volume_obj(self.ctxt, **updates)
|
|
self.mock_object(self.driver.db, 'volume_get', return_value=volume)
|
|
|
|
retval = self.driver._get_volume_host(fake.VOLUME_ID)
|
|
|
|
self.assertEqual(fake.HOST_NAME, retval)
|
|
|
|
@ddt.data(None, processutils.ProcessExecutionError)
|
|
def test__volume_not_present(self, side_effect):
|
|
self.mock_object(self.driver, '_get_volume_path')
|
|
self.mock_object(self.driver, '_try_execute', side_effect=side_effect)
|
|
|
|
retval = self.driver._volume_not_present(
|
|
fake.MOUNT_PATH, fake.VOLUME_NAME)
|
|
|
|
self.assertEqual(side_effect is not None, retval)
|
|
|
|
@mock.patch.object(time, 'sleep')
|
|
def test__try_execute_exception(self, patched_sleep):
|
|
self.mock_object(self.driver, '_execute',
|
|
side_effect=processutils.ProcessExecutionError)
|
|
mock_exception_log = self.mock_object(nfs_base.LOG, 'exception')
|
|
self.driver.configuration.num_shell_tries = 3
|
|
|
|
self.assertRaises(processutils.ProcessExecutionError,
|
|
self.driver._try_execute,
|
|
'fake-command', attr1='val1', attr2='val2')
|
|
self.assertEqual(2, mock_exception_log.call_count)
|
|
self.driver._execute.assert_has_calls([
|
|
mock.call('fake-command', attr1='val1', attr2='val2'),
|
|
mock.call('fake-command', attr1='val1', attr2='val2'),
|
|
mock.call('fake-command', attr1='val1', attr2='val2')])
|
|
self.assertEqual(2, time.sleep.call_count)
|
|
patched_sleep.assert_has_calls([mock.call(1), mock.call(4)])
|
|
|
|
def test__update_volume_stats(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver._update_volume_stats)
|
|
|
|
def test_copy_image_to_volume_base_exception(self):
|
|
mock_info_log = self.mock_object(nfs_base.LOG, 'info')
|
|
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
|
|
self.mock_object(remotefs.RemoteFSDriver, 'copy_image_to_volume',
|
|
side_effect=exception.NfsException)
|
|
|
|
self.assertRaises(exception.NfsException,
|
|
self.driver.copy_image_to_volume,
|
|
'fake_context', fake.NFS_VOLUME,
|
|
'fake_img_service', fake.IMAGE_FILE_ID)
|
|
mock_info_log.assert_not_called()
|
|
|
|
def test_copy_image_to_volume(self):
|
|
mock_log = self.mock_object(nfs_base, 'LOG')
|
|
self.mock_object(self.driver, '_is_flexgroup',
|
|
return_value=False)
|
|
self.mock_object(self.driver, '_is_flexgroup_clone_file_supported',
|
|
return_value=True)
|
|
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
|
|
mock_copy_image = self.mock_object(
|
|
remotefs.RemoteFSDriver, 'copy_image_to_volume')
|
|
mock_register_image = self.mock_object(
|
|
self.driver, '_register_image_in_cache')
|
|
|
|
self.driver.copy_image_to_volume('fake_context',
|
|
fake.NFS_VOLUME,
|
|
'fake_img_service',
|
|
fake.IMAGE_FILE_ID)
|
|
|
|
mock_copy_image.assert_called_once_with(
|
|
'fake_context', fake.NFS_VOLUME, 'fake_img_service',
|
|
fake.IMAGE_FILE_ID, disable_sparse=False)
|
|
self.assertEqual(1, mock_log.info.call_count)
|
|
mock_register_image.assert_called_once_with(
|
|
fake.NFS_VOLUME, fake.IMAGE_FILE_ID)
|
|
|
|
@ddt.data(None, Exception)
|
|
def test__register_image_in_cache(self, exc):
|
|
mock_log = self.mock_object(nfs_base, 'LOG')
|
|
self.mock_object(self.driver, '_do_clone_rel_img_cache',
|
|
side_effect=exc)
|
|
|
|
retval = self.driver._register_image_in_cache(
|
|
fake.NFS_VOLUME, fake.IMAGE_FILE_ID)
|
|
|
|
self.assertIsNone(retval)
|
|
self.assertEqual(exc is not None, mock_log.warning.called)
|
|
self.assertEqual(1, mock_log.info.call_count)
|
|
|
|
@ddt.data(True, False)
|
|
def test_do_clone_rel_img_cache(self, path_exists):
|
|
self.mock_object(nfs_base.LOG, 'info')
|
|
self.mock_object(utils, 'synchronized', return_value=lambda f: f)
|
|
self.mock_object(self.driver, '_get_mount_point_for_share',
|
|
return_value='dir')
|
|
self.mock_object(os.path, 'exists', return_value=path_exists)
|
|
self.mock_object(self.driver, '_clone_backing_file_for_volume')
|
|
self.mock_object(os, 'utime')
|
|
|
|
retval = self.driver._do_clone_rel_img_cache(
|
|
fake.CLONE_SOURCE_NAME, fake.CLONE_DESTINATION_NAME,
|
|
fake.NFS_SHARE, 'fake_cache_file')
|
|
|
|
self.assertIsNone(retval)
|
|
self.assertTrue(self.driver._get_mount_point_for_share.called)
|
|
if not path_exists:
|
|
self.driver._clone_backing_file_for_volume.assert_called_once_with(
|
|
fake.CLONE_SOURCE_NAME, fake.CLONE_DESTINATION_NAME,
|
|
share=fake.NFS_SHARE, volume_id=None)
|
|
os.utime.assert_called_once_with(
|
|
'dir/' + fake.CLONE_SOURCE_NAME, None)
|
|
else:
|
|
self.driver._clone_backing_file_for_volume.assert_not_called()
|
|
os.utime.assert_not_called()
|
|
|
|
os.path.exists.assert_called_once_with(
|
|
'dir/' + fake.CLONE_DESTINATION_NAME)
|
|
|
|
def test_cleanup_volume_on_failure(self):
|
|
path = '%s/%s' % (fake.NFS_SHARE, fake.NFS_VOLUME['name'])
|
|
mock_local_path = self.mock_object(self.driver, 'local_path')
|
|
mock_local_path.return_value = path
|
|
mock_exists_check = self.mock_object(os.path, 'exists')
|
|
mock_exists_check.return_value = True
|
|
mock_delete = self.mock_object(self.driver, '_delete_file_at_path')
|
|
|
|
self.driver._cleanup_volume_on_failure(fake.NFS_VOLUME)
|
|
|
|
mock_delete.assert_has_calls([mock.call(path)])
|
|
|
|
def test_cleanup_volume_on_failure_no_path(self):
|
|
self.mock_object(self.driver, 'local_path')
|
|
mock_exists_check = self.mock_object(os.path, 'exists')
|
|
mock_exists_check.return_value = False
|
|
mock_delete = self.mock_object(self.driver, '_delete_file_at_path')
|
|
|
|
self.driver._cleanup_volume_on_failure(fake.NFS_VOLUME)
|
|
|
|
self.assertEqual(0, mock_delete.call_count)
|
|
|
|
@ddt.data((fake.NFS_SHARE, fake.SHARE_IP),
|
|
(fake.NFS_SHARE_IPV6, fake.IPV6_ADDRESS))
|
|
@ddt.unpack
|
|
def test_get_export_ip_path_volume_id_provided(self, provider_location,
|
|
ip):
|
|
mock_get_host_ip = self.mock_object(self.driver,
|
|
'_get_provider_location')
|
|
mock_get_host_ip.return_value = provider_location
|
|
|
|
expected = (ip, fake.EXPORT_PATH)
|
|
|
|
result = self.driver._get_export_ip_path(fake.VOLUME_ID)
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
@ddt.data((fake.NFS_SHARE, fake.SHARE_IP, fake.EXPORT_PATH),
|
|
(fake.NFS_SHARE_IPV6, fake.IPV6_ADDRESS, fake.EXPORT_PATH))
|
|
@ddt.unpack
|
|
def test_get_export_ip_path_share_provided(self, share, ip, path):
|
|
expected = (ip, path)
|
|
|
|
result = self.driver._get_export_ip_path(share=share)
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_export_ip_path_volume_id_and_share_provided(self):
|
|
mock_get_host_ip = self.mock_object(self.driver,
|
|
'_get_provider_location')
|
|
mock_get_host_ip.return_value = fake.NFS_SHARE_IPV4
|
|
|
|
expected = (fake.IPV4_ADDRESS, fake.EXPORT_PATH)
|
|
|
|
result = self.driver._get_export_ip_path(
|
|
fake.VOLUME_ID, fake.NFS_SHARE)
|
|
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_get_export_ip_path_no_args(self):
|
|
self.assertRaises(exception.InvalidInput,
|
|
self.driver._get_export_ip_path)
|
|
|
|
def test_construct_image_url_loc(self):
|
|
img_loc = fake.FAKE_IMAGE_LOCATION
|
|
|
|
locations = self.driver._construct_image_nfs_url(img_loc)
|
|
|
|
self.assertIn("nfs://host/path/image-id-0", locations)
|
|
self.assertIn("nfs://host/path/image-id-6", locations)
|
|
self.assertEqual(2, len(locations))
|
|
|
|
def test_construct_image_url_direct(self):
|
|
img_loc = ("nfs://host/path/image-id", None)
|
|
|
|
locations = self.driver._construct_image_nfs_url(img_loc)
|
|
|
|
self.assertIn("nfs://host/path/image-id", locations)
|
|
|
|
@ddt.data(None, 'raw', 'qcow2')
|
|
@mock.patch('cinder.objects.volume.Volume.get_by_id')
|
|
def test_extend_volume(self, file_format, mock_get):
|
|
|
|
volume = fake_volume.fake_volume_obj(self.ctxt)
|
|
if file_format:
|
|
volume.admin_metadata = {'format': file_format}
|
|
mock_get.return_value = volume
|
|
new_size = 100
|
|
volume_copy = copy.copy(volume)
|
|
volume_copy['size'] = new_size
|
|
|
|
path = '%s/%s' % (fake.NFS_SHARE, fake.NFS_VOLUME['name'])
|
|
self.mock_object(self.driver,
|
|
'local_path',
|
|
return_value=path)
|
|
mock_resize_image_file = self.mock_object(self.driver,
|
|
'_resize_image_file')
|
|
mock_get_volume_extra_specs = self.mock_object(
|
|
na_utils, 'get_volume_extra_specs', return_value=fake.EXTRA_SPECS)
|
|
mock_do_qos_for_volume = self.mock_object(self.driver,
|
|
'_do_qos_for_volume')
|
|
|
|
self.driver.extend_volume(volume, new_size)
|
|
|
|
mock_resize_image_file.assert_called_once_with(path, new_size,
|
|
file_format=file_format)
|
|
mock_get_volume_extra_specs.assert_called_once_with(volume)
|
|
mock_do_qos_for_volume.assert_called_once_with(volume_copy,
|
|
fake.EXTRA_SPECS,
|
|
cleanup=False)
|
|
|
|
@mock.patch('cinder.objects.volume.Volume.get_by_id')
|
|
def test_extend_volume_resize_error(self, mock_get):
|
|
|
|
volume = fake_volume.fake_volume_obj(self.ctxt)
|
|
mock_get.return_value = volume
|
|
new_size = 100
|
|
volume_copy = copy.copy(volume)
|
|
volume_copy['size'] = new_size
|
|
|
|
path = '%s/%s' % (fake.NFS_SHARE, fake.NFS_VOLUME['name'])
|
|
self.mock_object(self.driver,
|
|
'local_path',
|
|
return_value=path)
|
|
mock_resize_image_file = self.mock_object(
|
|
self.driver, '_resize_image_file',
|
|
side_effect=netapp_api.NaApiError)
|
|
mock_get_volume_extra_specs = self.mock_object(
|
|
na_utils, 'get_volume_extra_specs', return_value=fake.EXTRA_SPECS)
|
|
mock_do_qos_for_volume = self.mock_object(self.driver,
|
|
'_do_qos_for_volume')
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.extend_volume,
|
|
volume,
|
|
new_size)
|
|
|
|
mock_resize_image_file.assert_called_once_with(path, new_size,
|
|
file_format=None)
|
|
self.assertFalse(mock_get_volume_extra_specs.called)
|
|
self.assertFalse(mock_do_qos_for_volume.called)
|
|
|
|
@mock.patch('cinder.objects.volume.Volume.get_by_id')
|
|
def test_extend_volume_qos_error(self, mock_get):
|
|
|
|
volume = fake_volume.fake_volume_obj(self.ctxt)
|
|
mock_get.return_value = volume
|
|
new_size = 100
|
|
volume_copy = copy.copy(volume)
|
|
volume_copy['size'] = new_size
|
|
|
|
path = '%s/%s' % (fake.NFS_SHARE, fake.NFS_VOLUME['name'])
|
|
self.mock_object(self.driver,
|
|
'local_path',
|
|
return_value=path)
|
|
mock_resize_image_file = self.mock_object(self.driver,
|
|
'_resize_image_file')
|
|
mock_get_volume_extra_specs = self.mock_object(
|
|
na_utils, 'get_volume_extra_specs',
|
|
return_value=fake.EXTRA_SPECS)
|
|
mock_do_qos_for_volume = self.mock_object(
|
|
self.driver, '_do_qos_for_volume',
|
|
side_effect=netapp_api.NaApiError)
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.extend_volume,
|
|
volume,
|
|
new_size)
|
|
|
|
mock_resize_image_file.assert_called_once_with(path, new_size,
|
|
file_format=None)
|
|
mock_get_volume_extra_specs.assert_called_once_with(volume)
|
|
mock_do_qos_for_volume.assert_called_once_with(volume_copy,
|
|
fake.EXTRA_SPECS,
|
|
cleanup=False)
|
|
|
|
def test_is_share_clone_compatible(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver._is_share_clone_compatible,
|
|
fake.NFS_VOLUME,
|
|
fake.NFS_SHARE)
|
|
|
|
def test_get_share_mount_and_vol_from_vol_ref(self):
|
|
self.mock_object(volume_utils, 'resolve_hostname',
|
|
return_value='10.12.142.11')
|
|
self.mock_object(os.path, 'isfile', return_value=True)
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
vol_path = "%s/%s" % (self.fake_nfs_export_1, 'test_file_name')
|
|
vol_ref = {'source-name': vol_path}
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
|
|
(share, mount, file_path) = (
|
|
self.driver._get_share_mount_and_vol_from_vol_ref(vol_ref))
|
|
|
|
self.assertEqual(self.fake_nfs_export_1, share)
|
|
self.assertEqual(self.fake_mount_point, mount)
|
|
self.assertEqual('test_file_name', file_path)
|
|
|
|
def test_get_share_mount_and_vol_from_vol_ref_with_bad_ref(self):
|
|
self.mock_object(volume_utils, 'resolve_hostname',
|
|
return_value='10.12.142.11')
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
vol_ref = {'source-id': '1234546'}
|
|
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
|
|
self.assertRaises(exception.ManageExistingInvalidReference,
|
|
self.driver._get_share_mount_and_vol_from_vol_ref,
|
|
vol_ref)
|
|
|
|
def test_get_share_mount_and_vol_from_vol_ref_where_not_found(self):
|
|
self.mock_object(volume_utils, 'resolve_hostname',
|
|
return_value='10.12.142.11')
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
vol_path = "%s/%s" % (self.fake_nfs_export_2, 'test_file_name')
|
|
vol_ref = {'source-name': vol_path}
|
|
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
|
|
self.assertRaises(exception.ManageExistingInvalidReference,
|
|
self.driver._get_share_mount_and_vol_from_vol_ref,
|
|
vol_ref)
|
|
|
|
def test_get_share_mount_and_vol_from_vol_ref_where_is_dir(self):
|
|
self.mock_object(volume_utils, 'resolve_hostname',
|
|
return_value='10.12.142.11')
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
vol_ref = {'source-name': self.fake_nfs_export_2}
|
|
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
|
|
self.assertRaises(exception.ManageExistingInvalidReference,
|
|
self.driver._get_share_mount_and_vol_from_vol_ref,
|
|
vol_ref)
|
|
|
|
@ddt.data(None,
|
|
{'replication_status': fields.ReplicationStatus.ENABLED})
|
|
def test_manage_existing(self, model_update):
|
|
self.mock_object(utils, 'get_file_size',
|
|
return_value=1074253824)
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
test_file = 'test_file_name'
|
|
volume = fake.FAKE_MANAGE_VOLUME
|
|
vol_path = "%s/%s" % (self.fake_nfs_export_1, test_file)
|
|
vol_ref = {'source-name': vol_path}
|
|
self.driver._check_volume_type = mock.Mock()
|
|
self.mock_object(self.driver, '_execute')
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
self.driver._get_share_mount_and_vol_from_vol_ref = mock.Mock(
|
|
return_value=(self.fake_nfs_export_1, self.fake_mount_point,
|
|
test_file))
|
|
mock_get_specs = self.mock_object(na_utils, 'get_volume_extra_specs')
|
|
mock_get_specs.return_value = {}
|
|
self.mock_object(self.driver, '_do_qos_for_volume')
|
|
self.mock_object(self.driver, '_get_volume_model_update',
|
|
return_value=model_update)
|
|
|
|
actual_model_update = self.driver.manage_existing(volume, vol_ref)
|
|
|
|
self.assertEqual(
|
|
self.fake_nfs_export_1, actual_model_update['provider_location'])
|
|
if model_update:
|
|
self.assertEqual(model_update['replication_status'],
|
|
actual_model_update['replication_status'])
|
|
else:
|
|
self.assertNotIn('replication_status', actual_model_update)
|
|
self.driver._check_volume_type.assert_called_once_with(
|
|
volume, self.fake_nfs_export_1, test_file, {})
|
|
|
|
def test_manage_existing_move_fails(self):
|
|
self.mock_object(utils, 'get_file_size', return_value=1074253824)
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
test_file = 'test_file_name'
|
|
volume = fake.FAKE_MANAGE_VOLUME
|
|
vol_path = "%s/%s" % (self.fake_nfs_export_1, test_file)
|
|
vol_ref = {'source-name': vol_path}
|
|
self.driver._check_volume_type = mock.Mock()
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
self.driver._get_share_mount_and_vol_from_vol_ref = mock.Mock(
|
|
return_value=(self.fake_nfs_export_1, self.fake_mount_point,
|
|
test_file))
|
|
self.driver._execute = mock.Mock(
|
|
side_effect=processutils.ProcessExecutionError)
|
|
mock_get_specs = self.mock_object(na_utils, 'get_volume_extra_specs')
|
|
mock_get_specs.return_value = {}
|
|
self.mock_object(self.driver, '_do_qos_for_volume')
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.manage_existing, volume, vol_ref)
|
|
|
|
def test_unmanage(self):
|
|
mock_log = self.mock_object(nfs_base, 'LOG')
|
|
volume = {'id': '123', 'provider_location': '/share'}
|
|
|
|
retval = self.driver.unmanage(volume)
|
|
|
|
self.assertIsNone(retval)
|
|
self.assertEqual(1, mock_log.info.call_count)
|
|
|
|
def test_manage_existing_get_size(self):
|
|
test_file = 'test_file_name'
|
|
self.driver._get_share_mount_and_vol_from_vol_ref = mock.Mock(
|
|
return_value=(self.fake_nfs_export_1, self.fake_mount_point,
|
|
test_file))
|
|
self.mock_object(utils, 'get_file_size', return_value=1073741824)
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
volume = fake.FAKE_MANAGE_VOLUME
|
|
vol_path = "%s/%s" % (self.fake_nfs_export_1, test_file)
|
|
vol_ref = {'source-name': vol_path}
|
|
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
|
|
vol_size = self.driver.manage_existing_get_size(volume, vol_ref)
|
|
|
|
self.assertEqual(1, vol_size)
|
|
|
|
def test_manage_existing_get_size_round_up(self):
|
|
test_file = 'test_file_name'
|
|
self.driver._get_share_mount_and_vol_from_vol_ref = mock.Mock(
|
|
return_value=(self.fake_nfs_export_1, self.fake_mount_point,
|
|
test_file))
|
|
self.mock_object(utils, 'get_file_size', return_value=1073760270)
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
volume = fake.FAKE_MANAGE_VOLUME
|
|
vol_path = "%s/%s" % (self.fake_nfs_export_1, test_file)
|
|
vol_ref = {'source-name': vol_path}
|
|
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
|
|
vol_size = self.driver.manage_existing_get_size(volume, vol_ref)
|
|
|
|
self.assertEqual(2, vol_size)
|
|
|
|
def test_manage_existing_get_size_error(self):
|
|
test_file = 'test_file_name'
|
|
self.driver._get_share_mount_and_vol_from_vol_ref = mock.Mock(
|
|
return_value=(self.fake_nfs_export_1, self.fake_mount_point,
|
|
test_file))
|
|
self.driver._mounted_shares = [self.fake_nfs_export_1]
|
|
volume = fake.FAKE_MANAGE_VOLUME
|
|
vol_path = "%s/%s" % (self.fake_nfs_export_1, test_file)
|
|
vol_ref = {'source-name': vol_path}
|
|
|
|
self.driver._ensure_shares_mounted = mock.Mock()
|
|
self.driver._get_mount_point_for_share = mock.Mock(
|
|
return_value=self.fake_mount_point)
|
|
|
|
self.assertRaises(exception.VolumeBackendAPIException,
|
|
self.driver.manage_existing_get_size,
|
|
volume,
|
|
vol_ref)
|
|
|
|
@ddt.data(True, False)
|
|
def test_delete_file(self, volume_not_present):
|
|
mock_get_provider_location = self.mock_object(
|
|
self.driver, '_get_provider_location')
|
|
mock_get_provider_location.return_value = fake.NFS_SHARE
|
|
mock_volume_not_present = self.mock_object(
|
|
self.driver, '_volume_not_present')
|
|
mock_volume_not_present.return_value = volume_not_present
|
|
mock_get_volume_path = self.mock_object(
|
|
self.driver, '_get_volume_path')
|
|
mock_get_volume_path.return_value = fake.PATH
|
|
mock_delete = self.mock_object(self.driver, '_delete')
|
|
|
|
self.driver._delete_file(fake.CG_VOLUME_ID, fake.CG_VOLUME_NAME)
|
|
|
|
mock_get_provider_location.assert_called_once_with(fake.CG_VOLUME_ID)
|
|
mock_volume_not_present.assert_called_once_with(
|
|
fake.NFS_SHARE, fake.CG_VOLUME_NAME)
|
|
if not volume_not_present:
|
|
mock_get_volume_path.assert_called_once_with(
|
|
fake.NFS_SHARE, fake.CG_VOLUME_NAME)
|
|
mock_delete.assert_called_once_with(fake.PATH)
|
|
|
|
def test_delete_file_volume_not_present(self):
|
|
mock_get_provider_location = self.mock_object(
|
|
self.driver, '_get_provider_location')
|
|
mock_get_provider_location.return_value = fake.NFS_SHARE
|
|
mock_volume_not_present = self.mock_object(
|
|
self.driver, '_volume_not_present')
|
|
mock_volume_not_present.return_value = True
|
|
mock_get_volume_path = self.mock_object(
|
|
self.driver, '_get_volume_path')
|
|
mock_delete = self.mock_object(self.driver, '_delete')
|
|
|
|
self.driver._delete_file(fake.CG_VOLUME_ID, fake.CG_VOLUME_NAME)
|
|
|
|
mock_get_provider_location.assert_called_once_with(fake.CG_VOLUME_ID)
|
|
mock_volume_not_present.assert_called_once_with(
|
|
fake.NFS_SHARE, fake.CG_VOLUME_NAME)
|
|
mock_get_volume_path.assert_not_called()
|
|
mock_delete.assert_not_called()
|
|
|
|
def test_check_for_setup_error(self):
|
|
super_check_for_setup_error = self.mock_object(
|
|
nfs.NfsDriver, 'check_for_setup_error')
|
|
mock_start_tasks = self.mock_object(
|
|
self.driver.loopingcalls, 'start_tasks')
|
|
|
|
self.driver.check_for_setup_error()
|
|
|
|
super_check_for_setup_error.assert_called_once_with()
|
|
mock_start_tasks.assert_called_once_with()
|
|
|
|
def test_add_looping_tasks(self):
|
|
mock_add_task = self.mock_object(self.driver.loopingcalls, 'add_task')
|
|
mock_call_snap_cleanup = self.mock_object(
|
|
self.driver, '_delete_snapshots_marked_for_deletion')
|
|
mock_call_ems_logging = self.mock_object(
|
|
self.driver, '_handle_ems_logging')
|
|
mock_call_clean_image_cache = self.mock_object(
|
|
self.driver, '_clean_image_cache')
|
|
|
|
# image cache cleanup task can be configured with custom timeout
|
|
cache_cleanup_interval = loopingcalls.ONE_HOUR
|
|
self.driver.configuration.netapp_nfs_image_cache_cleanup_interval = (
|
|
cache_cleanup_interval)
|
|
|
|
self.driver._add_looping_tasks()
|
|
|
|
mock_add_task.assert_has_calls([
|
|
mock.call(mock_call_snap_cleanup, loopingcalls.ONE_MINUTE,
|
|
loopingcalls.ONE_MINUTE),
|
|
mock.call(mock_call_ems_logging, loopingcalls.ONE_HOUR),
|
|
mock.call(mock_call_clean_image_cache, cache_cleanup_interval)
|
|
])
|
|
|
|
def test__clone_from_cache(self):
|
|
image_id = 'fake_image_id'
|
|
cache_result = [
|
|
('fakepool_bad1', '/fakepath/img-cache-1'),
|
|
('fakepool', '/fakepath/img-cache-2'),
|
|
('fakepool_bad2', '/fakepath/img-cache-3'),
|
|
]
|
|
mock_call__is_share_clone_compatible = self.mock_object(
|
|
self.driver, '_is_share_clone_compatible')
|
|
mock_call__is_share_clone_compatible.return_value = True
|
|
mock_call__do_clone_rel_img_cache = self.mock_object(
|
|
self.driver, '_do_clone_rel_img_cache')
|
|
cloned = self.driver._clone_from_cache(fake.test_volume, image_id,
|
|
cache_result)
|
|
self.assertTrue(cloned)
|
|
mock_call__is_share_clone_compatible.assert_called_once_with(
|
|
fake.test_volume, 'fakepool')
|
|
mock_call__do_clone_rel_img_cache.assert_called_once_with(
|
|
'/fakepath/img-cache-2', 'fakename', 'fakepool',
|
|
'/fakepath/img-cache-2'
|
|
)
|
|
|
|
def test__clone_from_cache_not_found(self):
|
|
image_id = 'fake_image_id'
|
|
cache_result = [
|
|
('fakepool_bad1', '/fakepath/img-cache-1'),
|
|
('fakepool_bad2', '/fakepath/img-cache-2'),
|
|
('fakepool_bad3', '/fakepath/img-cache-3'),
|
|
]
|
|
mock_call__is_share_clone_compatible = self.mock_object(
|
|
self.driver, '_is_share_clone_compatible')
|
|
mock_call__do_clone_rel_img_cache = self.mock_object(
|
|
self.driver, '_do_clone_rel_img_cache')
|
|
cloned = self.driver._clone_from_cache(fake.test_volume, image_id,
|
|
cache_result)
|
|
self.assertFalse(cloned)
|
|
mock_call__is_share_clone_compatible.assert_not_called()
|
|
mock_call__do_clone_rel_img_cache.assert_not_called()
|
|
|
|
def test__find_share(self):
|
|
mock_extract = self.mock_object(volume_utils, 'extract_host',
|
|
return_value=fake.POOL_NAME)
|
|
|
|
pool_name = self.driver._find_share(fake.VOLUME)
|
|
|
|
self.assertEqual(pool_name, fake.POOL_NAME)
|
|
mock_extract.assert_called_once_with(fake.VOLUME['host'],
|
|
level='pool')
|
|
|
|
def test__find_share_error(self):
|
|
mock_extract = self.mock_object(volume_utils, 'extract_host',
|
|
return_value=None)
|
|
|
|
self.assertRaises(exception.InvalidHost,
|
|
self.driver._find_share,
|
|
fake.VOLUME)
|
|
mock_extract.assert_called_once_with(fake.VOLUME['host'],
|
|
level='pool')
|
|
|
|
def test__ensure_flexgroup_not_in_cg_raises(self):
|
|
self.mock_object(self.driver, '_is_flexgroup',
|
|
return_value=True)
|
|
self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
|
return_value=True)
|
|
fake_v1 = {
|
|
'group': 'fake_group',
|
|
'host': 'fake_host',
|
|
'id': 'fake_id'
|
|
}
|
|
self.assertRaises(na_utils.NetAppDriverException,
|
|
self.driver._ensure_flexgroup_not_in_cg,
|
|
fake_v1)
|
|
|
|
def test__is_flexgroup_clone_file_supported(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver._is_flexgroup_clone_file_supported)
|
|
|
|
def test_update_migrated_volume(self):
|
|
self.assertRaises(NotImplementedError,
|
|
self.driver.update_migrated_volume, self.ctxt,
|
|
fake.test_volume, mock.sentinel.new_volume,
|
|
mock.sentinel.original_status)
|
|
|
|
@ddt.data({'is_flexgroup': False, 'is_flexgroup_supported': False},
|
|
{'is_flexgroup': False, 'is_flexgroup_supported': True},
|
|
{'is_flexgroup': True, 'is_flexgroup_supported': False},
|
|
{'is_flexgroup': True, 'is_flexgroup_supported': True})
|
|
@ddt.unpack
|
|
def test_revert_to_snapshot(self, is_flexgroup, is_flexgroup_supported):
|
|
context = {}
|
|
|
|
self.mock_object(self.driver,
|
|
'_is_flexgroup',
|
|
return_value=is_flexgroup)
|
|
self.mock_object(self.driver,
|
|
'_is_flexgroup_clone_file_supported',
|
|
return_value=is_flexgroup_supported)
|
|
mock_revert_to_snapshot = self.mock_object(
|
|
remotefs.RemoteFSSnapDriverDistributed, 'revert_to_snapshot')
|
|
mock__revert_to_snapshot = self.mock_object(self.driver,
|
|
'_revert_to_snapshot')
|
|
|
|
self.driver.revert_to_snapshot(context, fake.SNAPSHOT_VOLUME,
|
|
fake.SNAPSHOT)
|
|
|
|
if is_flexgroup and not is_flexgroup_supported:
|
|
mock_revert_to_snapshot.assert_called_once_with(
|
|
context, fake.SNAPSHOT_VOLUME, fake.SNAPSHOT)
|
|
mock__revert_to_snapshot.assert_not_called()
|
|
else:
|
|
mock__revert_to_snapshot.assert_called_once_with(
|
|
fake.SNAPSHOT_VOLUME, fake.SNAPSHOT)
|
|
mock_revert_to_snapshot.assert_not_called()
|
|
|
|
def test__revert_to_snapshot(self):
|
|
self.assertRaises(NotImplementedError, self.driver._revert_to_snapshot,
|
|
fake.SNAPSHOT_VOLUME, fake.SNAPSHOT)
|