From: chenzongliang Date: Thu, 4 Jun 2015 09:12:27 +0000 (+0800) Subject: Add iSCSI multipath support for Huawei driver X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=a1b50a0905aee41cb5c9fe8ac319e82b0081da20;p=openstack-build%2Fcinder-build.git Add iSCSI multipath support for Huawei driver Update the Huawei18000ISCSIDriver to return lists of portals in initialize connection instead of just choose a single one from the list to return. Also add _() translation markers at some place in this patch. Co-Authored-By: Bob-OpenStack liuxinguo@huawei.com Change-Id: I3f523a1f891e2407616a57d4507333fff45bd171 Implements: blueprint huawei-storage-iscsi-multipath-support --- diff --git a/cinder/tests/unit/test_huawei_drivers.py b/cinder/tests/unit/test_huawei_drivers.py index c037710cb..ef11bda08 100644 --- a/cinder/tests/unit/test_huawei_drivers.py +++ b/cinder/tests/unit/test_huawei_drivers.py @@ -286,7 +286,14 @@ FAKE_GET_ISCSI_INFO_RESPONSE = """ "ID": "iqn.oceanstor:21004846fb8ca15f::22003:111.111.101.244", "TPGT": "8196", "TYPE": 249 - }], + }, + { + "ETHPORTID": "139268", + "ID": "iqn.oceanstor:21004846fb8ca15f::22003:111.111.102.244", + "TPGT": "8196", + "TYPE": 249 + } + ], "error": { "code": 0, "description": "0" @@ -305,7 +312,37 @@ FAKE_GET_ETH_INFO_RESPONSE = """ "MACADDRESS": "00:22:a1:0a:79:57", "ETHNEGOTIATE": "-1", "ERRORPACKETS": "0", - "IPV4ADDR": "192.168.100.2", + "IPV4ADDR": "198.100.10.1", + "IPV6GATEWAY": "", + "IPV6MASK": "0", + "OVERFLOWEDPACKETS": "0", + "ISCSINAME": "P0", + "HEALTHSTATUS": "1", + "ETHDUPLEX": "2", + "ID": "16909568", + "LOSTPACKETS": "0", + "TYPE": 213, + "NAME": "P0", + "INIORTGT": "4", + "RUNNINGSTATUS": "10", + "IPV4GATEWAY": "", + "BONDNAME": "", + "STARTTIME": "1371684218", + "SPEED": "1000", + "ISCSITCPPORT": "0", + "IPV4MASK": "255.255.0.0", + "IPV6ADDR": "", + "LOGICTYPE": "0", + "LOCATION": "ENG0.A5.P0", + "MTU": "1500", + "PARENTID": "1.5" + }, + { + "PARENTTYPE": 209, + "MACADDRESS": "00:22:a1:0a:79:57", + "ETHNEGOTIATE": "-1", + "ERRORPACKETS": "0", + "IPV4ADDR": "198.100.10.2", "IPV6GATEWAY": "", "IPV6MASK": "0", "OVERFLOWEDPACKETS": "0", @@ -326,7 +363,7 @@ FAKE_GET_ETH_INFO_RESPONSE = """ "IPV4MASK": "255.255.0.0", "IPV6ADDR": "", "LOGICTYPE": "0", - "LOCATION": "ENG0.B5.P0", + "LOCATION": "ENG0.A5.P3", "MTU": "1500", "PARENTID": "1.5" }] @@ -339,10 +376,16 @@ FAKE_GET_ETH_ASSOCIATE_RESPONSE = """ "code":0 }, "data":[{ - "IPV4ADDR":"192.168.100.10", - "HEALTHSTATUS":"1", - "RUNNINGSTATUS":"10" - }] + "IPV4ADDR": "198.100.10.1", + "HEALTHSTATUS": "1", + "RUNNINGSTATUS": "10" + }, + { + "IPV4ADDR": "198.100.10.2", + "HEALTHSTATUS": "1", + "RUNNINGSTATUS": "10" + } + ] } """ # A fake response of get iscsi device info response @@ -352,8 +395,7 @@ FAKE_GET_ISCSI_DEVICE_RESPONSE = """ "code": 0 }, "data": [{ - "CMO_ISCSI_DEVICE_NAME":\ - "iqn.2006-08.com.huawei:oceanstor:21000022a10a2a39:iscsinametest" + "CMO_ISCSI_DEVICE_NAME": "iqn.2006-08.com.huawei:oceanstor:21000022a:" }] } """ @@ -406,7 +448,7 @@ FAKE_GET_ALL_HOST_GROUP_INFO_RESPONSE = """ "code": 0 }, "data": [{ - "NAME":"ubuntuc", + "NAME":"OpenStack_HostGroup_1", "DESCRIPTION":"", "ID":"0", "TYPE":14 @@ -603,7 +645,27 @@ FAKE_PORT_GROUP_RESPONSE = """ }, "data":[{ "ID":11, - "NAME":"test" + "NAME":"portgroup-test" + }] +} +""" + +FAKE_ISCSI_INITIATOR_RESPONSE = """ +{ + "error":{ + "code":0 + }, + "data":[{ + "CHAPNAME":"mm-user", + "HEALTHSTATUS":"1", + "ID":"iqn.1993-08.org.debian:01:9073aba6c6f", + "ISFREE":"true", + "MULTIPATHTYPE":"1", + "NAME":"", + "OPERATIONSYSTEM":"255", + "RUNNINGSTATUS":"28", + "TYPE":222, + "USECHAP":"true" }] } """ @@ -770,7 +832,7 @@ MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator?range=[0-256]/GET'] = ( FAKE_COMMON_SUCCESS_RESPONSE) MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/'] = ( - FAKE_ISCSI_INITIATOR_RESPONSE) + FAKE_COMMON_SUCCESS_RESPONSE) MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/POST'] = ( FAKE_ISCSI_INITIATOR_RESPONSE) @@ -938,8 +1000,14 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase): driver = Fake18000ISCSIStorage(configuration=self.configuration) self.driver = driver self.driver.do_setup() - self.portgroup = 'test' - self.target_ip = '192.168.100.10' + self.portgroup = 'portgroup-test' + self.iscsi_iqns = ['iqn.2006-08.com.huawei:oceanstor:21000022a:' + ':20500:198.100.10.1', + 'iqn.2006-08.com.huawei:oceanstor:21000022a:' + ':20503:198.100.10.2'] + self.target_ips = ['198.100.10.1', + '198.100.10.2'] + self.portgroup_id = 11 def test_login_success(self): deviceid = self.driver.restclient.login() @@ -1028,9 +1096,10 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase): def test_initialize_connection_fail(self): self.driver.restclient.login() self.driver.restclient.test_fail = True - iscsi_properties = self.driver.initialize_connection(test_volume, - FakeConnector) - self.assertEqual(1, iscsi_properties['data']['target_lun']) + + self.assertRaises(exception.VolumeBackendAPIException, + self.driver.initialize_connection, + test_volume, FakeConnector) def test_get_default_timeout(self): result = huawei_utils.get_default_timeout(self.xml_file_path) @@ -1060,7 +1129,16 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase): self.driver.restclient.login() portg_id = self.driver.restclient.find_tgt_port_group(self.portgroup) result = self.driver.restclient._get_tgt_ip_from_portgroup(portg_id) - self.assertEqual(result, self.target_ip) + self.assertEqual(self.target_ips, result) + + def test_get_iscsi_params(self): + self.driver.restclient.login() + (iscsi_iqns, target_ips, portgroup_id) = ( + self.driver.restclient.get_iscsi_params(self.xml_file_path, + FakeConnector)) + self.assertEqual(self.iscsi_iqns, iscsi_iqns) + self.assertEqual(self.target_ips, target_ips) + self.assertEqual(self.portgroup_id, portgroup_id) def test_get_lun_conf_params(self): self.driver.restclient.login() @@ -1222,6 +1300,7 @@ class Huawei18000ISCSIDriverTestCase(test.TestCase): initiator.setAttribute('TargetIP', '192.168.100.2') initiator.setAttribute('CHAPinfo', 'mm-user;mm-user@storage') initiator.setAttribute('ALUA', '1') + initiator.setAttribute('TargetPortGroup', 'portgroup-test') iscsi.appendChild(initiator) host = doc.createElement('Host') diff --git a/cinder/volume/drivers/huawei/constants.py b/cinder/volume/drivers/huawei/constants.py index 4afd4b172..92af7abb2 100644 --- a/cinder/volume/drivers/huawei/constants.py +++ b/cinder/volume/drivers/huawei/constants.py @@ -25,6 +25,7 @@ LUNGROUP_PREFIX = 'OpenStack_LunGroup_' MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_' QOS_NAME_PREFIX = 'OpenStack_' +CAPACITY_UNIT = 1024.0 / 1024.0 / 2 DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30 DEFAULT_WAIT_INTERVAL = 5 ERROR_CONNECT_TO_SERVER = -403 diff --git a/cinder/volume/drivers/huawei/huawei_driver.py b/cinder/volume/drivers/huawei/huawei_driver.py index 403230765..5ed674e52 100644 --- a/cinder/volume/drivers/huawei/huawei_driver.py +++ b/cinder/volume/drivers/huawei/huawei_driver.py @@ -121,7 +121,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): if self.restclient.check_lun_exist(lun_id): self.restclient.delete_lun(lun_id) else: - LOG.warning(_LW("Can't find %s on the array."), lun_id) + LOG.warning(_LW("Can't find lun %s on the array."), lun_id) return False return True @@ -342,15 +342,15 @@ class HuaweiBaseDriver(driver.VolumeDriver): {'initiator_name': initiator_name, 'volume': volume_name}) - (iscsi_iqn, - target_ip, + (iscsi_iqns, + target_ips, portgroup_id) = self.restclient.get_iscsi_params(self.xml_file_path, connector) LOG.info(_LI('initialize_connection_iscsi, iscsi_iqn: %(iscsi_iqn)s, ' 'target_ip: %(target_ip)s, ' - 'TargetPortGroup: %(portgroup_id)s.'), - {'iscsi_iqn': iscsi_iqn, - 'target_ip': target_ip, + 'portgroup_id: %(portgroup_id)s.'), + {'iscsi_iqn': iscsi_iqns, + 'target_ip': target_ips, 'portgroup_id': portgroup_id},) # Create hostgroup if not exist. @@ -385,10 +385,18 @@ class HuaweiBaseDriver(driver.VolumeDriver): # Return iSCSI properties. properties = {} properties['target_discovered'] = False - properties['target_portal'] = ('%s:%s' % (target_ip, '3260')) - properties['target_iqn'] = iscsi_iqn - properties['target_lun'] = int(hostlun_id) properties['volume_id'] = volume['id'] + multipath = connector.get('multipath', False) + hostlun_id = int(hostlun_id) + if not multipath: + properties['target_portal'] = ('%s:3260' % target_ips[0]) + properties['target_iqn'] = iscsi_iqns[0] + properties['target_lun'] = hostlun_id + else: + properties['target_iqns'] = [iqn for iqn in iscsi_iqns] + properties['target_portals'] = [ + '%s:3260' % ip for ip in target_ips] + properties['target_luns'] = [hostlun_id] * len(target_ips) # If use CHAP, return CHAP info. if chapinfo: @@ -591,7 +599,11 @@ class Huawei18000ISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): Version history: 1.0.0 - Initial driver - 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver. + 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver + 1.1.1 - Code refactor + CHAP support + Multiple pools support + ISCSI multipath support """ VERSION = "1.1.1" @@ -625,7 +637,9 @@ class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): Version history: 1.0.0 - Initial driver - 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver. + 1.1.0 - Provide Huawei OceanStor 18000 storage volume driver + 1.1.1 - Code refactor + Multiple pools support """ VERSION = "1.1.1" diff --git a/cinder/volume/drivers/huawei/rest_client.py b/cinder/volume/drivers/huawei/rest_client.py index adff4b1ae..d028aa9de 100644 --- a/cinder/volume/drivers/huawei/rest_client.py +++ b/cinder/volume/drivers/huawei/rest_client.py @@ -134,7 +134,7 @@ class RestClient(object): data = json.dumps(lun_param) result = self.call(url, data) - msg = 'Create volume error.' + msg = _('Create volume error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -160,19 +160,20 @@ class RestClient(object): data = json.dumps({"TYPE": "11", "ID": lun_id}) result = self.call(url, data, "DELETE") - self._assert_rest_result(result, 'Delete lun error.') + self._assert_rest_result(result, _('Delete lun error.')) def find_all_pools(self): url = self.url + "/storagepool" result = self.call(url, None) - self._assert_rest_result(result, 'Query resource pool error.') - self._assert_data_in_result(result, 'Query resource pool error.') + msg = _('Query resource pool error.') + self._assert_rest_result(result, msg) + self._assert_data_in_result(result, msg) return result def find_pool_info(self, pool_name, result): - poolinfo = {} + pool_info = {} if not pool_name: - return poolinfo + return pool_info if 'data' in result: for item in result['data']: @@ -181,11 +182,11 @@ class RestClient(object): if ('USAGETYPE' in item and item['USAGETYPE'] == constants.FILE_SYSTEM_POOL_TYPE): break - poolinfo['ID'] = item['ID'] - poolinfo['CAPACITY'] = item['USERFREECAPACITY'] - poolinfo['TOTALCAPACITY'] = item['USERTOTALCAPACITY'] + pool_info['ID'] = item['ID'] + pool_info['CAPACITY'] = item['USERFREECAPACITY'] + pool_info['TOTALCAPACITY'] = item['USERTOTALCAPACITY'] break - return poolinfo + return pool_info def _get_id_from_result(self, result, name, key): if 'data' in result: @@ -196,7 +197,7 @@ class RestClient(object): def get_volume_by_name(self, name): url = self.url + "/lun?range=[0-65535]" result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Get volume by name error.') + self._assert_rest_result(result, _('Get volume by name error.')) return self._get_id_from_result(result, name, 'NAME') @@ -204,7 +205,7 @@ class RestClient(object): activeurl = self.url + "/snapshot/activate" data = json.dumps({"SNAPSHOTLIST": [snapshot_id]}) result = self.call(activeurl, data) - self._assert_rest_result(result, 'Active snapshot error.') + self._assert_rest_result(result, _('Active snapshot error.')) def create_snapshot(self, snapshot): snapshot_name = huawei_utils.encode_name(snapshot['id']) @@ -232,7 +233,7 @@ class RestClient(object): "PARENTID": lun_id}) result = self.call(url, data) - msg = 'Create snapshot error.' + msg = _('Create snapshot error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -253,19 +254,19 @@ class RestClient(object): url = self.url + "/snapshot/stop" stopdata = json.dumps({"ID": snapshot_id}) result = self.call(url, stopdata, "PUT") - self._assert_rest_result(result, 'Stop snapshot error.') + self._assert_rest_result(result, _('Stop snapshot error.')) def delete_snapshot(self, snapshotid): url = self.url + "/snapshot/%s" % snapshotid data = json.dumps({"TYPE": "27", "ID": snapshotid}) result = self.call(url, data, "DELETE") - self._assert_rest_result(result, 'Delete snapshot error.') + self._assert_rest_result(result, _('Delete snapshot error.')) def get_snapshotid_by_name(self, name): url = self.url + "/snapshot?range=[0-32767]" data = json.dumps({"TYPE": "27"}) result = self.call(url, data, "GET") - self._assert_rest_result(result, 'Get snapshot id error.') + self._assert_rest_result(result, _('Get snapshot id error.')) return self._get_id_from_result(result, name, 'NAME') @@ -283,7 +284,7 @@ class RestClient(object): % tgtlunid)}) result = self.call(url, data) - msg = 'Create luncopy error.' + msg = _('Create luncopy error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -294,8 +295,8 @@ class RestClient(object): If hostgroup doesn't exist, create one. """ - host_group_name = constants.HOSTGROUP_PREFIX + host_id - hostgroup_id = self._create_hostgroup_with_check(host_group_name) + hostgroup_name = constants.HOSTGROUP_PREFIX + host_id + hostgroup_id = self._create_hostgroup_with_check(hostgroup_name) is_associated = self._is_host_associate_to_hostgroup(hostgroup_id, host_id) if not is_associated: @@ -308,9 +309,9 @@ class RestClient(object): url = self.url + "/portgroup?range=[0-8191]&TYPE=257" result = self.call(url, None, "GET") - msg = 'Find portgroup error.' + msg = _('Find portgroup error.') self._assert_rest_result(result, msg) - msg = 'Can not find the portgroup on the array.' + msg = _('Can not find the portgroup on the array.') self._assert_data_in_result(result, msg) return self._get_id_from_result(result, tgt_port_group, 'NAME') @@ -322,15 +323,15 @@ class RestClient(object): "TYPE": "245", "ID": view_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Associate portgroup to mapping ' - 'view error.') + self._assert_rest_result(result, _('Associate portgroup to mapping ' + 'view error.')) def _portgroup_associated(self, view_id, portgroup_id): url_subfix = ("/mappingview/associate?TYPE=245&" "ASSOCIATEOBJTYPE=257&ASSOCIATEOBJID=%s" % portgroup_id) url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Check portgroup associate error.') + self._assert_rest_result(result, _('Check portgroup associate error.')) if self._get_id_from_result(result, view_id, 'ID'): return True @@ -382,10 +383,9 @@ class RestClient(object): except Exception: with excutils.save_and_reraise_exception(): - err_msg = (_LE( + LOG.error(_LE( 'Error occurred when adding hostgroup and lungroup to ' 'view. Remove lun from lungroup now.')) - LOG.error(err_msg) self.remove_lun_from_lungroup(lungroup_id, lun_id) return lun_id @@ -403,7 +403,7 @@ class RestClient(object): url = self.url + "/iscsidevicename" result = self.call(url, None) - msg = 'Get iSCSI target port error.' + msg = _('Get iSCSI target port error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -413,7 +413,7 @@ class RestClient(object): """Get the given hostgroup id.""" url = self.url + "/hostgroup?range=[0-8191]" result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Get hostgroup information error.') + self._assert_rest_result(result, _('Get hostgroup information error.')) return self._get_id_from_result(result, groupname, 'NAME') @@ -421,35 +421,35 @@ class RestClient(object): """Get the given hostgroup id.""" url = self.url + "/lungroup?range=[0-8191]" result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Get lungroup information error.') + self._assert_rest_result(result, _('Get lungroup information error.')) return self._get_id_from_result(result, lungroup_name, 'NAME') - def _create_hostgroup_with_check(self, host_group_name): + def _create_hostgroup_with_check(self, hostgroup_name): """Check if host exists on the array, or create it.""" - hostgroup_id = self.find_hostgroup(host_group_name) + hostgroup_id = self.find_hostgroup(hostgroup_name) if hostgroup_id: LOG.info(_LI( '_create_hostgroup_with_check. ' 'hostgroup name: %(name)s, ' 'hostgroup id: %(id)s'), - {'name': host_group_name, + {'name': hostgroup_name, 'id': hostgroup_id}) return hostgroup_id try: - hostgroup_id = self._create_hostgroup(host_group_name) + hostgroup_id = self._create_hostgroup(hostgroup_name) except Exception: LOG.info(_LI( 'Failed to create hostgroup: %(name)s. ' 'Please check if it exists on the array.'), - {'name': host_group_name}) - hostgroup_id = self.find_hostgroup(host_group_name) + {'name': hostgroup_name}) + hostgroup_id = self.find_hostgroup(hostgroup_name) if hostgroup_id is None: err_msg = (_( 'Failed to create hostgroup: %(name)s. ' 'Check if it exists on the array.'), - {'name': host_group_name}) + {'name': hostgroup_name}) LOG.error(err_msg) raise exception.VolumeBackendAPIException(data=err_msg) @@ -458,16 +458,16 @@ class RestClient(object): 'Create hostgroup success. ' 'hostgroup name: %(name)s, ' 'hostgroup id: %(id)s'), - {'name': host_group_name, + {'name': hostgroup_name, 'id': hostgroup_id}) return hostgroup_id - def _create_hostgroup(self, hostgroupname): + def _create_hostgroup(self, hostgroup_name): url = self.url + "/hostgroup" - data = json.dumps({"TYPE": "14", "NAME": hostgroupname}) + data = json.dumps({"TYPE": "14", "NAME": hostgroup_name}) result = self.call(url, data) - msg = 'Create hostgroup error.' + msg = _('Create hostgroup error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -481,7 +481,7 @@ class RestClient(object): "NAME": lungroup_name}) result = self.call(url, data) - msg = 'Create lungroup error.' + msg = _('Create lungroup error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -490,14 +490,14 @@ class RestClient(object): def delete_lungroup(self, lungroup_id): url = self.url + "/LUNGroup/" + lungroup_id result = self.call(url, None, "DELETE") - self._assert_rest_result(result, 'Delete lungroup error.') + self._assert_rest_result(result, _('Delete lungroup error.')) def lungroup_associated(self, view_id, lungroup_id): url_subfix = ("/mappingview/associate?TYPE=245&" "ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=%s" % lungroup_id) url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Check lungroup associate error.') + self._assert_rest_result(result, _('Check lungroup associate error.')) if self._get_id_from_result(result, view_id, 'ID'): return True @@ -508,7 +508,7 @@ class RestClient(object): "ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=%s" % hostgroup_id) url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Check hostgroup associate error.') + self._assert_rest_result(result, _('Check hostgroup associate error.')) if self._get_id_from_result(result, view_id, 'ID'): return True @@ -518,7 +518,7 @@ class RestClient(object): url = self.url + ("/lun/associate?TYPE=11&ASSOCIATEOBJTYPE=21" "&ASSOCIATEOBJID=%s" % (host_id)) result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Find host lun id error.') + self._assert_rest_result(result, _('Find host lun id error.')) host_lun_id = 1 if 'data' in result: @@ -539,7 +539,7 @@ class RestClient(object): url = self.url + "/host?range=[0-65535]" data = json.dumps({"TYPE": "21"}) result = self.call(url, data, "GET") - self._assert_rest_result(result, 'Find host in hostgroup error.') + self._assert_rest_result(result, _('Find host in hostgroup error.')) return self._get_id_from_result(result, hostname, 'NAME') @@ -587,7 +587,7 @@ class RestClient(object): "OPERATIONSYSTEM": "0", "DESCRIPTION": host_name_before_hash}) result = self.call(url, data) - self._assert_rest_result(result, 'Add new host error.') + self._assert_rest_result(result, _('Add new host error.')) if 'data' in result: return result['data']['ID'] @@ -601,7 +601,7 @@ class RestClient(object): url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Check hostgroup associate error.') + self._assert_rest_result(result, _('Check hostgroup associate error.')) if self._get_id_from_result(result, host_id, 'ID'): return True @@ -614,7 +614,7 @@ class RestClient(object): url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Check lungroup associate error.') + self._assert_rest_result(result, _('Check lungroup associate error.')) if self._get_id_from_result(result, lun_id, 'ID'): return True @@ -629,7 +629,8 @@ class RestClient(object): "ASSOCIATEOBJID": host_id}) result = self.call(url, data) - self._assert_rest_result(result, 'Associate host to hostgroup error.') + self._assert_rest_result(result, _('Associate host to hostgroup ' + 'error.')) def associate_lun_to_lungroup(self, lungroup_id, lun_id): """Associate lun to lungroup.""" @@ -638,7 +639,7 @@ class RestClient(object): "ASSOCIATEOBJTYPE": "11", "ASSOCIATEOBJID": lun_id}) result = self.call(url, data) - self._assert_rest_result(result, 'Associate lun to lungroup error.') + self._assert_rest_result(result, _('Associate lun to lungroup error.')) def remove_lun_from_lungroup(self, lungroup_id, lun_id): """Remove lun from lungroup.""" @@ -647,15 +648,15 @@ class RestClient(object): % (lungroup_id, lun_id)) result = self.call(url, None, 'DELETE') - self._assert_rest_result(result, - 'Delete associated lun from lungroup error.') + self._assert_rest_result( + result, _('Delete associated lun from lungroup error.')) def _initiator_is_added_to_array(self, ininame): """Check whether the initiator is already added on the array.""" url = self.url + "/iscsi_initiator?range=[0-256]" result = self.call(url, None, "GET") self._assert_rest_result(result, - 'Check initiator added to array error.') + _('Check initiator added to array error.')) if 'data' in result: for item in result['data']: @@ -667,8 +668,8 @@ class RestClient(object): """Check whether the initiator is associated to the host.""" url = self.url + "/iscsi_initiator?range=[0-256]" result = self.call(url, None, "GET") - self._assert_rest_result(result, - 'Check initiator associated to host error.') + self._assert_rest_result( + result, _('Check initiator associated to host error.')) if 'data' in result: for item in result['data']: @@ -683,7 +684,8 @@ class RestClient(object): "ID": initiator_name, "USECHAP": "false"}) result = self.call(url, data) - self._assert_rest_result(result, 'Add initiator to array error.') + self._assert_rest_result(result, + _('Add initiator to array error.')) def _add_initiator_to_host(self, initiator_name, host_id): url = self.url + "/iscsi_initiator/" + initiator_name @@ -693,7 +695,8 @@ class RestClient(object): "PARENTTYPE": "21", "PARENTID": host_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Associate initiator to host error.') + self._assert_rest_result(result, + _('Associate initiator to host error.')) def _associate_initiator_to_host(self, xml_file_path, @@ -701,22 +704,20 @@ class RestClient(object): host_id): """Associate initiator with the host.""" iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path) - chapinfo = None - multipathtype = None chapinfo = self.find_chap_info(iscsi_conf, initiator_name) - multipathtype = self._find_alua_info(iscsi_conf, - initiator_name) + multipath_type = self._find_alua_info(iscsi_conf, + initiator_name) if chapinfo: LOG.info(_LI('Use CHAP when adding initiator to host.')) self._use_chap(chapinfo, initiator_name, host_id) else: self._add_initiator_to_host(initiator_name, host_id) - if multipathtype: + if multipath_type: LOG.info(_LI('Use ALUA when adding initiator to host.')) - self._use_alua(initiator_name, multipathtype) + self._use_alua(initiator_name, multipath_type) def find_chap_info(self, iscsi_conf, initiator_name): """Find CHAP info from xml.""" @@ -731,7 +732,7 @@ class RestClient(object): def _find_alua_info(self, iscsi_conf, initiator_name): """Find ALUA info from xml.""" - multipathtype = 0 + multipath_type = 0 for ini in iscsi_conf['Initiator']: if ini['Name'] == initiator_name: if 'ALUA' in ini: @@ -742,9 +743,9 @@ class RestClient(object): LOG.error(msg) raise exception.InvalidInput(msg) else: - multipathtype = ini['ALUA'] + multipath_type = ini['ALUA'] break - return multipathtype + return multipath_type def _use_chap(self, chapinfo, initiator_name, host_id): """Use CHAP when adding initiator to host.""" @@ -759,22 +760,19 @@ class RestClient(object): "PARENTTYPE": "21", "PARENTID": host_id}) result = self.call(url, data, "PUT") + msg = _('Use CHAP to associate initiator to host error. ' + 'Please check the CHAP username and password.') + self._assert_rest_result(result, msg) - self._assert_rest_result(result, - 'Use CHAP to associate initiator ' - 'to host error. Please check the CHAP ' - 'username and password.') - - def _use_alua(self, initiator_name, multipathtype): + def _use_alua(self, initiator_name, multipath_type): """Use ALUA when adding initiator to host.""" url = self.url + "/iscsi_initiator" data = json.dumps({"ID": initiator_name, - "MULTIPATHTYPE": multipathtype}) + "MULTIPATHTYPE": multipath_type}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, - 'Use ALUA to associate initiator ' - 'to host error.') + self._assert_rest_result( + result, _('Use ALUA to associate initiator to host error.')) def remove_chap(self, initiator_name): """Remove CHAP when terminate connection.""" @@ -784,7 +782,7 @@ class RestClient(object): "ID": initiator_name}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Remove CHAP error.') + self._assert_rest_result(result, _('Remove CHAP error.')) def find_mapping_view(self, name): """Find mapping view.""" @@ -792,7 +790,7 @@ class RestClient(object): data = json.dumps({"TYPE": "245"}) result = self.call(url, data, "GET") - msg = 'Find mapping view error.' + msg = _('Find mapping view error.') self._assert_rest_result(result, msg) return self._get_id_from_result(result, name, 'NAME') @@ -801,29 +799,29 @@ class RestClient(object): url = self.url + "/mappingview" data = json.dumps({"NAME": name, "TYPE": "245"}) result = self.call(url, data) - self._assert_rest_result(result, 'Add mapping view error.') + self._assert_rest_result(result, _('Add mapping view error.')) return result['data']['ID'] - def _associate_hostgroup_to_view(self, viewID, hostGroupID): + def _associate_hostgroup_to_view(self, view_id, hostgroup_id): url = self.url + "/MAPPINGVIEW/CREATE_ASSOCIATE" data = json.dumps({"ASSOCIATEOBJTYPE": "14", - "ASSOCIATEOBJID": hostGroupID, + "ASSOCIATEOBJID": hostgroup_id, "TYPE": "245", - "ID": viewID}) + "ID": view_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Associate host to mapping view ' - 'error.') + self._assert_rest_result(result, _('Associate host to mapping view ' + 'error.')) - def _associate_lungroup_to_view(self, viewID, lunGroupID): + def _associate_lungroup_to_view(self, view_id, lungroup_id): url = self.url + "/MAPPINGVIEW/CREATE_ASSOCIATE" data = json.dumps({"ASSOCIATEOBJTYPE": "256", - "ASSOCIATEOBJID": lunGroupID, + "ASSOCIATEOBJID": lungroup_id, "TYPE": "245", - "ID": viewID}) + "ID": view_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Associate lungroup to mapping view ' - 'error.') + self._assert_rest_result( + result, _('Associate lungroup to mapping view error.')) def delete_lungroup_mapping_view(self, view_id, lungroup_id): """Remove lungroup associate from the mapping view.""" @@ -833,8 +831,8 @@ class RestClient(object): "TYPE": "245", "ID": view_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Delete lungroup from mapping view ' - 'error.') + self._assert_rest_result(result, _('Delete lungroup from mapping view ' + 'error.')) def delete_hostgoup_mapping_view(self, view_id, hostgroup_id): """Remove hostgroup associate from the mapping view.""" @@ -844,8 +842,8 @@ class RestClient(object): "TYPE": "245", "ID": view_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Delete hostgroup from mapping view ' - 'error.') + self._assert_rest_result( + result, _('Delete hostgroup from mapping view error.')) def delete_portgroup_mapping_view(self, view_id, portgroup_id): """Remove portgroup associate from the mapping view.""" @@ -855,14 +853,14 @@ class RestClient(object): "TYPE": "245", "ID": view_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Delete portgroup from mapping view ' - 'error.') + self._assert_rest_result( + result, _('Delete portgroup from mapping view error.')) def delete_mapping_view(self, view_id): """Remove mapping view from the storage.""" url = self.url + "/mappingview/" + view_id result = self.call(url, None, "DELETE") - self._assert_rest_result(result, 'Delete mapping view error.') + self._assert_rest_result(result, _('Delete mapping view error.')) def get_lunnum_from_lungroup(self, lungroup_id): """Check if there are still other luns associated to the lungroup.""" @@ -870,7 +868,7 @@ class RestClient(object): "ASSOCIATEOBJID=%s" % lungroup_id) url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Find lun number error.') + self._assert_rest_result(result, _('Find lun number error.')) lunnum = -1 if 'data' in result: lunnum = result['data']['COUNT'] @@ -882,8 +880,8 @@ class RestClient(object): "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id) url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Find portgroup from mapping view ' - 'error.') + self._assert_rest_result(result, _('Find portgroup from mapping view ' + 'error.')) if self._get_id_from_result(result, portgroup_id, 'ID'): return True @@ -895,17 +893,17 @@ class RestClient(object): "ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=%s" % view_id) url = self.url + url_subfix result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Find lun group from mapping view ' - 'error.') + self._assert_rest_result(result, _('Find lun group from mapping view ' + 'error.')) return self._get_id_from_result(result, view_id, 'ID') - def start_luncopy(self, luncopyid): + def start_luncopy(self, luncopy_id): """Start a LUNcopy.""" url = self.url + "/LUNCOPY/start" - data = json.dumps({"TYPE": "219", "ID": luncopyid}) + data = json.dumps({"TYPE": "219", "ID": luncopy_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Start LUNcopy error.') + self._assert_rest_result(result, _('Start LUNcopy error.')) def _get_capacity(self, pool_name, result): """Get free capacity and total capacity of the pool.""" @@ -914,24 +912,24 @@ class RestClient(object): 'free_capacity': 0.0} if poolinfo: - total = int(poolinfo['TOTALCAPACITY']) / 1024.0 / 1024.0 / 2 - free = int(poolinfo['CAPACITY']) / 1024.0 / 1024.0 / 2 + total = int(poolinfo['TOTALCAPACITY']) / constants.CAPACITY_UNIT + free = int(poolinfo['CAPACITY']) / constants.CAPACITY_UNIT pool_capacity['total_capacity'] = total pool_capacity['free_capacity'] = free return pool_capacity - def get_luncopy_info(self, luncopyid): + def get_luncopy_info(self, luncopy_id): """Get LUNcopy information.""" url = self.url + "/LUNCOPY?range=[0-1023]" data = json.dumps({"TYPE": "219", }) result = self.call(url, data, "GET") - self._assert_rest_result(result, 'Get LUNcopy information error.') + self._assert_rest_result(result, _('Get LUNcopy information error.')) luncopyinfo = {} if 'data' in result: for item in result['data']: - if luncopyid == item['ID']: + if luncopy_id == item['ID']: luncopyinfo['name'] = item['NAME'] luncopyinfo['id'] = item['ID'] luncopyinfo['state'] = item['HEALTHSTATUS'] @@ -939,11 +937,11 @@ class RestClient(object): break return luncopyinfo - def delete_luncopy(self, luncopyid): + def delete_luncopy(self, luncopy_id): """Delete a LUNcopy.""" - url = self.url + "/LUNCOPY/%s" % luncopyid + url = self.url + "/LUNCOPY/%s" % luncopy_id result = self.call(url, None, "DELETE") - self._assert_rest_result(result, 'Delete LUNcopy error.') + self._assert_rest_result(result, _('Delete LUNcopy error.')) def get_connected_free_wwns(self): """Get free connected FC port WWNs. @@ -953,7 +951,7 @@ class RestClient(object): url = self.url + "/fc_initiator?ISFREE=true&range=[0-8191]" result = self.call(url, None, "GET") - msg = 'Get connected free FC wwn error.' + msg = _('Get connected free FC wwn error.') self._assert_rest_result(result, msg) wwns = [] @@ -971,14 +969,14 @@ class RestClient(object): "PARENTTYPE": 21, "PARENTID": host_id}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Add FC port to host error.') + self._assert_rest_result(result, _('Add FC port to host error.')) def _get_iscsi_port_info(self, ip): """Get iscsi port info in order to build the iscsi target iqn.""" url = self.url + "/eth_port" result = self.call(url, None, "GET") - msg = 'Get iSCSI port information error.' + msg = _('Get iSCSI port information error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -1029,7 +1027,7 @@ class RestClient(object): "/host_link?INITIATOR_TYPE=223&INITIATOR_PORT_WWN=" + wwn) result = self.call(url, None, "GET") - msg = 'Get FC target wwpn error.' + msg = _('Get FC target wwpn error.') self._assert_rest_result(result, msg) fc_wwpns = None @@ -1051,7 +1049,7 @@ class RestClient(object): LOG.error(msg) raise exception.InvalidInput(msg) data = {} - data["pools"] = [] + data['pools'] = [] result = self.find_all_pools() for pool_name in pool_names.split(";"): pool_name = pool_name.strip(' \t\n\r') @@ -1063,14 +1061,14 @@ class RestClient(object): 'QoS_support': True, } - data["pools"].append(pool) + data['pools'].append(pool) return data def _find_qos_policy_info(self, policy_name): url = self.url + "/ioclass" result = self.call(url, None, "GET") - msg = 'Get QoS policy error.' + msg = _('Get QoS policy error.') self._assert_rest_result(result, msg) qos_info = {} @@ -1091,15 +1089,15 @@ class RestClient(object): "ID": policy_id, "LUNLIST": lunlist}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Update QoS policy error.') + self._assert_rest_result(result, _('Update QoS policy error.')) def _get_tgt_ip_from_portgroup(self, portgroup_id): - target_ip = None + target_ips = [] url = self.url + ("/eth_port/associate?TYPE=213&ASSOCIATEOBJTYPE=257" "&ASSOCIATEOBJID=%s" % portgroup_id) result = self.call(url, None, "GET") - msg = 'Get target IP error.' + msg = _('Get target IP error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -1111,14 +1109,16 @@ class RestClient(object): target_ip = item['IPV4ADDR'] LOG.info(_LI('_get_tgt_ip_from_portgroup: Get ip: %s.'), target_ip) - break - return target_ip + target_ips.append(target_ip) + + return target_ips def get_iscsi_params(self, xml_file_path, connector): """Get target iSCSI params, including iqn, IP.""" initiator = connector['initiator'] iscsi_conf = huawei_utils.get_iscsi_conf(xml_file_path) - target_ip = None + target_ips = [] + target_iqns = [] portgroup = None portgroup_id = None for ini in iscsi_conf['Initiator']: @@ -1127,16 +1127,16 @@ class RestClient(object): if key == 'TargetPortGroup': portgroup = ini['TargetPortGroup'] elif key == 'TargetIP': - target_ip = ini['TargetIP'] + target_ips.append(ini['TargetIP']) if portgroup: portgroup_id = self.find_tgt_port_group(portgroup) - target_ip = self._get_tgt_ip_from_portgroup(portgroup_id) + target_ips = self._get_tgt_ip_from_portgroup(portgroup_id) # If not specify target IP for some initiators, use default IP. - if not target_ip: + if not target_ips: if iscsi_conf['DefaultTargetIP']: - target_ip = iscsi_conf['DefaultTargetIP'] + target_ips.append(iscsi_conf['DefaultTargetIP']) else: msg = (_( @@ -1146,14 +1146,14 @@ class RestClient(object): LOG.error(msg) raise exception.InvalidInput(reason=msg) - LOG.info(_LI('Get the default ip: %s.'), target_ip) - # If didn't get target IP for rest, Automated assembly target ip. - target_iqn = self._get_tgt_iqn_from_rest(target_ip) + LOG.info(_LI('Get the default ip: %s.'), target_ips) + for ip in target_ips: + target_iqn = self._get_tgt_iqn_from_rest(ip) + if not target_iqn: + target_iqn = self._get_tgt_iqn(ip) + target_iqns.append(target_iqn) - if not target_iqn: - target_iqn = self._get_tgt_iqn(target_ip) - - return (target_iqn, target_ip, portgroup_id) + return (target_iqns, target_ips, portgroup_id) def _get_tgt_iqn_from_rest(self, target_ip): url = self.url + "/iscsi_tgt_port" @@ -1199,7 +1199,7 @@ class RestClient(object): url = self.url + "/ioclass/" result = self.call(url, data) - self._assert_rest_result(result, 'Create QoS policy error.') + self._assert_rest_result(result, _('Create QoS policy error.')) return result['data']['ID'] @@ -1210,7 +1210,7 @@ class RestClient(object): "ID": qos_id}) result = self.call(url, data, 'DELETE') - self._assert_rest_result(result, 'Delete QoS policy error.') + self._assert_rest_result(result, _('Delete QoS policy error.')) def active_deactive_qos(self, qos_id, enablestatus): """Active or deactive QoS. @@ -1223,7 +1223,7 @@ class RestClient(object): "ID": qos_id, "ENABLESTATUS": enablestatus}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Active or deactive QoS error.') + self._assert_rest_result(result, _('Active or deactive QoS error.')) def get_qos_info(self, qos_id): """Get QoS information.""" @@ -1231,7 +1231,7 @@ class RestClient(object): data = json.dumps({"TYPE": "230", "ID": qos_id}) result = self.call(url, data, "GET") - self._assert_rest_result(result, 'Get QoS information error.') + self._assert_rest_result(result, _('Get QoS information error.')) return result['data'] @@ -1243,16 +1243,16 @@ class RestClient(object): "IOPRIORITY": "3"}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Change lun priority error.') + self._assert_rest_result(result, _('Change lun priority error.')) def get_qosid_by_lunid(self, lun_id): - """Get qosid by lun id.""" + """Get QoS id by lun id.""" url = self.url + "/lun/" + lun_id data = json.dumps({"TYPE": "11", "ID": lun_id}) result = self.call(url, data, "GET") - self._assert_rest_result(result, 'Get QoS id by lun id error.') + self._assert_rest_result(result, _('Get QoS id by lun id error.')) return result['data']['IOCLASSID'] @@ -1262,7 +1262,7 @@ class RestClient(object): "&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s" % lun_id) result = self.call(url, None, "GET") - self._assert_rest_result(result, 'Get lungroup id by lun id error.') + self._assert_rest_result(result, _('Get lungroup id by lun id error.')) return self._get_id_from_result(result, lun_id, 'ID') @@ -1272,7 +1272,7 @@ class RestClient(object): "ID": lun_id}) result = self.call(url, data, "GET") - msg = 'Get volume error.' + msg = _('Get volume error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -1284,7 +1284,7 @@ class RestClient(object): "CAPACITY": new_volume_size}) result = self.call(url, data, 'PUT') - msg = 'Extend volume error.' + msg = _('Extend volume error.') self._assert_rest_result(result, msg) self._assert_data_in_result(result, msg) @@ -1293,23 +1293,24 @@ class RestClient(object): def remove_host(self, host_id): url = self.url + "/host/%s" % host_id result = self.call(url, None, "DELETE") - self._assert_rest_result(result, 'Remove host from array error.') + self._assert_rest_result(result, _('Remove host from array error.')) def delete_hostgroup(self, hostgroup_id): url = self.url + "/hostgroup/%s" % hostgroup_id result = self.call(url, None, "DELETE") - self._assert_rest_result(result, 'Delete hostgroup error.') + self._assert_rest_result(result, _('Delete hostgroup error.')) def remove_host_from_hostgroup(self, hostgroup_id, host_id): url_subfix001 = "/host/associate?TYPE=14&ID=%s" % hostgroup_id url_subfix002 = "&ASSOCIATEOBJTYPE=21&ASSOCIATEOBJID=%s" % host_id url = self.url + url_subfix001 + url_subfix002 result = self.call(url, None, "DELETE") - self._assert_rest_result(result, 'Remove host from hostgroup error.') + self._assert_rest_result(result, + _('Remove host from hostgroup error.')) def remove_iscsi_from_host(self, initiator): url = self.url + "/iscsi_initiator/remove_iscsi_from_host" data = json.dumps({"TYPE": '222', "ID": initiator}) result = self.call(url, data, "PUT") - self._assert_rest_result(result, 'Remove iscsi from host error.') + self._assert_rest_result(result, _('Remove iscsi from host error.'))