diff --git a/manila/share/drivers/huawei/base.py b/manila/share/drivers/huawei/base.py index 7ad41192..0e76d34c 100644 --- a/manila/share/drivers/huawei/base.py +++ b/manila/share/drivers/huawei/base.py @@ -59,5 +59,9 @@ class HuaweiBase(object): def get_network_allocations_number(self): """Get number of network interfaces to be created.""" + @abc.abstractmethod + def get_pool(self, share): + """Return pool name where the share resides on.""" + def update_share_stats(self, stats_dict): """Retrieve stats info from share group.""" diff --git a/manila/share/drivers/huawei/huawei_nas.py b/manila/share/drivers/huawei/huawei_nas.py index 4c8f299d..b40f2d86 100644 --- a/manila/share/drivers/huawei/huawei_nas.py +++ b/manila/share/drivers/huawei/huawei_nas.py @@ -139,6 +139,11 @@ class HuaweiNasDriver(driver.ShareDriver): self.plugin.deny_access(share, access, share_server) + def get_pool(self, share): + """Return pool name where the share resides on.""" + LOG.debug("Get pool.") + return self.plugin.get_pool(share) + def get_network_allocations_number(self): """Get number of network interfaces to be created.""" LOG.debug("Get network allocations number.") @@ -151,7 +156,9 @@ class HuaweiNasDriver(driver.ShareDriver): data = dict( share_backend_name=backend_name or 'HUAWEI_NAS_Driver', vendor_name='Huawei', - storage_protocol='NFS_CIFS') + storage_protocol='NFS_CIFS', + total_capacity_gb=0.0, + free_capacity_gb=0.0) self.plugin.update_share_stats(data) super(HuaweiNasDriver, self)._update_share_stats(data) diff --git a/manila/share/drivers/huawei/v3/connection.py b/manila/share/drivers/huawei/v3/connection.py index af6861d2..1cc75ff5 100644 --- a/manila/share/drivers/huawei/v3/connection.py +++ b/manila/share/drivers/huawei/v3/connection.py @@ -24,6 +24,7 @@ from manila.i18n import _, _LI, _LW from manila.share.drivers.huawei import base as driver from manila.share.drivers.huawei import constants from manila.share.drivers.huawei.v3 import helper +from manila.share import utils as share_utils LOG = log.getLogger(__name__) @@ -47,13 +48,25 @@ class V3StorageConnection(driver.HuaweiBase): share_name = share['name'] share_proto = share['share_proto'] + pool_name = share_utils.extract_host(share['host'], level='pool') + + if not pool_name: + msg = _("Pool is not available in the share host field.") + raise exception.InvalidHost(reason=msg) + + result = self.helper._find_all_pool_info() + poolinfo = self.helper._find_pool_info(pool_name, result) + if not poolinfo: + msg = (_("Can not find pool info by pool name: %s") % pool_name) + raise exception.InvalidHost(reason=msg) + fs_id = None # We sleep here to ensure the newly created filesystem can be read. wait_interval = self._get_wait_interval() timeout = self._get_timeout() try: - fs_id = self.allocate_container(share) + fs_id = self.allocate_container(share, poolinfo) fs = self.helper._get_fs_info_by_id(fs_id) end_time = time.time() + timeout @@ -162,18 +175,30 @@ class V3StorageConnection(driver.HuaweiBase): def update_share_stats(self, stats_dict): """Retrieve status info from share group.""" - capacity = self._get_capacity() + root = self.helper._read_xml() + pool_name_list = root.findtext('Filesystem/StoragePool') + if not pool_name_list: + err_msg = _("The StoragePool is None.") + LOG.error(err_msg) + raise exception.InvalidInput(err_msg) + pool_name_list = pool_name_list.split(";") + + result = self.helper._find_all_pool_info() stats_dict["pools"] = [] - pool = {} - pool.update(dict( - pool_name=capacity['name'], - total_capacity_gb=capacity['TOTALCAPACITY'], - free_capacity_gb=capacity['CAPACITY'], - QoS_support=False, - reserved_percentage=0, - )) - stats_dict["pools"].append(pool) + for pool_name in pool_name_list: + pool_name = pool_name.strip().strip('\n') + capacity = self._get_capacity(pool_name, result) + if capacity: + pool = dict( + pool_name=pool_name, + total_capacity_gb=capacity['TOTALCAPACITY'], + free_capacity_gb=capacity['CAPACITY'], + allocated_capacity_gb=capacity['CONSUMEDCAPACITY'], + QoS_support=False, + reserved_percentage=0, + ) + stats_dict["pools"].append(pool) def delete_share(self, share, share_server=None): """Delete share.""" @@ -206,23 +231,24 @@ class V3StorageConnection(driver.HuaweiBase): """Get number of network interfaces to be created.""" return constants.IP_ALLOCATIONS - def _get_capacity(self): + def _get_capacity(self, pool_name, result): """Get free capacity and total capacity of the pools.""" - poolinfo = self.helper._find_pool_info() + poolinfo = self.helper._find_pool_info(pool_name, result) if poolinfo: total = int(poolinfo['TOTALCAPACITY']) / units.Mi / 2 free = int(poolinfo['CAPACITY']) / units.Mi / 2 + consumed = int(poolinfo['CONSUMEDCAPACITY']) / units.Mi / 2 poolinfo['TOTALCAPACITY'] = total poolinfo['CAPACITY'] = free + poolinfo['CONSUMEDCAPACITY'] = consumed return poolinfo - def _init_filesys_para(self, share): + def _init_filesys_para(self, share, poolinfo): """Init basic filesystem parameters.""" name = share['name'] size = share['size'] * units.Mi * 2 - poolinfo = self.helper._find_pool_info() fileparam = { "NAME": name.replace("-", "_"), "DESCRIPTION": "", @@ -335,9 +361,24 @@ class V3StorageConnection(driver.HuaweiBase): self.helper._allow_access_rest(share_id, access_to, share_proto, access_level) - def allocate_container(self, share): + def get_pool(self, share): + pool_name = share_utils.extract_host(share['host'], level='pool') + if pool_name: + return pool_name + share_name = share['name'] + share_url_type = self.helper._get_share_url_type(share['share_proto']) + share = self.helper._get_share_by_name(share_name, share_url_type) + + pool_name = None + if share: + pool = self.helper._get_fs_info_by_id(share['FSID']) + pool_name = pool['POOLNAME'] + + return pool_name + + def allocate_container(self, share, poolinfo): """Creates filesystem associated to share by name.""" - fileParam = self._init_filesys_para(share) + fileParam = self._init_filesys_para(share, poolinfo) fsid = self.helper._create_filesystem(fileParam) return fsid diff --git a/manila/share/drivers/huawei/v3/helper.py b/manila/share/drivers/huawei/v3/helper.py index 80f2f7d4..1841c38d 100644 --- a/manila/share/drivers/huawei/v3/helper.py +++ b/manila/share/drivers/huawei/v3/helper.py @@ -323,30 +323,33 @@ class RestHelper(object): self._assert_rest_result(result, 'Start CIFS service error.') - def _find_pool_info(self): - root = self._read_xml() - pool_name = root.findtext('Filesystem/StoragePool') - if not pool_name: - err_msg = (_("Invalid resource pool: %s.") % pool_name) - LOG.error(err_msg) - raise exception.InvalidInput(err_msg) - - url = self.url + "/storagepool" - result = self.call(url, None) - self._assert_rest_result(result, 'Query resource pool error.') + def _find_pool_info(self, pool_name, result): + if pool_name is None: + return poolinfo = {} pool_name = pool_name.strip() for item in result.get('data', []): - if pool_name == item['NAME']: + if pool_name == item['NAME'] and '2' == item['USAGETYPE']: poolinfo['name'] = pool_name poolinfo['ID'] = item['ID'] poolinfo['CAPACITY'] = item['USERFREECAPACITY'] poolinfo['TOTALCAPACITY'] = item['USERTOTALCAPACITY'] + poolinfo['CONSUMEDCAPACITY'] = item['USERCONSUMEDCAPACITY'] break return poolinfo + def _find_all_pool_info(self): + url = self.url + "/storagepool" + result = self.call(url, None) + + msg = "Query resource pool error." + self._assert_rest_result(result, msg) + self._assert_data_in_result(result, msg) + + return result + def _read_xml(self): """Open xml file and parse the content.""" filename = self.configuration.manila_huawei_conf_file @@ -580,6 +583,7 @@ class RestHelper(object): fs = {} fs['HEALTHSTATUS'] = result['data']['HEALTHSTATUS'] fs['RUNNINGSTATUS'] = result['data']['RUNNINGSTATUS'] + fs['POOLNAME'] = result['data']['PARENTNAME'] return fs def _get_share_path(self, share_name): diff --git a/manila/tests/share/drivers/huawei/test_huawei_nas.py b/manila/tests/share/drivers/huawei/test_huawei_nas.py index 09d6f1d3..de95608a 100644 --- a/manila/tests/share/drivers/huawei/test_huawei_nas.py +++ b/manila/tests/share/drivers/huawei/test_huawei_nas.py @@ -64,11 +64,13 @@ def filesystem(method, data, fs_status_flag): if fs_status_flag: data = """{"error":{"code":0}, "data":{"HEALTHSTATUS":"1", - "RUNNINGSTATUS":"27"}}""" + "RUNNINGSTATUS":"27", + "PARENTNAME":"OpenStack_Pool"}}""" else: data = """{"error":{"code":0}, "data":{"HEALTHSTATUS":"0", - "RUNNINGSTATUS":"27"}}""" + "RUNNINGSTATUS":"27", + "PARENTNAME":"OpenStack_Pool"}}""" else: data = '{"error":{"code":31755596}}' return (data, extend_share_flag) @@ -171,7 +173,9 @@ class FakeHuaweiNasHelper(helper.RestHelper): "data":[{"USERFREECAPACITY":"2097152", "ID":"1", "NAME":"OpenStack_Pool", - "USERTOTALCAPACITY":"4194304"}]}""" + "USERTOTALCAPACITY":"4194304", + "USAGETYPE":"2", + "USERCONSUMEDCAPACITY":"2097152"}]}""" if url == "filesystem": data = """{"error":{"code":0},"data":{ @@ -196,7 +200,7 @@ class FakeHuaweiNasHelper(helper.RestHelper): else: data = """{"error":{"code":0}, "data":[{"ID":"1", - "FSID":"4", + "FSID":"", "NAME":"test", "SHAREPATH":"/share_fake_uuid_fail/"}]}""" @@ -393,6 +397,7 @@ class HuaweiShareDriverTestCase(test.TestCase): 'share_proto': 'NFS', 'share_network_id': 'fake_net_id', 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#OpenStack_Pool', } self.share_proto_fail = { @@ -404,6 +409,7 @@ class HuaweiShareDriverTestCase(test.TestCase): 'share_proto': 'proto_fail', 'share_network_id': 'fake_net_id', 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#OpenStack_Pool', } self.share_cifs = { @@ -415,6 +421,7 @@ class HuaweiShareDriverTestCase(test.TestCase): 'share_proto': 'CIFS', 'share_network_id': 'fake_net_id', 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#OpenStack_Pool', } self.nfs_snapshot = { @@ -473,6 +480,30 @@ class HuaweiShareDriverTestCase(test.TestCase): ], } + self.share_nfs_host_not_exist = { + 'id': 'fake_uuid', + 'project_id': 'fake_tenant_id', + 'display_name': 'fake', + 'name': 'share-fake-uuid', + 'size': 1, + 'share_proto': 'NFS', + 'share_network_id': 'fake_net_id', + 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#', + } + + self.share_nfs_storagepool_fail = { + 'id': 'fake_uuid', + 'project_id': 'fake_tenant_id', + 'display_name': 'fake', + 'name': 'share-fake-uuid', + 'size': 1, + 'share_proto': 'NFS', + 'share_network_id': 'fake_net_id', + 'share_server_id': 'fake-share-srv-id', + 'host': 'fake_host@fake_backend#OpenStack_Pool2', + } + def test_conf_product_fail(self): self.recreate_fake_conf_file(product_flag=False) self.driver.plugin.configuration.manila_huawei_conf_file = ( @@ -533,15 +564,20 @@ class HuaweiShareDriverTestCase(test.TestCase): self.share_nfs, self.share_server) - def test_create_share_nfs_storagepool_fail(self): - self.recreate_fake_conf_file(pool_node_flag=False) - self.driver.plugin.configuration.manila_huawei_conf_file = ( - self.fake_conf_file) + def test_create_share_storagepool_not_exist(self): self.driver.plugin.helper.login() - self.assertRaises(exception.InvalidShare, + self.assertRaises(exception.InvalidHost, self.driver.create_share, self._context, - self.share_nfs, + self.share_nfs_host_not_exist, + self.share_server) + + def test_create_share_nfs_storagepool_fail(self): + self.driver.plugin.helper.login() + self.assertRaises(exception.InvalidHost, + self.driver.create_share, + self._context, + self.share_nfs_storagepool_fail, self.share_server) def test_create_share_nfs_no_data_fail(self): @@ -725,6 +761,14 @@ class HuaweiShareDriverTestCase(test.TestCase): self._context, self.share_nfs, self.nfs_snapshot, self.share_server) + def test_get_share_stats_refresh_pool_not_exist(self): + self.driver.plugin.helper.login() + self.recreate_fake_conf_file(pool_node_flag=False) + self.driver.plugin.configuration.manila_huawei_conf_file = ( + self.fake_conf_file) + self.assertRaises(exception.InvalidInput, + self.driver._update_share_stats) + def test_get_share_stats_refresh(self): self.driver.plugin.helper.login() self.driver._update_share_stats() @@ -736,8 +780,8 @@ class HuaweiShareDriverTestCase(test.TestCase): expected["driver_version"] = '1.0' expected["storage_protocol"] = 'NFS_CIFS' expected['reserved_percentage'] = 0 - expected['total_capacity_gb'] = 'infinite' - expected['free_capacity_gb'] = 'infinite' + expected['total_capacity_gb'] = 0.0 + expected['free_capacity_gb'] = 0.0 expected['QoS_support'] = False expected["pools"] = [] pool = {} @@ -745,19 +789,14 @@ class HuaweiShareDriverTestCase(test.TestCase): pool_name='OpenStack_Pool', total_capacity_gb=2, free_capacity_gb=1, + allocated_capacity_gb=1, QoS_support=False, reserved_percentage=0, )) + expected["pools"].append(pool) self.assertEqual(expected, self.driver._stats) - def test_get_capacity_success(self): - self.driver.plugin.helper.login() - capacity = {} - capacity = self.driver.plugin._get_capacity() - self.assertEqual(2, capacity['TOTALCAPACITY']) - self.assertEqual(1, capacity['CAPACITY']) - def test_allow_access_proto_fail(self): self.driver.plugin.helper.login() self.assertRaises(exception.InvalidInput, @@ -995,6 +1034,17 @@ class HuaweiShareDriverTestCase(test.TestCase): self.driver.delete_snapshot, self._context, self.cifs_snapshot, self.share_server) + def test_get_pool_success(self): + self.driver.plugin.helper.login() + pool_name = self.driver.get_pool(self.share_nfs_host_not_exist) + self.assertEqual('OpenStack_Pool', pool_name) + + def test_get_pool_fail(self): + self.driver.plugin.helper.login() + self.driver.plugin.helper.share_exist = False + pool_name = self.driver.get_pool(self.share_nfs_host_not_exist) + self.assertEqual(None, pool_name) + def test_multi_resturls_success(self): self.recreate_fake_conf_file(multi_url=True) self.driver.plugin.configuration.manila_huawei_conf_file = ( @@ -1074,7 +1124,7 @@ class HuaweiShareDriverTestCase(test.TestCase): storagepool = doc.createElement('StoragePool') if pool_node_flag: - pool_text = doc.createTextNode('OpenStack_Pool') + pool_text = doc.createTextNode('OpenStack_Pool;OpenStack_Pool2; ;') else: pool_text = doc.createTextNode('') storagepool.appendChild(pool_text)