From e2c94a599e223c79573f654034452b9e70667d52 Mon Sep 17 00:00:00 2001 From: liucheng Date: Wed, 25 Nov 2015 18:14:11 +0800 Subject: [PATCH] Huawei: Add manage share with share type in Huawei driver Manage share that is already on array, before this time we just changed share's name, now we import it with share_type and retype it. Implements: blueprint manage-share-on-array Change-Id: I906916f6ae7f5625863ca9f8e3324489937263cd --- manila/share/drivers/huawei/constants.py | 4 + manila/share/drivers/huawei/v3/connection.py | 215 +++++++- manila/share/drivers/huawei/v3/helper.py | 57 +++ .../share/drivers/huawei/test_huawei_nas.py | 465 +++++++++++++++++- 4 files changed, 730 insertions(+), 11 deletions(-) diff --git a/manila/share/drivers/huawei/constants.py b/manila/share/drivers/huawei/constants.py index 548df91faa..ac8236a59f 100644 --- a/manila/share/drivers/huawei/constants.py +++ b/manila/share/drivers/huawei/constants.py @@ -32,6 +32,10 @@ ERROR_CONNECT_TO_SERVER = -403 ERROR_UNAUTHORIZED_TO_SERVER = -401 ALLOC_TYPE_THIN_FLAG = "1" +ALLOC_TYPE_THIN = "Thin" +ALLOC_TYPE_THICK = "Thick" +THIN_PROVISIONING = "true" +THICK_PROVISIONING = "false" OPTS_CAPABILITIES = { 'dedupe': False, diff --git a/manila/share/drivers/huawei/v3/connection.py b/manila/share/drivers/huawei/v3/connection.py index 85bc095e26..5d3c97e912 100644 --- a/manila/share/drivers/huawei/v3/connection.py +++ b/manila/share/drivers/huawei/v3/connection.py @@ -16,6 +16,7 @@ import time from oslo_log import log +from oslo_utils import strutils from oslo_utils import units from manila.common import constants as common_constants @@ -28,6 +29,7 @@ from manila.share.drivers.huawei import constants from manila.share.drivers.huawei import huawei_utils from manila.share.drivers.huawei.v3 import helper from manila.share.drivers.huawei.v3 import smartx +from manila.share import share_types from manila.share import utils as share_utils @@ -474,19 +476,18 @@ class V3StorageConnection(driver.HuaweiBase): old_export_location = share['export_locations'][0]['path'] pool_name = share_utils.extract_host(share['host'], level='pool') share_url_type = self.helper._get_share_url_type(share_proto) - old_share_name = self.helper._get_share_name_by_export_location( old_export_location, share_proto) - share = self.helper._get_share_by_name(old_share_name, - share_url_type) - if not share: + share_storage = self.helper._get_share_by_name(old_share_name, + share_url_type) + if not share_storage: err_msg = (_("Can not get share ID by share %s.") % old_export_location) LOG.error(err_msg) raise exception.InvalidShare(reason=err_msg) - fs_id = share['FSID'] + fs_id = share_storage['FSID'] fs = self.helper._get_fs_info_by_id(fs_id) if not self.check_fs_status(fs['HEALTHSTATUS'], fs['RUNNINGSTATUS']): @@ -503,12 +504,212 @@ class V3StorageConnection(driver.HuaweiBase): % {'fs_pool': fs['POOLNAME'], 'host_pool': pool_name})) - self.helper._change_fs_name(fs_id, share_name) - share_size = int(fs['CAPACITY']) / units.Mi / 2 + result = self.helper._find_all_pool_info() + poolinfo = self.helper._find_pool_info(pool_name, result) + opts = huawei_utils.get_share_extra_specs_params( + share['share_type_id']) + specs = share_types.get_share_type_extra_specs(share['share_type_id']) + if ('capabilities:thin_provisioning' not in specs.keys() + and 'thin_provisioning' not in specs.keys()): + if fs['ALLOCTYPE'] == constants.ALLOC_TYPE_THIN_FLAG: + opts['thin_provisioning'] = constants.THIN_PROVISIONING + else: + opts['thin_provisioning'] = constants.THICK_PROVISIONING + + change_opts = self.check_retype_change_opts(opts, poolinfo, fs) + LOG.info(_LI('Retyping share (%(share)s), changed options are : ' + '(%(change_opts)s).'), + {'share': old_share_name, 'change_opts': change_opts}) + try: + self.retype_share(change_opts, fs_id) + except Exception as err: + message = (_("Retype share error. Share: %(share)s. " + "Reason: %(reason)s.") + % {'share': old_share_name, + 'reason': err}) + raise exception.InvalidShare(reason=message) + + share_size = int(fs['CAPACITY']) / units.Mi / 2 + self.helper._change_fs_name(fs_id, share_name) location = self._get_location_path(share_name, share_proto) return (share_size, [location]) + def check_retype_change_opts(self, opts, poolinfo, fs): + change_opts = { + "partitionid": None, + "cacheid": None, + "dedupe&compression": None, + } + + # SmartPartition + old_partition_id = fs['SMARTPARTITIONID'] + old_partition_name = None + new_partition_id = None + new_partition_name = None + if strutils.bool_from_string(opts['huawei_smartpartition']): + if not opts['partitionname']: + raise exception.InvalidInput( + reason=_('Partition name is None, please set ' + 'huawei_smartpartition:partitionname in key.')) + new_partition_name = opts['partitionname'] + new_partition_id = self.helper._get_partition_id_by_name( + new_partition_name) + if new_partition_id is None: + raise exception.InvalidInput( + reason=(_("Can't find partition name on the array, " + "partition name is: %(name)s.") + % {"name": new_partition_name})) + + if old_partition_id != new_partition_id: + if old_partition_id: + partition_info = self.helper.get_partition_info_by_id( + old_partition_id) + old_partition_name = partition_info['NAME'] + change_opts["partitionid"] = ([old_partition_id, + old_partition_name], + [new_partition_id, + new_partition_name]) + + # SmartCache + old_cache_id = fs['SMARTCACHEID'] + old_cache_name = None + new_cache_id = None + new_cache_name = None + if strutils.bool_from_string(opts['huawei_smartcache']): + if not opts['cachename']: + raise exception.InvalidInput( + reason=_('Cache name is None, please set ' + 'huawei_smartcache:cachename in key.')) + new_cache_name = opts['cachename'] + new_cache_id = self.helper._get_cache_id_by_name( + new_cache_name) + if new_cache_id is None: + raise exception.InvalidInput( + reason=(_("Can't find cache name on the array, " + "cache name is: %(name)s.") + % {"name": new_cache_name})) + + if old_cache_id != new_cache_id: + if old_cache_id: + cache_info = self.helper.get_cache_info_by_id( + old_cache_id) + old_cache_name = cache_info['NAME'] + change_opts["cacheid"] = ([old_cache_id, old_cache_name], + [new_cache_id, new_cache_name]) + + # SmartDedupe&SmartCompression + smartx_opts = constants.OPTS_CAPABILITIES + if opts is not None: + smart = smartx.SmartX() + smartx_opts = smart.get_smartx_extra_specs_opts(opts) + + old_compression = fs['COMPRESSION'] + new_compression = smartx_opts['compression'] + old_dedupe = fs['DEDUP'] + new_dedupe = smartx_opts['dedupe'] + + if fs['ALLOCTYPE'] == constants.ALLOC_TYPE_THIN_FLAG: + fs['ALLOCTYPE'] = constants.ALLOC_TYPE_THIN + else: + fs['ALLOCTYPE'] = constants.ALLOC_TYPE_THICK + + if strutils.bool_from_string(opts['thin_provisioning']): + opts['thin_provisioning'] = constants.ALLOC_TYPE_THIN + else: + opts['thin_provisioning'] = constants.ALLOC_TYPE_THICK + + if (fs['ALLOCTYPE'] != poolinfo['type'] + or fs['ALLOCTYPE'] != opts['thin_provisioning']): + msg = (_("Manage existing share fs type and pool type " + "or fs type and new_share_type mismatch. " + "fs type is: %(fs_type)s, pool type is: " + "%(pool_type)s, new_share_type is: " + "%(new_share_type)s") + % {"fs_type": fs['ALLOCTYPE'], + "pool_type": poolinfo['type'], + "new_share_type": opts['thin_provisioning']}) + raise exception.InvalidHost(reason=msg) + else: + if fs['ALLOCTYPE'] == constants.ALLOC_TYPE_THICK: + if new_compression or new_dedupe: + raise exception.InvalidInput( + reason=_("Dedupe or compression cannot be set for " + "thick filesystem.")) + else: + if (old_dedupe != new_dedupe + or old_compression != new_compression): + change_opts["dedupe&compression"] = ([old_dedupe, + old_compression], + [new_dedupe, + new_compression]) + return change_opts + + def retype_share(self, change_opts, fs_id): + if change_opts.get('partitionid'): + old, new = change_opts['partitionid'] + old_id = old[0] + old_name = old[1] + new_id = new[0] + new_name = new[1] + + if old_id: + self.helper._remove_fs_from_partition(fs_id, old_id) + if new_id: + self.helper._add_fs_to_partition(fs_id, new_id) + msg = (_("Retype FS(id: %(fs_id)s) smartpartition from " + "(name: %(old_name)s, id: %(old_id)s) to " + "(name: %(new_name)s, id: %(new_id)s) " + "performed successfully.") + % {"fs_id": fs_id, + "old_id": old_id, "old_name": old_name, + "new_id": new_id, "new_name": new_name}) + LOG.info(msg) + + if change_opts.get('cacheid'): + old, new = change_opts['cacheid'] + old_id = old[0] + old_name = old[1] + new_id = new[0] + new_name = new[1] + if old_id: + self.helper._remove_fs_from_cache(fs_id, old_id) + if new_id: + self.helper._add_fs_to_cache(fs_id, new_id) + msg = (_("Retype FS(id: %(fs_id)s) smartcache from " + "(name: %(old_name)s, id: %(old_id)s) to " + "(name: %(new_name)s, id: %(new_id)s) " + "performed successfully.") + % {"fs_id": fs_id, + "old_id": old_id, "old_name": old_name, + "new_id": new_id, "new_name": new_name}) + LOG.info(msg) + + if change_opts.get('dedupe&compression'): + old, new = change_opts['dedupe&compression'] + old_dedupe = old[0] + old_compression = old[1] + new_dedupe = new[0] + new_compression = new[1] + if ((old_dedupe != new_dedupe) + or (old_compression != new_compression)): + + new_smartx_opts = {"dedupe": new_dedupe, + "compression": new_compression} + + self.helper._change_extra_specs(fs_id, new_smartx_opts) + msg = (_("Retype FS(id: %(fs_id)s) dedupe from %(old_dedupe)s " + "to %(new_dedupe)s performed successfully, " + "compression from " + "%(old_compression)s to %(new_compression)s " + "performed successfully.") + % {"fs_id": fs_id, + "old_dedupe": old_dedupe, + "new_dedupe": new_dedupe, + "old_compression": old_compression, + "new_compression": new_compression}) + LOG.info(msg) + def _get_location_path(self, share_name, share_proto): root = self.helper._read_xml() target_ip = root.findtext('Storage/LogicalPortIP').strip() diff --git a/manila/share/drivers/huawei/v3/helper.py b/manila/share/drivers/huawei/v3/helper.py index 60c1fe0504..7be3ed91b8 100644 --- a/manila/share/drivers/huawei/v3/helper.py +++ b/manila/share/drivers/huawei/v3/helper.py @@ -606,6 +606,10 @@ class RestHelper(object): fs['CAPACITY'] = result['data']['CAPACITY'] fs['ALLOCTYPE'] = result['data']['ALLOCTYPE'] fs['POOLNAME'] = result['data']['PARENTNAME'] + fs['COMPRESSION'] = result['data']['ENABLECOMPRESSION'] + fs['DEDUP'] = result['data']['ENABLEDEDUP'] + fs['SMARTPARTITIONID'] = result['data']['CACHEPARTITIONID'] + fs['SMARTCACHEID'] = result['data']['SMARTCACHEPARTITIONID'] return fs def _get_share_path(self, share_name): @@ -679,6 +683,18 @@ class RestHelper(object): msg = _("Change filesystem name error.") self._assert_rest_result(result, msg) + def _change_extra_specs(self, fsid, extra_specs): + url = "/filesystem/%s" % fsid + fs_param = { + "ENABLEDEDUP": extra_specs['dedupe'], + "ENABLECOMPRESSION": extra_specs['compression'] + } + data = jsonutils.dumps(fs_param) + result = self.call(url, data, "PUT") + + msg = _("Change extra_specs error.") + self._assert_rest_result(result, msg) + def _get_partition_id_by_name(self, name): url = "/cachepartition" result = self.call(url, None, "GET") @@ -690,6 +706,14 @@ class RestHelper(object): return item['ID'] return None + def get_partition_info_by_id(self, partitionid): + url = '/cachepartition/' + partitionid + result = self.call(url, None, "GET") + self._assert_rest_result(result, + _('Get partition by partition id error.')) + + return result['data'] + def _add_fs_to_partition(self, fs_id, partition_id): url = "/filesystem/associate/cachepartition" data = jsonutils.dumps({"ID": partition_id, @@ -701,6 +725,17 @@ class RestHelper(object): self._assert_rest_result(result, _('Add filesystem to partition error.')) + def _remove_fs_from_partition(self, fs_id, partition_id): + url = "/smartPartition/removeFs" + data = jsonutils.dumps({"ID": partition_id, + "ASSOCIATEOBJTYPE": 40, + "ASSOCIATEOBJID": fs_id, + "TYPE": 268}) + result = self.call(url, data, "PUT") + + self._assert_rest_result(result, + _('Remove filesystem from partition error.')) + def _get_cache_id_by_name(self, name): url = "/SMARTCACHEPARTITION" result = self.call(url, None, "GET") @@ -712,6 +747,17 @@ class RestHelper(object): return item['ID'] return None + def get_cache_info_by_id(self, cacheid): + url = "/SMARTCACHEPARTITION/" + cacheid + data = jsonutils.dumps({"TYPE": "273", + "ID": cacheid}) + + result = self.call(url, data, "GET") + self._assert_rest_result( + result, _('Get smartcache by cache id error.')) + + return result['data'] + def _add_fs_to_cache(self, fs_id, cache_id): url = "/SMARTCACHEPARTITION/CREATE_ASSOCIATE" data = jsonutils.dumps({"ID": cache_id, @@ -721,3 +767,14 @@ class RestHelper(object): result = self.call(url, data, "PUT") self._assert_rest_result(result, _('Add filesystem to cache error.')) + + def _remove_fs_from_cache(self, fs_id, cache_id): + url = "/SMARTCACHEPARTITION/REMOVE_ASSOCIATE" + data = jsonutils.dumps({"ID": cache_id, + "ASSOCIATEOBJTYPE": 40, + "ASSOCIATEOBJID": fs_id, + "TYPE": 273}) + result = self.call(url, data, "PUT") + + self._assert_rest_result(result, + _('Remove filesystem from cache error.')) diff --git a/manila/tests/share/drivers/huawei/test_huawei_nas.py b/manila/tests/share/drivers/huawei/test_huawei_nas.py index 82f1358153..6a72eee85e 100644 --- a/manila/tests/share/drivers/huawei/test_huawei_nas.py +++ b/manila/tests/share/drivers/huawei/test_huawei_nas.py @@ -71,6 +71,16 @@ def filesystem(method, data, fs_status_flag): data = """{"error":{"code":0}, "data":{"ID":"4", "CAPACITY":"8388608"}}""" + elif data == jsonutils.dumps({"ENABLEDEDUP": True, + "ENABLECOMPRESSION": True}): + data = """{"error":{"code":0}, + "data":{"ID":"4", + "CAPACITY":"8388608"}}""" + elif data == jsonutils.dumps({"ENABLEDEDUP": False, + "ENABLECOMPRESSION": False}): + data = """{"error":{"code":0}, + "data":{"ID":"4", + "CAPACITY":"8388608"}}""" elif method == "DELETE": data = """{"error":{"code":0}}""" elif method == "GET": @@ -80,14 +90,139 @@ def filesystem(method, data, fs_status_flag): "RUNNINGSTATUS":"27", "ALLOCTYPE":"1", "CAPACITY":"8388608", - "PARENTNAME":"OpenStack_Pool"}}""" + "PARENTNAME":"OpenStack_Pool", + "ENABLECOMPRESSION":"false", + "ENABLEDEDUP":"false", + "CACHEPARTITIONID":"", + "SMARTCACHEPARTITIONID":""}}""" else: data = """{"error":{"code":0}, "data":{"HEALTHSTATUS":"0", "RUNNINGSTATUS":"27", "ALLOCTYPE":"0", "CAPACITY":"8388608", - "PARENTNAME":"OpenStack_Pool"}}""" + "PARENTNAME":"OpenStack_Pool", + "ENABLECOMPRESSION":"false", + "ENABLEDEDUP":"false", + "CACHEPARTITIONID":"", + "SMARTCACHEPARTITIONID":""}}""" + else: + data = '{"error":{"code":31755596}}' + return (data, extend_share_flag, shrink_share_flag) + + +def filesystem_thick(method, data, fs_status_flag): + extend_share_flag = False + shrink_share_flag = False + + if method == "PUT": + if data == """{"CAPACITY": 10485760}""": + data = """{"error":{"code":0}, + "data":{"ID":"5", + "CAPACITY":"8388608"}}""" + extend_share_flag = True + elif data == """{"CAPACITY": 2097152}""": + data = """{"error":{"code":0}, + "data":{"ID":"5", + "CAPACITY":"2097152"}}""" + shrink_share_flag = True + elif data == """{"NAME": "share_fake_uuid_thickfs"}""": + data = """{"error":{"code":0}, + "data":{"ID":"5", + "CAPACITY":"8388608"}}""" + elif data == jsonutils.dumps({"ENABLEDEDUP": False, + "ENABLECOMPRESSION": False}): + data = """{"error":{"code":0}, + "data":{"ID":"5", + "CAPACITY":"8388608"}}""" + elif method == "DELETE": + data = """{"error":{"code":0}}""" + elif method == "GET": + if fs_status_flag: + data = """{"error":{"code":0}, + "data":{"HEALTHSTATUS":"1", + "RUNNINGSTATUS":"27", + "ALLOCTYPE":"0", + "CAPACITY":"8388608", + "PARENTNAME":"OpenStack_Pool_Thick", + "ENABLECOMPRESSION":"false", + "ENABLEDEDUP":"false", + "CACHEPARTITIONID":"", + "SMARTCACHEPARTITIONID":""}}""" + else: + data = """{"error":{"code":0}, + "data":{"HEALTHSTATUS":"0", + "RUNNINGSTATUS":"27", + "ALLOCTYPE":"0", + "CAPACITY":"8388608", + "PARENTNAME":"OpenStack_Pool_Thick", + "ENABLECOMPRESSION":"false", + "ENABLEDEDUP":"false", + "CACHEPARTITIONID":"", + "SMARTCACHEPARTITIONID":""}}""" + else: + data = '{"error":{"code":31755596}}' + return (data, extend_share_flag, shrink_share_flag) + + +def filesystem_inpartition(method, data, fs_status_flag): + extend_share_flag = False + shrink_share_flag = False + + if method == "PUT": + if data == """{"CAPACITY": 10485760}""": + data = """{"error":{"code":0}, + "data":{"ID":"6", + "CAPACITY":"8388608"}}""" + extend_share_flag = True + elif data == """{"CAPACITY": 2097152}""": + data = """{"error":{"code":0}, + "data":{"ID":"6", + "CAPACITY":"2097152"}}""" + shrink_share_flag = True + elif data == """{"NAME": "share_fake_manage_uuid"}""": + data = """{"error":{"code":0}, + "data":{"ID":"6", + "CAPACITY":"8388608"}}""" + elif data == """{"NAME": "share_fake_uuid_inpartition"}""": + data = """{"error":{"code":0}, + "data":{"ID":"6", + "CAPACITY":"8388608"}}""" + elif data == jsonutils.dumps({"ENABLEDEDUP": True, + "ENABLECOMPRESSION": True}): + data = """{"error":{"code":0}, + "data":{"ID":"6", + "CAPACITY":"8388608"}}""" + elif data == jsonutils.dumps({"ENABLEDEDUP": False, + "ENABLECOMPRESSION": False}): + data = """{"error":{"code":0}, + "data":{"ID":"6", + "CAPACITY":"8388608"}}""" + elif method == "DELETE": + data = """{"error":{"code":0}}""" + elif method == "GET": + if fs_status_flag: + data = """{"error":{"code":0}, + "data":{"HEALTHSTATUS":"1", + "RUNNINGSTATUS":"27", + "ALLOCTYPE":"1", + "CAPACITY":"8388608", + "PARENTNAME":"OpenStack_Pool", + "ENABLECOMPRESSION":"false", + "ENABLEDEDUP":"false", + "CACHEPARTITIONID":"1", + "SMARTCACHEPARTITIONID":"1"}}""" + else: + data = """{"error":{"code":0}, + "data":{"HEALTHSTATUS":"0", + "RUNNINGSTATUS":"27", + "ALLOCTYPE":"0", + "CAPACITY":"8388608", + "PARENTNAME":"OpenStack_Pool", + "ENABLECOMPRESSION":"false", + "ENABLEDEDUP":"false", + "CACHEPARTITIONID":"1", + "SMARTCACHEPARTITIONID":"1"}}""" else: data = '{"error":{"code":31755596}}' return (data, extend_share_flag, shrink_share_flag) @@ -229,7 +364,15 @@ class FakeHuaweiNasHelper(helper.RestHelper): "data":[{"ID":"1", "FSID":"4", "NAME":"test", - "SHAREPATH":"/share_fake_uuid/"}]}""" + "SHAREPATH":"/share_fake_uuid/"}, + {"ID":"2", + "FSID":"5", + "NAME":"test", + "SHAREPATH":"/share_fake_uuid_thickfs/"}, + {"ID":"3", + "FSID":"6", + "NAME":"test", + "SHAREPATH":"/share_fake_uuid_inpartition/"}]}""" else: data = """{"error":{"code":0}, "data":[{"ID":"1", @@ -365,6 +508,16 @@ class FakeHuaweiNasHelper(helper.RestHelper): filesystem(method, data, self.fs_status_flag)) self.delete_flag = True + if url == "/filesystem/5": + data, self.extend_share_flag, self.shrink_share_flag = ( + filesystem_thick(method, data, self.fs_status_flag)) + self.delete_flag = True + + if url == "/filesystem/6": + data, self.extend_share_flag, self.shrink_share_flag = ( + filesystem_inpartition(method, data, self.fs_status_flag)) + self.delete_flag = True + if url == "/cachepartition": if self.partition_exist: data = """{"error":{"code":0}, @@ -375,6 +528,16 @@ class FakeHuaweiNasHelper(helper.RestHelper): "data":[{"ID":"7", "NAME":"test_partition_name_fail"}]}""" + if url == "/cachepartition/1": + if self.partition_exist: + data = """{"error":{"code":0}, + "data":{"ID":"7", + "NAME":"test_partition_name"}}""" + else: + data = """{"error":{"code":0}, + "data":{"ID":"7", + "NAME":"test_partition_name_fail"}}""" + if url == "/SMARTCACHEPARTITION": if self.cache_exist: data = """{"error":{"code":0}, @@ -385,6 +548,16 @@ class FakeHuaweiNasHelper(helper.RestHelper): "data":[{"ID":"8", "NAME":"test_cache_name_fail"}]}""" + if url == "/SMARTCACHEPARTITION/1": + if self.cache_exist: + data = """{"error":{"code":0}, + "data":{"ID":"8", + "NAME":"test_cache_name"}}""" + else: + data = """{"error":{"code":0}, + "data":{"ID":"8", + "NAME":"test_cache_name_fail"}}""" + if url == "/filesystem/associate/cachepartition": data = """{"error":{"code":0}}""" self.add_fs_to_partition_flag = True @@ -392,6 +565,12 @@ class FakeHuaweiNasHelper(helper.RestHelper): if url == "/SMARTCACHEPARTITION/CREATE_ASSOCIATE": data = """{"error":{"code":0}}""" self.add_fs_to_cache_flag = True + + if url == "/SMARTCACHEPARTITION/REMOVE_ASSOCIATE": + data = """{"error":{"code":0}}""" + + if url == "/smartPartition/removeFs": + data = """{"error":{"code":0}}""" else: data = '{"error":{"code":31755596}}' @@ -481,6 +660,54 @@ class HuaweiShareDriverTestCase(test.TestCase): 'share_type_id': 'fake_id', } + self.share_nfs_thickfs = { + 'id': 'fake_uuid', + 'project_id': 'fake_tenant_id', + 'display_name': 'fake', + 'name': 'share-fake-uuid-thickfs', + 'size': 1, + 'share_proto': 'NFS', + 'share_network_id': 'fake_net_id', + 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#OpenStack_Pool', + 'export_locations': [ + {'path': '100.115.10.68:/share_fake_uuid_thickfs'}, + ], + 'share_type_id': 'fake_id', + } + + self.share_nfs_thick_thickfs = { + 'id': 'fake_uuid', + 'project_id': 'fake_tenant_id', + 'display_name': 'fake', + 'name': 'share-fake-uuid-thickfs', + 'size': 1, + 'share_proto': 'NFS', + 'share_network_id': 'fake_net_id', + 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#OpenStack_Pool_Thick', + 'export_locations': [ + {'path': '100.115.10.68:/share_fake_uuid_thickfs'}, + ], + 'share_type_id': 'fake_id', + } + + self.share_nfs_inpartition = { + 'id': 'fake_uuid', + 'project_id': 'fake_tenant_id', + 'display_name': 'fake', + 'name': 'share-fake-uuid-inpartition', + 'size': 1, + 'share_proto': 'NFS', + 'share_network_id': 'fake_net_id', + 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#OpenStack_Pool', + 'export_locations': [ + {'path': '100.115.10.68:/share_fake_uuid_inpartition'}, + ], + 'share_type_id': 'fake_id', + } + self.share_manage_nfs = { 'id': 'fake_uuid', 'project_id': 'fake_tenant_id', @@ -661,6 +888,56 @@ class HuaweiShareDriverTestCase(test.TestCase): } } + fake_extra_specs = { + 'capabilities:dedupe': ' True', + 'capabilities:compression': ' True', + 'capabilities:huawei_smartcache': ' False', + 'huawei_smartcache:cachename': None, + 'capabilities:huawei_smartpartition': ' False', + 'huawei_smartpartition:partitionname': None, + 'capabilities:thin_provisioning': ' True', + 'test:test:test': 'test', + } + + fake_share_type_id = 'fooid-3' + self.fake_type_fake_extra = { + 'test_with_extra': { + 'created_at': 'fake_time', + 'deleted': '0', + 'deleted_at': None, + 'extra_specs': fake_extra_specs, + 'required_extra_specs': {}, + 'id': fake_share_type_id, + 'name': 'test_with_extra', + 'updated_at': None + } + } + + fake_extra_specs = { + 'capabilities:dedupe': ' True', + 'capabilities:compression': ' True', + 'capabilities:huawei_smartcache': ' False', + 'huawei_smartcache:cachename': None, + 'capabilities:huawei_smartpartition': ' False', + 'huawei_smartpartition:partitionname': None, + 'capabilities:thin_provisioning': ' False', + 'test:test:test': 'test', + } + + fake_share_type_id = 'fooid-4' + self.fake_type_thin_extra = { + 'test_with_extra': { + 'created_at': 'fake_time', + 'deleted': '0', + 'deleted_at': None, + 'extra_specs': fake_extra_specs, + 'required_extra_specs': {}, + 'id': fake_share_type_id, + 'name': 'test_with_extra', + 'updated_at': None + } + } + self.share_nfs_host_not_exist = { 'id': 'fake_uuid', 'project_id': 'fake_tenant_id', @@ -1405,7 +1682,41 @@ class HuaweiShareDriverTestCase(test.TestCase): elif share_proto == "CIFS": share = self.share_manage_cifs - share_type = self.fake_type_extra['test_with_extra'] + share_type = self.fake_type_w_extra['test_with_extra'] + self.mock_object(db, 'share_type_get', + mock.Mock(return_value=share_type)) + self.driver.plugin.helper.login() + share_info = self.driver.manage_existing(share, + self.driver_options) + self.assertEqual(4, share_info["size"]) + self.assertEqual(path, share_info["export_locations"]) + + @ddt.data({"fs_alloctype": "THIN", + "path": ["100.115.10.68:/share_fake_manage_uuid"]}, + {"fs_alloctype": "THICK", + "path": ["100.115.10.68:/share_fake_uuid_thickfs"]}) + @ddt.unpack + def test_manage_share_with_default_type(self, fs_alloctype, path): + if fs_alloctype == "THIN": + share = self.share_manage_nfs + elif fs_alloctype == "THICK": + share = self.share_nfs_thick_thickfs + + share_type = self.fake_type_not_extra['test_with_extra'] + self.mock_object(db, 'share_type_get', + mock.Mock(return_value=share_type)) + self.driver.plugin.helper.login() + share_info = self.driver.manage_existing(share, + self.driver_options) + self.assertEqual(4, share_info["size"]) + self.assertEqual(path, share_info["export_locations"]) + + @ddt.data({"path": ["100.115.10.68:/share_fake_uuid_inpartition"]}) + @ddt.unpack + def test_manage_share_remove_from_partition(self, path): + share = self.share_nfs_inpartition + + share_type = self.fake_type_fake_extra['test_with_extra'] self.mock_object(db, 'share_type_get', mock.Mock(return_value=share_type)) @@ -1441,6 +1752,152 @@ class HuaweiShareDriverTestCase(test.TestCase): share, self.driver_options) + def test_manage_share_thickfs_set_dedupe_fail(self): + share = self.share_nfs_thick_thickfs + + self.driver.plugin.helper.login() + share_type = self.fake_type_thin_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidInput, + self.driver.manage_existing, + share, + self.driver_options) + + def test_manage_share_thickfs_not_match_thinpool_fail(self): + share = self.share_nfs_thickfs + + self.driver.plugin.helper.login() + share_type = self.fake_type_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidHost, + self.driver.manage_existing, + share, + self.driver_options) + + @ddt.data({"flag": "old_cache_id", "exc": exception.InvalidInput}, + {"flag": "not_old_cache_id", "exc": exception.InvalidInput}) + @ddt.unpack + def test_manage_share_cache_not_exist(self, flag, exc): + share = None + if flag == "old_cache_id": + share = self.share_nfs_inpartition + elif flag == "not_old_cache_id": + share = self.share_nfs + + self.driver.plugin.helper.cache_exist = False + share_type = self.fake_type_w_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.driver.plugin.helper.login() + self.assertRaises(exc, + self.driver.manage_existing, + share, + self.share_server) + + def test_manage_add_share_to_cache_fail(self): + opts = dict( + huawei_smartcache='true', + huawei_smartpartition='true', + cachename='test_cache_name_fake', + partitionname='test_partition_name_fake', + ) + fs = dict( + SMARTCACHEID='6', + SMARTPARTITIONID=None, + ) + poolinfo = dict( + type='Thin', + ) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.check_retype_change_opts, + opts, poolinfo, fs) + + def test_manage_notsetcache_fail(self): + opts = dict( + huawei_smartcache='true', + huawei_smartpartition='true', + cachename=None, + partitionname='test_partition_name_fake', + ) + fs = dict( + SMARTCACHEID='6', + SMARTPARTITIONID='6', + ) + poolinfo = dict( + type='Thin', + ) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.check_retype_change_opts, + opts, poolinfo, fs) + + @ddt.data({"flag": "old_partition_id", "exc": exception.InvalidInput}, + {"flag": "not_old_partition_id", "exc": exception.InvalidInput}) + @ddt.unpack + def test_manage_share_partition_not_exist(self, flag, exc): + share = None + if flag == "old_partition_id": + share = self.share_nfs_inpartition + elif flag == "not_old_partition_id": + share = self.share_nfs + + self.driver.plugin.helper.partition_exist = False + share_type = self.fake_type_w_extra['test_with_extra'] + self.mock_object(db, + 'share_type_get', + mock.Mock(return_value=share_type)) + self.driver.plugin.helper.login() + self.assertRaises(exc, + self.driver.manage_existing, + share, + self.share_server) + + def test_manage_add_share_to_partition_fail(self): + opts = dict( + huawei_smartcache='true', + huawei_smartpartition='true', + cachename='test_cache_name_fake', + partitionname='test_partition_name_fake', + ) + fs = dict( + SMARTCACHEID=None, + SMARTPARTITIONID='6', + ) + poolinfo = dict( + type='Thin', + ) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.check_retype_change_opts, + opts, poolinfo, fs) + + def test_manage_notset_partition_fail(self): + opts = dict( + huawei_smartcache='true', + huawei_smartpartition='true', + cachename='test_cache_name_fake', + partitionname=None, + ) + fs = dict( + SMARTCACHEID=None, + SMARTPARTITIONID='6', + ) + poolinfo = dict( + type='Thin', + ) + self.assertRaises(exception.InvalidInput, + self.driver.plugin.check_retype_change_opts, + opts, poolinfo, fs) + @ddt.data({"share_proto": "NFS", "export_path": "fake_ip:/share_fake_uuid"}, {"share_proto": "NFS", "export_path": "fake_ip:/"},