From e9ca4344866f90ac32eb6644a1d180313031d785 Mon Sep 17 00:00:00 2001 From: liucheng Date: Mon, 21 Dec 2015 15:22:53 +0800 Subject: [PATCH] Huawei: Create share from snapshot support in Huawei driver Once create share from snapshot is in Manila core, we should support the API in the Huawei drivers. Implements: blueprint huawei-driver-create-share-from-snapshot Change-Id: I906916f6ae7f5625863ca9f8e3324480048374de --- ...hare_back_ends_feature_support_mapping.rst | 2 +- manila/exception.py | 12 + manila/share/drivers/huawei/base.py | 5 + manila/share/drivers/huawei/constants.py | 2 + manila/share/drivers/huawei/huawei_nas.py | 8 + manila/share/drivers/huawei/v3/connection.py | 250 +++++++++++++ manila/share/drivers/huawei/v3/helper.py | 4 + .../share/drivers/huawei/test_huawei_nas.py | 351 +++++++++++++++++- 8 files changed, 625 insertions(+), 9 deletions(-) diff --git a/doc/source/devref/share_back_ends_feature_support_mapping.rst b/doc/source/devref/share_back_ends_feature_support_mapping.rst index 30985cfa2a..ef1578af9d 100644 --- a/doc/source/devref/share_back_ends_feature_support_mapping.rst +++ b/doc/source/devref/share_back_ends_feature_support_mapping.rst @@ -51,7 +51,7 @@ Mapping of share drivers and share features support +----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+ | HPE 3PAR | DHSS = True (L) & False (K) | \- | \- | \- | K | K | +----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+ -| Huawei | DHSS = True (M) & False(K) | L | L | L | K | \- | +| Huawei | DHSS = True (M) & False(K) | L | L | L | K | M | +----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+ | IBM GPFS | DHSS = False(K) | \- | L | \- | K | K | +----------------------------------------+-----------------------------+-----------------------+--------------+--------------+------------------------+----------------------------+ diff --git a/manila/exception.py b/manila/exception.py index c56dfe9c51..67e162b05e 100644 --- a/manila/exception.py +++ b/manila/exception.py @@ -706,3 +706,15 @@ class DriverNotInitialized(ManilaException): class ShareResourceNotFound(StorageResourceNotFound): message = _("Share id %(share_id)s could not be found " "in storage backend.") + + +class ShareUmountException(ManilaException): + message = _("Failed to unmount share: %(reason)s") + + +class ShareMountException(ManilaException): + message = _("Failed to mount share: %(reason)s") + + +class ShareCopyDataException(ManilaException): + message = _("Failed to copy data: %(reason)s") diff --git a/manila/share/drivers/huawei/base.py b/manila/share/drivers/huawei/base.py index 5d579f0a85..add2ab05b6 100644 --- a/manila/share/drivers/huawei/base.py +++ b/manila/share/drivers/huawei/base.py @@ -59,6 +59,11 @@ class HuaweiBase(object): def extend_share(self, share, new_size, share_server): """Extends size of existing share.""" + @abc.abstractmethod + def create_share_from_snapshot(self, share, snapshot, + share_server=None): + """Create share from snapshot.""" + @abc.abstractmethod def shrink_share(self, share, new_size, share_server): """Shrinks size of existing share.""" diff --git a/manila/share/drivers/huawei/constants.py b/manila/share/drivers/huawei/constants.py index b277e8a94a..34801650b4 100644 --- a/manila/share/drivers/huawei/constants.py +++ b/manila/share/drivers/huawei/constants.py @@ -33,6 +33,8 @@ LOGIN_SOCKET_TIMEOUT = 4 QOS_NAME_PREFIX = 'OpenStack_' SYSTEM_NAME_PREFIX = "Array-" ARRAY_VERSION = 'V300R003C00' +TMP_PATH_SRC_PREFIX = "huawei_manila_tmp_path_src_" +TMP_PATH_DST_PREFIX = "huawei_manila_tmp_path_dst_" ACCESS_NFS_RW = "1" ACCESS_NFS_RO = "0" diff --git a/manila/share/drivers/huawei/huawei_nas.py b/manila/share/drivers/huawei/huawei_nas.py index 76fec7c7cf..602311de87 100644 --- a/manila/share/drivers/huawei/huawei_nas.py +++ b/manila/share/drivers/huawei/huawei_nas.py @@ -55,6 +55,7 @@ class HuaweiNasDriver(driver.ShareDriver): 1.2 - Add share server support. Add ensure share. Add QoS support. + Add create share from snapshot. """ def __init__(self, *args, **kwargs): @@ -112,6 +113,13 @@ class HuaweiNasDriver(driver.ShareDriver): LOG.debug("Extend a share.") self.plugin.extend_share(share, new_size, share_server) + def create_share_from_snapshot(self, context, share, snapshot, + share_server=None): + """Create a share from snapshot.""" + LOG.debug("Create a share from snapshot %s.", snapshot['snapshot_id']) + location = self.plugin.create_share_from_snapshot(share, snapshot) + return location + def shrink_share(self, share, new_size, share_server=None): """Shrinks size of existing share.""" LOG.debug("Shrink a share.") diff --git a/manila/share/drivers/huawei/v3/connection.py b/manila/share/drivers/huawei/v3/connection.py index fb2cc71841..2c430ccbc2 100644 --- a/manila/share/drivers/huawei/v3/connection.py +++ b/manila/share/drivers/huawei/v3/connection.py @@ -13,18 +13,23 @@ # License for the specific language governing permissions and limitations # under the License. +import os import random import string +import tempfile import time from oslo_log import log from oslo_serialization import jsonutils +from oslo_utils import excutils from oslo_utils import strutils from oslo_utils import units +import six from manila.common import constants as common_constants from manila import exception from manila.i18n import _ +from manila.i18n import _LE from manila.i18n import _LI from manila.i18n import _LW from manila.share.drivers.huawei import base as driver @@ -328,6 +333,241 @@ class V3StorageConnection(driver.HuaweiBase): return share + def create_share_from_snapshot(self, share, snapshot, + share_server=None): + """Create a share from snapshot.""" + share_fs_id = self.helper._get_fsid_by_name(snapshot['share_name']) + if not share_fs_id: + err_msg = (_("The source filesystem of snapshot %s " + "does not exist.") + % snapshot['snapshot_id']) + LOG.error(err_msg) + raise exception.StorageResourceNotFound( + name=snapshot['share_name']) + + snapshot_id = self.helper._get_snapshot_id(share_fs_id, snapshot['id']) + snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_id) + if not snapshot_flag: + err_msg = (_("Cannot find snapshot %s on array.") + % snapshot['snapshot_id']) + LOG.error(err_msg) + raise exception.ShareSnapshotNotFound( + snapshot_id=snapshot['snapshot_id']) + + self.assert_filesystem(share_fs_id) + + old_share_name = self.helper.get_share_name_by_id( + snapshot['share_id']) + old_share_proto = self._get_share_proto(old_share_name) + if not old_share_proto: + err_msg = (_("Cannot find source share %(share)s of " + "snapshot %(snapshot)s on array.") + % {'share': snapshot['share_id'], + 'snapshot': snapshot['snapshot_id']}) + LOG.error(err_msg) + raise exception.ShareResourceNotFound( + share_id=snapshot['share_id']) + + new_share_path = self.create_share(share) + new_share = { + "share_proto": share['share_proto'], + "size": share['size'], + "name": share['name'], + "mount_path": new_share_path.replace("\\", "/"), + "mount_src": + tempfile.mkdtemp(prefix=constants.TMP_PATH_DST_PREFIX), + } + + old_share_path = self._get_location_path(old_share_name, + old_share_proto) + old_share = { + "share_proto": old_share_proto, + "name": old_share_name, + "mount_path": old_share_path.replace("\\", "/"), + "mount_src": + tempfile.mkdtemp(prefix=constants.TMP_PATH_SRC_PREFIX), + "snapshot_name": ("share_snapshot_" + + snapshot['id'].replace("-", "_")) + } + + try: + self.copy_data_from_parent_share(old_share, new_share) + except Exception: + with excutils.save_and_reraise_exception(): + self.delete_share(new_share) + finally: + for item in (new_share, old_share): + try: + os.rmdir(item['mount_src']) + except Exception as err: + err_msg = (_('Failed to remove temp file. ' + 'File path: %(file_path)s. Reason: %(err)s.') + % {'file_path': item['mount_src'], + 'err': six.text_type(err)}) + LOG.warning(err_msg) + + return new_share_path + + def copy_data_from_parent_share(self, old_share, new_share): + old_access = self.get_access(old_share) + old_access_id = self._get_access_id(old_share, old_access) + if not old_access_id: + try: + self.allow_access(old_share, old_access) + except exception.ManilaException as err: + with excutils.save_and_reraise_exception(): + LOG.error(_LE('Failed to add access to share %(name)s. ' + 'Reason: %(err)s.') + % {'name': old_share['name'], + 'err': six.text_type(err)}) + + new_access = self.get_access(new_share) + try: + try: + self.mount_share_to_host(old_share, old_access) + except exception.ShareMountException as err: + with excutils.save_and_reraise_exception(): + LOG.error(_LE('Failed to mount old share %(name)s. ' + 'Reason: %(err)s.') + % {'name': old_share['name'], + 'err': six.text_type(err)}) + + try: + self.allow_access(new_share, new_access) + self.mount_share_to_host(new_share, new_access) + except Exception as err: + with excutils.save_and_reraise_exception(): + self.umount_share_from_host(old_share) + LOG.error(_LE('Failed to mount new share %(name)s. ' + 'Reason: %(err)s.') + % {'name': new_share['name'], + 'err': six.text_type(err)}) + + copied = self.copy_snapshot_data(old_share, new_share) + + for item in (new_share, old_share): + try: + self.umount_share_from_host(item) + except exception.ShareUmountException as err: + err_msg = (_('Failed to unmount share %(name)s. ' + 'Reason: %(err)s.') + % {'name': item['name'], + 'err': six.text_type(err)}) + LOG.warning(err_msg) + + self.deny_access(new_share, new_access) + + if copied: + LOG.debug("Created share from snapshot successfully, " + "new_share: %s, old_share: %s.", + new_share, old_share) + else: + message = (_('Failed to copy data from share %(old_share)s ' + 'to share %(new_share)s.') + % {'old_share': old_share['name'], + 'new_share': new_share['name']}) + raise exception.ShareCopyDataException(reason=message) + finally: + if not old_access_id: + self.deny_access(old_share, old_access) + + def get_access(self, share): + share_proto = share['share_proto'] + access = {} + root = self.helper._read_xml() + + if share_proto == 'NFS': + access['access_to'] = root.findtext('Filesystem/NFSClient/IP') + access['access_level'] = common_constants.ACCESS_LEVEL_RW + access['access_type'] = 'ip' + elif share_proto == 'CIFS': + access['access_to'] = root.findtext( + 'Filesystem/CIFSClient/UserName') + access['access_password'] = root.findtext( + 'Filesystem/CIFSClient/UserPassword') + access['access_level'] = common_constants.ACCESS_LEVEL_RW + access['access_type'] = 'user' + + LOG.debug("Get access for share: %s, access_type: %s, access_to: %s, " + "access_level: %s", share['name'], access['access_type'], + access['access_to'], access['access_level']) + return access + + def _get_access_id(self, share, access): + """Get access id of the share.""" + access_id = None + share_name = share['name'] + share_url_type = self.helper._get_share_url_type(share['share_proto']) + share_client_type = self.helper._get_share_client_type( + share['share_proto']) + access_to = access['access_to'] + share = self.helper._get_share_by_name(share_name, share_url_type) + access_id = self.helper._get_access_from_share(share['ID'], access_to, + share_client_type) + if access_id is None: + LOG.debug('Cannot get access ID from share. ' + 'share_name: %s', share_name) + + return access_id + + def copy_snapshot_data(self, old_share, new_share): + src_path = '/'.join((old_share['mount_src'], '.snapshot', + old_share['snapshot_name'])) + dst_path = new_share['mount_src'] + copy_finish = False + LOG.debug("Copy data from src_path: %s to dst_path: %s.", + src_path, dst_path) + try: + ignore_list = '' + copy = share_utils.Copy(src_path, + dst_path, + ignore_list) + copy.run() + if copy.get_progress()['total_progress'] == 100: + copy_finish = True + except Exception as err: + err_msg = (_("Failed to copy data, reason: %s.") + % six.text_type(err)) + LOG.error(err_msg) + + return copy_finish + + def umount_share_from_host(self, share): + try: + utils.execute('umount', share['mount_path'], + run_as_root=True) + except Exception as err: + message = (_("Failed to unmount share %(share)s. " + "Reason: %(reason)s.") + % {'share': share['name'], + 'reason': six.text_type(err)}) + raise exception.ShareUmountException(reason=message) + + def mount_share_to_host(self, share, access): + LOG.debug("Mounting share: %s to host, mount_src: %s", + share['name'], share['mount_src']) + try: + if share['share_proto'] == 'NFS': + utils.execute('mount', '-t', 'nfs', + share['mount_path'], share['mount_src'], + run_as_root=True) + + LOG.debug("Execute mount. mount_src: %s", + share['mount_src']) + + elif share['share_proto'] == 'CIFS': + user = ('user=' + access['access_to'] + ',' + + 'password=' + access['access_password']) + utils.execute('mount', '-t', 'cifs', + share['mount_path'], share['mount_src'], + '-o', user, run_as_root=True) + except Exception as err: + message = (_('Bad response from mount share: %(share)s. ' + 'Reason: %(reason)s.') + % {'share': share['name'], + 'reason': six.text_type(err)}) + raise exception.ShareMountException(reason=message) + def get_network_allocations_number(self): """Get number of network interfaces to be created.""" if self.configuration.driver_handles_share_servers: @@ -799,6 +1039,16 @@ class V3StorageConnection(driver.HuaweiBase): return location + def _get_share_proto(self, share_name): + share_proto = None + for proto in ('NFS', 'CIFS'): + share_url_type = self.helper._get_share_url_type(proto) + share = self.helper._get_share_by_name(share_name, share_url_type) + if share: + share_proto = proto + break + return share_proto + def _get_wait_interval(self): """Get wait interval from huawei conf file.""" root = self.helper._read_xml() diff --git a/manila/share/drivers/huawei/v3/helper.py b/manila/share/drivers/huawei/v3/helper.py index c159d89367..12339ee9b4 100644 --- a/manila/share/drivers/huawei/v3/helper.py +++ b/manila/share/drivers/huawei/v3/helper.py @@ -660,6 +660,10 @@ class RestHelper(object): share_path = "/" + share_name.replace("-", "_") + "/" return share_path + def get_share_name_by_id(self, share_id): + share_name = "share_" + share_id + return share_name + def _get_share_name_by_export_location(self, export_location, share_proto): export_location_split = None share_name = None diff --git a/manila/tests/share/drivers/huawei/test_huawei_nas.py b/manila/tests/share/drivers/huawei/test_huawei_nas.py index d57c9791e3..90523e717e 100644 --- a/manila/tests/share/drivers/huawei/test_huawei_nas.py +++ b/manila/tests/share/drivers/huawei/test_huawei_nas.py @@ -34,7 +34,9 @@ from manila.share.drivers.huawei import huawei_nas from manila.share.drivers.huawei.v3 import connection from manila.share.drivers.huawei.v3 import helper from manila.share.drivers.huawei.v3 import smartx +from manila.share import utils as share_utils from manila import test +from manila import utils def fake_sleep(time): @@ -283,8 +285,8 @@ def dec_driver_handles_share_servers(func): def QoS_response(method): if method == "GET": data = """{"error":{"code":0}, - "data":[{"NAME": "OpenStack_Fake_QoS", "MAXIOPS": "100", - "FSLIST": 4, "LUNLIST": ""}]}""" + "data":{"NAME": "OpenStack_Fake_QoS", "MAXIOPS": "100", + "FSLIST": "4", "LUNLIST": "", "RUNNINGSTATUS": "2"}}""" elif method == "PUT": data = """{"error":{"code":0}}""" else: @@ -515,7 +517,7 @@ class FakeHuaweiNasHelper(helper.RestHelper): "RUNNINGSTATUS":"2"}}""" else: data = """{"error":{"code":0},"data":{ - "RUNNINGSTATUS":"1"}}""" + "RUNNINGSTATUS":"1"}}""" if url == "/NFSSERVICE": if self.service_nfs_status_flag: @@ -732,6 +734,7 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_nfs = { 'id': 'fake_uuid', + 'share_id': 'fake_uuid', 'project_id': 'fake_tenant_id', 'display_name': 'fake', 'name': 'share-fake-uuid', @@ -857,6 +860,7 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_cifs = { 'id': 'fake_uuid', + 'share_id': 'fake_uuid', 'project_id': 'fake_tenant_id', 'display_name': 'fake', 'name': 'share-fake-uuid', @@ -889,10 +893,12 @@ class HuaweiShareDriverTestCase(test.TestCase): self.nfs_snapshot = { 'id': 'fake_snapshot_uuid', + 'snapshot_id': 'fake_snapshot_uuid', 'display_name': 'snapshot', 'name': 'fake_snapshot_name', 'size': 1, 'share_name': 'share_fake_uuid', + 'share_id': 'fake_uuid', 'share': { 'share_name': 'share_fake_uuid', 'share_id': 'fake_uuid', @@ -903,10 +909,12 @@ class HuaweiShareDriverTestCase(test.TestCase): self.cifs_snapshot = { 'id': 'fake_snapshot_uuid', + 'snapshot_id': 'fake_snapshot_uuid', 'display_name': 'snapshot', 'name': 'fake_snapshot_name', 'size': 1, 'share_name': 'share_fake_uuid', + 'share_id': 'fake_uuid', 'share': { 'share_name': 'share_fake_uuid', 'share_id': 'fake_uuid', @@ -1665,11 +1673,318 @@ class HuaweiShareDriverTestCase(test.TestCase): number = self.driver.get_network_allocations_number() self.assertEqual(0, number) - def test_create_share_from_snapshot(self): - self.assertRaises(NotImplementedError, + def test_create_nfsshare_from_nfssnapshot_success(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + 'mount_share_to_host', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin, + 'copy_snapshot_data', + mock.Mock(return_value=True)) + self.mock_object(self.driver.plugin, + 'umount_share_from_host', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + location = self.driver.create_share_from_snapshot(self._context, + self.share_nfs, + self.nfs_snapshot, + self.share_server) + + self.assertTrue(db.share_type_get.called) + self.assertEqual(2, self.driver.plugin. + mount_share_to_host.call_count) + self.assertTrue(self.driver.plugin. + copy_snapshot_data.called) + self.assertEqual(2, self.driver.plugin. + umount_share_from_host.call_count) + self.assertEqual("100.115.10.68:/share_fake_uuid", location) + + def test_create_cifsshare_from_cifssnapshot_success(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + 'mount_share_to_host', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin, + 'copy_snapshot_data', + mock.Mock(return_value=True)) + self.mock_object(self.driver.plugin, + 'umount_share_from_host', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + location = self.driver.create_share_from_snapshot(self._context, + self.share_cifs, + self.cifs_snapshot, + self.share_server) + + self.assertTrue(db.share_type_get.called) + self.assertEqual(2, self.driver.plugin. + mount_share_to_host.call_count) + self.assertTrue(self.driver.plugin. + copy_snapshot_data.called) + self.assertEqual(2, self.driver.plugin. + umount_share_from_host.call_count) + self.assertEqual("\\\\100.115.10.68\\share_fake_uuid", location) + + def test_create_nfsshare_from_cifssnapshot_success(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + '_get_access_id', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin, + 'mount_share_to_host', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin, + 'copy_snapshot_data', + mock.Mock(return_value=True)) + self.mock_object(self.driver.plugin, + 'umount_share_from_host', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.access_id = None + self.driver.plugin.helper.snapshot_flag = True + + location = self.driver.create_share_from_snapshot(self._context, + self.share_nfs, + self.cifs_snapshot, + self.share_server) + + self.assertTrue(db.share_type_get.called) + self.assertTrue(self.driver.plugin. + _get_access_id.called) + self.assertEqual(2, self.driver.plugin. + mount_share_to_host.call_count) + self.assertTrue(self.driver.plugin. + copy_snapshot_data.called) + self.assertEqual(2, self.driver.plugin. + umount_share_from_host.call_count) + self.assertEqual("100.115.10.68:/share_fake_uuid", location) + + def test_create_cifsshare_from_nfssnapshot_success(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + '_get_access_id', + mock.Mock(return_value={})) + self.mock_object(utils, + 'execute', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + location = self.driver.create_share_from_snapshot(self._context, + self.share_cifs, + self.nfs_snapshot, + self.share_server) + + self.assertTrue(db.share_type_get.called) + self.assertTrue(self.driver.plugin. + _get_access_id.called) + self.assertEqual(4, utils.execute.call_count) + self.assertEqual("\\\\100.115.10.68\\share_fake_uuid", location) + + def test_create_share_from_snapshot_nonefs(self): + self.driver.plugin.helper.login() + self.mock_object(self.driver.plugin.helper, + '_get_fsid_by_name', + mock.Mock(return_value={})) + self.assertRaises(exception.StorageResourceNotFound, self.driver.create_share_from_snapshot, - self._context, self.share_nfs, self.nfs_snapshot, - self.share_server) + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + self.assertTrue(self.driver.plugin.helper. + _get_fsid_by_name.called) + + def test_create_share_from_notexistingsnapshot_fail(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = False + self.assertRaises(exception.ShareSnapshotNotFound, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + + def test_create_share_from_share_fail(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + self.mock_object(self.driver.plugin, + 'check_fs_status', + mock.Mock(return_value={})) + self.assertRaises(exception.StorageResourceException, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + self.assertTrue(self.driver.plugin.check_fs_status.called) + + def test_create_share_from_snapshot_share_error(self): + self.mock_object(self.driver.plugin, + '_get_share_proto', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + self.assertRaises(exception.ShareResourceNotFound, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + self.assertTrue(self.driver.plugin. + _get_share_proto.called) + + def test_create_share_from_snapshot_allow_oldaccess_fail(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + '_get_share_proto', + mock.Mock(return_value='NFS')) + self.mock_object(self.driver.plugin, + '_get_access_id', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin.helper, + '_get_share_by_name', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + self.assertRaises(exception.InvalidShareAccess, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + self.assertTrue(db.share_type_get.called) + self.assertTrue(self.driver.plugin._get_share_proto.called) + self.assertTrue(self.driver.plugin._get_access_id.called) + self.assertTrue(self.driver.plugin.helper._get_share_by_name.called) + + def test_create_share_from_snapshot_mountshare_fail(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + 'mount_share_to_host', + mock.Mock(side_effect=exception. + ShareMountException('err'))) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + self.assertRaises(exception.ShareMountException, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + self.assertTrue(db.share_type_get.called) + self.assertEqual(1, self.driver.plugin. + mount_share_to_host.call_count) + + def test_create_share_from_snapshot_allow_newaccess_fail(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + '_get_share_proto', + mock.Mock(return_value='NFS')) + self.mock_object(self.driver.plugin, + '_get_access_id', + mock.Mock(return_value='5')) + self.mock_object(self.driver.plugin, + 'mount_share_to_host', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin.helper, + '_get_share_by_name', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin, + 'umount_share_from_host', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + self.assertRaises(exception.InvalidShareAccess, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + self.assertTrue(db.share_type_get.called) + self.assertTrue(self.driver.plugin._get_share_proto.called) + self.assertTrue(self.driver.plugin._get_access_id.called) + self.assertEqual(1, self.driver.plugin. + mount_share_to_host.call_count) + self.assertTrue(self.driver.plugin.helper. + _get_share_by_name.called) + self.assertEqual(1, self.driver.plugin. + umount_share_from_host.call_count) + + def test_create_nfsshare_from_nfssnapshot_copydata_fail(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + 'mount_share_to_host', + mock.Mock(return_value={})) + self.mock_object(share_utils, + 'Copy', + mock.Mock(side_effect=Exception('err'))) + self.mock_object(utils, + 'execute', + mock.Mock(return_value={})) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + self.assertRaises(exception.ShareCopyDataException, + self.driver.create_share_from_snapshot, + self._context, self.share_nfs, + self.nfs_snapshot, self.share_server) + self.assertTrue(db.share_type_get.called) + self.assertEqual(2, self.driver.plugin. + mount_share_to_host.call_count) + self.assertTrue(share_utils.Copy.called) + self.assertEqual(2, utils.execute.call_count) + + def test_create_nfsshare_from_nfssnapshot_umountshare_fail(self): + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.mock_object(self.driver.plugin, + 'mount_share_to_host', + mock.Mock(return_value={})) + self.mock_object(self.driver.plugin, + 'copy_snapshot_data', + mock.Mock(return_value=True)) + self.mock_object(self.driver.plugin, + 'umount_share_from_host', + mock.Mock(side_effect=exception. + ShareUmountException('err'))) + self.mock_object(os, 'rmdir', + mock.Mock(side_effect=Exception('err'))) + self.driver.plugin.helper.login() + self.driver.plugin.helper.snapshot_flag = True + + location = self.driver.create_share_from_snapshot(self._context, + self.share_nfs, + self.cifs_snapshot, + self.share_server) + + self.assertTrue(db.share_type_get.called) + self.assertEqual(2, self.driver.plugin. + mount_share_to_host.call_count) + self.assertTrue(self.driver.plugin.copy_snapshot_data.called) + self.assertEqual(2, self.driver.plugin. + umount_share_from_host.call_count) + self.assertTrue(os.rmdir.called) + self.assertEqual("100.115.10.68:/share_fake_uuid", location) def test_get_share_stats_refresh_pool_not_exist(self): self.driver.plugin.helper.login() @@ -1693,7 +2008,7 @@ class HuaweiShareDriverTestCase(test.TestCase): expected['total_capacity_gb'] = 0.0 expected['free_capacity_gb'] = 0.0 expected['qos'] = True - expected["snapshot_support"] = False + expected["snapshot_support"] = True expected["pools"] = [] pool = dict( pool_name='OpenStack_Pool', @@ -3018,6 +3333,26 @@ class HuaweiShareDriverTestCase(test.TestCase): alloctype_text = doc.createTextNode(alloctype_value) alloctype.appendChild(alloctype_text) + NFSClient = doc.createElement('NFSClient') + + virtualip = doc.createElement('IP') + virtualip_text = doc.createTextNode('100.112.0.1') + virtualip.appendChild(virtualip_text) + NFSClient.appendChild(virtualip) + CIFSClient = doc.createElement('CIFSClient') + + username = doc.createElement('UserName') + username_text = doc.createTextNode('user_name') + username.appendChild(username_text) + CIFSClient.appendChild(username) + + userpassword = doc.createElement('UserPassword') + userpassword_text = doc.createTextNode('user_password') + userpassword.appendChild(userpassword_text) + CIFSClient.appendChild(userpassword) + + lun.appendChild(NFSClient) + lun.appendChild(CIFSClient) lun.appendChild(timeout) lun.appendChild(alloctype) lun.appendChild(waitinterval)