from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers.huawei import constants
+from cinder.volume.drivers.huawei import fc_zone_helper
from cinder.volume.drivers.huawei import huawei_driver
from cinder.volume.drivers.huawei import huawei_utils
from cinder.volume.drivers.huawei import rest_client
'provider_location': '11',
}
+fake_smartx_value = {'smarttier': 'true',
+ 'smartcache': 'true',
+ 'smartpartition': 'true',
+ 'thin_provisioning_support': 'true',
+ 'thick_provisioning_support': False,
+ 'policy': '2',
+ 'cachename': 'cache-test',
+ 'partitionname': 'partition-test',
+ }
+
error_volume = {'name': 'volume-21ec7341-9256-497b-97d9-ef48edcf0637',
'size': 2,
'volume_name': 'vol2',
'display_description': 'test volume',
'volume_type_id': None,
'provider_location': '11',
+ 'volume': {"volume_id": '21ec7341-9256-497b-97d9-ef48edcf0635'}
}
test_host = {'host': 'ubuntu001@backend001#OpenStack_Pool',
'hypermetro': True,
'reserved_percentage': 0,
'vendor_name': None,
- 'thick_provisioning': True,
- 'thin_provisioning': True,
+ 'thick_provisioning_support': False,
+ 'thin_provisioning_support': True,
'storage_protocol': 'FC',
}
}
'smarttier': '<is> true',
'smartcache': '<is> true',
'smartpartition': '<is> true',
- 'thin_provisioning': '<is> true',
- 'thick_provisioning': '<is> False',
+ 'thin_provisioning_support': '<is> true',
+ 'thick_provisioning_support': '<is> False',
'policy': '2',
'smartcache:cachename': 'cache-test',
'smartpartition:partitionname': 'partition-test',
'writecachepolicy': None,
}
+fake_fabric_mapping = {
+ 'swd1': {
+ 'target_port_wwn_list': ['2000643e8c4c5f66'],
+ 'initiator_port_wwn_list': ['10000090fa0d6754']
+ }
+}
+
+FAKE_CREATE_VOLUME_RESPONSE = {"ID": "1",
+ "NAME": "5mFHcBv4RkCcD+JyrWc0SA"}
+
+CHANGE_OPTS = {'policy': ('1', '2'),
+ 'partitionid': (['1', 'partition001'], ['2', 'partition002']),
+ 'cacheid': (['1', 'cache001'], ['2', 'cache002']),
+ 'qos': (['11', {'MAXIOPS': '100', 'IOType': '1'}],
+ {'MAXIOPS': '100', 'IOType': '2',
+ 'MIN': 1, 'LATENCY': 1}),
+ 'host': ('ubuntu@huawei#OpenStack_Pool',
+ 'ubuntu@huawei#OpenStack_Pool'),
+ 'LUNType': ('0', '1'),
+ }
+
+# A fake response of create a host
+FAKE_CREATE_HOST_RESPONSE = """
+{
+ "error": {
+ "code": 0
+ },
+ "data":{"NAME": "ubuntuc001",
+ "ID": "1"}
+}
+"""
+
# A fake response of success response storage
FAKE_COMMON_SUCCESS_RESPONSE = """
{
},
"data": {
"ID": "11",
- "IOCLASSID": "",
+ "IOCLASSID": "11",
"NAME": "5mFHcBv4RkCcD+JyrWc0SA",
"RUNNINGSTATUS": "2",
"HEALTHSTATUS": "1",
"RUNNINGSTATUS": "27",
- "ALLOCTYPE": "0",
+ "LUNLIST": "",
+ "ALLOCTYPE": "1",
"CAPACITY": "2097152",
"WRITEPOLICY": "1",
"MIRRORPOLICY": "0",
FAKE_LUN_COUNT_RESPONSE = """
{
"data":{
- "COUNT":"7"
+ "COUNT":"0"
},
"error":{
"code":0,
{
"ID": 12,
"NAME": "SDFAJSDFLKJ"
+ },
+ {
+ "ID": 13,
+ "NAME": "s1Ew5v36To-hR2txJitX5Q"
}]
}
"""
"code": 0
},
"data": [{
- "NAME": "OpenStack_HostGroup_1",
+ "NAME":"ubuntuc",
"DESCRIPTION":"",
"ID":"0",
"TYPE":14
- }]
+ },
+ {"NAME":"OpenStack_HostGroup_1",
+ "DESCRIPTION":"",
+ "ID":"0",
+ "TYPE":14
+ }
+ ]
}
"""
"data":[{
"WORKMODE":"255",
"HEALTHSTATUS":"1",
- "NAME":"IexzQZJWSXuX2e9I7c8GNQ",
+ "NAME":"OpenStack_Mapping_View_1",
"RUNNINGSTATUS":"27",
"DESCRIPTION":"",
"ENABLEINBANDCOMMAND":"true",
"DESCRIPTION":"",
"ENABLEINBANDCOMMAND":"true",
"ID":"2",
- "INBANDLUNWWN":"",
- "TYPE":245
+ "INBANDLUNWWN": "",
+ "TYPE": 245
}]
}
"""
FAKE_GET_MAPPING_VIEW_RESPONSE = """
+{
+ "error":{
+ "code":0
+ },
+ "data":[{
+ "WORKMODE":"255",
+ "HEALTHSTATUS":"1",
+ "NAME":"mOWtSXnaQKi3hpB3tdFRIQ",
+ "RUNNINGSTATUS":"27",
+ "DESCRIPTION":"",
+ "ENABLEINBANDCOMMAND":"true",
+ "ID":"11",
+ "INBANDLUNWWN":"",
+ "TYPE": 245,
+ "AVAILABLEHOSTLUNIDLIST": ""
+ }]
+}
+"""
+
+FAKE_GET_SPEC_MAPPING_VIEW_RESPONSE = """
{
"error":{
"code":0
"data":{
"ID":"11",
"IOCLASSID":"11",
- "NAME":"5mFHcBv4RkCcD+JyrWc0SA"
+ "NAME":"5mFHcBv4RkCcD+JyrWc0SA",
+ "ALLOCTYPE": "0",
+ "DATATRANSFERPOLICY": "0",
+ "SMARTCACHEPARTITIONID": "0",
+ "CACHEPARTITIONID": "0"
}
}
"""
+FAKE_GET_FC_INI_RESPONSE = """
+{
+ "error":{
+ "code":0
+ },
+ "data":[{
+ "ID":"10000090fa0d6754",
+ "ISFREE":"true"
+ }]
+}
+"""
+
+FAKE_GET_FC_PORT_RESPONSE = """
+{
+ "error":{
+ "code":0
+ },
+ "data":[{
+ "RUNNINGSTATUS":"10",
+ "WWN":"2000643e8c4c5f66",
+ "PARENTID":"0A.1"
+ }]
+}
+"""
FAKE_SYSTEM_VERSION_RESPONSE = """
{
}
"""
+FAKE_GET_FC_INI_RESPONSE = """
+{
+ "error":{
+ "code":0
+ },
+ "data":[{
+ "ID":"10000090fa0d6754",
+ "ISFREE":"true"
+ }]
+}
+"""
+
FAKE_QOS_INFO_RESPONSE = """
{
"error":{
}
"""
+FAKE_GET_FC_PORT_RESPONSE = """
+{
+ "error":{
+ "code":0
+ },
+ "data":[{
+ "RUNNINGSTATUS":"10",
+ "WWN":"2000643e8c4c5f66",
+ "PARENTID":"0A.1"
+ }]
+}
+"""
+
# mock login info map
MAP_COMMAND_TO_FAKE_RESPONSE = {}
MAP_COMMAND_TO_FAKE_RESPONSE['/xx/sessions'] = (
MAP_COMMAND_TO_FAKE_RESPONSE['lun/1/DELETE'] = (
FAKE_COMMON_SUCCESS_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/1/PUT'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/11/PUT'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
MAP_COMMAND_TO_FAKE_RESPONSE['lun?range=[0-65535]/GET'] = (
FAKE_QUERY_ALL_LUN_RESPONSE)
'&ASSOCIATEOBJID=1/GET'] = (
FAKE_COMMON_SUCCESS_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['lun/associate/cachepartition?ID=1'
+ '&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=11'
+ '/DELETE'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup?range=[0-8191]/GET'] = (
FAKE_QUERY_LUN_GROUP_INFO_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate'] = (
FAKE_QUERY_LUN_GROUP_ASSOCIAT_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['LUNGroup/11/DELETE'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?ID=11&ASSOCIATEOBJTYPE=11'
'&ASSOCIATEOBJID=1/DELETE'] = (
FAKE_COMMON_SUCCESS_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?TYPE=256&ASSOCIATEOBJTYPE=11'
'&ASSOCIATEOBJID=1/GET'] = (
- FAKE_COMMON_SUCCESS_RESPONSE)
+ FAKE_LUN_ASSOCIATE_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['lungroup/associate?ID=11&ASSOCIATEOBJTYPE=11'
'&ASSOCIATEOBJID=11/DELETE'] = (
MAP_COMMAND_TO_FAKE_RESPONSE['eth_port/GET'] = (
FAKE_GET_ETH_INFO_RESPONSE)
-MAP_COMMAND_TO_FAKE_RESPONSE['eth_port/associate?TYPE=213&ASSOCIATEOBJTYPE=257'
- '&ASSOCIATEOBJID=11/GET'] = (
+MAP_COMMAND_TO_FAKE_RESPONSE['eth_port/associate?TYPE=213&ASSOCIATEOBJTYPE'
+ '=257&ASSOCIATEOBJID=11/GET'] = (
FAKE_GET_ETH_ASSOCIATE_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['iscsidevicename'] = (
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)
MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/PUT'] = (
FAKE_ISCSI_INITIATOR_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/remove_iscsi_from_host/PUT'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
MAP_COMMAND_TO_FAKE_RESPONSE['iscsi_initiator/'
'iqn.1993-08.debian:01:ec2bff7ac3a3/PUT'] = (
FAKE_ISCSI_INITIATOR_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['host?range=[0-65535]/GET'] = (
FAKE_GET_ALL_HOST_INFO_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['host/1/DELETE'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['host'] = (
+ FAKE_CREATE_HOST_RESPONSE)
+
MAP_COMMAND_TO_FAKE_RESPONSE['hostgroup?range=[0-8191]/GET'] = (
FAKE_GET_ALL_HOST_GROUP_INFO_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['hostgroup'] = (
FAKE_GET_HOST_GROUP_INFO_RESPONSE)
-MAP_COMMAND_TO_FAKE_RESPONSE['host/associate?TYPE=21&ASSOCIATEOBJTYPE=14'
+MAP_COMMAND_TO_FAKE_RESPONSE['host/associate?TYPE=14&ID=0'
+ '&ASSOCIATEOBJTYPE=21&ASSOCIATEOBJID=1'
+ '/DELETE'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['host/associate?TYPE=14&ID=0'
'&ASSOCIATEOBJID=0/GET'] = (
FAKE_COMMON_SUCCESS_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['host/associate?TYPE=21&'
+ 'ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=0/GET'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['hostgroup/0/DELETE'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
MAP_COMMAND_TO_FAKE_RESPONSE['hostgroup/associate'] = (
FAKE_COMMON_SUCCESS_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['mappingview'] = (
FAKE_GET_MAPPING_VIEW_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['MAPPINGVIEW/1/GET'] = (
+ FAKE_GET_SPEC_MAPPING_VIEW_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['mappingview/1/DELETE'] = (
+ FAKE_COMMON_SUCCESS_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['mappingview/associate/lungroup?TYPE=256&'
+ 'ASSOCIATEOBJTYPE=245&ASSOCIATEOBJID=1/GET'] = (
+ FAKE_GET_MAPPING_VIEW_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['mappingview/associate?TYPE=245&'
+ 'ASSOCIATEOBJTYPE=14&ASSOCIATEOBJID=0/GET'] = (
+ FAKE_GET_MAPPING_VIEW_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['mappingview/associate?TYPE=245&'
+ 'ASSOCIATEOBJTYPE=256&ASSOCIATEOBJID=11/GET'] = (
+ FAKE_GET_MAPPING_VIEW_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['mappingview/associate?TYPE=245&'
+ 'ASSOCIATEOBJTYPE=257&ASSOCIATEOBJID=11/GET'] = (
+ FAKE_GET_MAPPING_VIEW_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['portgroup/associate?ASSOCIATEOBJTYPE=245&'
+ 'ASSOCIATEOBJID=1&range=[0-8191]/GET'] = (
+ FAKE_GET_MAPPING_VIEW_RESPONSE)
+
MAP_COMMAND_TO_FAKE_RESPONSE['MAPPINGVIEW/CREATE_ASSOCIATE/PUT'] = (
FAKE_COMMON_SUCCESS_RESPONSE)
# mock FC info map
-MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator?ISFREE=true&range=[0-8191]/GET'] = (
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator?ISFREE=true&'
+ 'range=[0-8191]/GET'] = (
+ FAKE_FC_INFO_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator/10000090fa0d6754/GET'] = (
FAKE_FC_INFO_RESPONSE)
MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator/10000090fa0d6754/PUT'] = (
MAP_COMMAND_TO_FAKE_RESPONSE['system/'] = (
FAKE_SYSTEM_VERSION_RESPONSE)
-MAP_COMMAND_TO_FAKE_RESPONSE['lun/associate/cachepartition/POST'] = (
- FAKE_SYSTEM_VERSION_RESPONSE)
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator?range=[0-256]/GET'] = (
+ FAKE_GET_FC_INI_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_port/GET'] = (
+ FAKE_GET_FC_PORT_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator/GET'] = (
+ FAKE_GET_FC_PORT_RESPONSE)
+
+MAP_COMMAND_TO_FAKE_RESPONSE['fc_initiator?range=[0-100]/GET'] = (
+ FAKE_GET_FC_PORT_RESPONSE)
def Fake_sleep(time):
rest_client.RestClient.__init__(self, configuration)
self.delete_flag = False
self.terminateFlag = False
- self.deviceid = None
+ self.device_id = None
self.test_fail = False
self.checkFlag = False
self.remove_chap_flag = False
def __init__(self, configuration):
self.configuration = configuration
self.xml_file_path = self.configuration.cinder_huawei_conf_file
+ self.fcsan_lookup_service = None
def do_setup(self):
self.restclient = Fake18000Client(configuration=self.configuration)
self.portgroup_id = 11
def test_login_success(self):
- deviceid = self.driver.restclient.login()
- self.assertEqual('210235G7J20000000000', deviceid)
+ device_id = self.driver.restclient.login()
+ self.assertEqual('210235G7J20000000000', device_id)
def test_create_volume_success(self):
self.driver.restclient.login()
def test_create_snapshot_success(self):
self.driver.restclient.login()
- lun_info = self.driver.create_snapshot(test_volume)
+ lun_info = self.driver.create_snapshot(test_snap)
self.assertEqual(11, lun_info['provider_location'])
def test_delete_snapshot_success(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_snapshot, test_volume)
+ self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_volume, test_volume)
-
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, error_volume)
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
+ @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
@mock.patch.object(huawei_utils, 'get_volume_params',
return_value={'smarttier': 'true',
'smartcache': 'true',
'smartpartition': 'true',
'thin_provisioning_support': 'true',
- 'thick_provisioning_support': False,
+ 'thick_provisioning_support': 'false',
'policy': '2',
'cachename': 'cache-test',
'partitionname': 'partition-test'})
- def test_creat_smartx(self, mock_volume_types):
+ def test_creat_smartx(self, mock_volume_types, mock_add_lun_to_partition):
self.driver.restclient.login()
lun_info = self.driver.create_volume(test_volume)
self.assertEqual('1', lun_info['provider_location'])
fakefile.close()
+class FCSanLookupService(object):
+ def get_device_mapping_from_network(self, initiator_list,
+ target_list):
+ return fake_fabric_mapping
+
+
class Huawei18000FCDriverTestCase(test.TestCase):
def setUp(self):
self.driver.do_setup()
def test_login_success(self):
- deviceid = self.driver.restclient.login()
- self.assertEqual('210235G7J20000000000', deviceid)
+ device_id = self.driver.restclient.login()
+ self.assertEqual('210235G7J20000000000', device_id)
def test_create_volume_success(self):
self.driver.restclient.login()
def test_create_snapshot_success(self):
self.driver.restclient.login()
- lun_info = self.driver.create_snapshot(test_volume)
+ lun_info = self.driver.create_snapshot(test_snap)
self.assertEqual(11, lun_info['provider_location'])
def test_delete_snapshot_success(self):
self.driver.restclient.login()
self.driver.restclient.test_fail = True
self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_snapshot, test_volume)
+ self.driver.create_snapshot, test_snap)
def test_create_volume_fail(self):
self.driver.restclient.login()
self.configuration)
self.assertEqual('0', host_os)
- def test_migrate_volume_success(self):
+ @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
+ def test_migrate_volume_success(self, mock_add_lun_to_partition):
self.driver.restclient.login()
# Migrate volume without new type.
{'smarttier': '<is> true',
'smartcache': '<is> true',
'smartpartition': '<is> true',
- 'thin_provisioning': '<is> true',
- 'thick_provisioning': '<is> False',
+ 'thin_provisioning_support': '<is> true',
+ 'thick_provisioning_support': '<is> False',
'policy': '2',
'smartcache:cachename': 'cache-test',
'smartpartition:partitionname': 'partition-test'}}
new_type = {'extra_specs':
{'smarttier': '<is> true',
'smartcache': '<is> true',
- 'thin_provisioning': '<is> true',
- 'thick_provisioning': '<is> False',
+ 'thin_provisioning_support': '<is> true',
+ 'thick_provisioning_support': '<is> False',
'policy': '2',
'smartcache:cachename': 'cache-test',
'partitionname': 'partition-test'}}
self.assertEqual({'_name_id': '21ec7341-9256-497b-97d9-ef48edcf0637'},
model_update)
- def test_retype_volume_success(self):
+ @mock.patch.object(rest_client.RestClient, 'add_lun_to_partition')
+ def test_retype_volume_success(self, mock_add_lun_to_partition):
self.driver.restclient.login()
retype = self.driver.retype(None, test_volume,
test_new_type, None, test_host)
test_new_type, None, test_host)
self.assertFalse(retype)
+ def test_build_ini_targ_map(self):
+ self.driver.restclient.login()
+ fake_lookup_service = FCSanLookupService()
+ fake_lookup_service.get_device_mapping_from_network = mock.Mock(
+ return_value=fake_fabric_mapping)
+
+ zone_helper = fc_zone_helper.FCZoneHelper(
+ fake_lookup_service, self.driver.restclient)
+ (tgt_port_wwns,
+ init_targ_map) = (zone_helper.build_ini_targ_map(
+ ['10000090fa0d6754']))
+ target_port_wwns = ['2000643e8c4c5f66']
+ ini_target_map = {'10000090fa0d6754': ['2000643e8c4c5f66']}
+ self.assertEqual(target_port_wwns, tgt_port_wwns)
+ self.assertEqual(ini_target_map, init_targ_map)
+
+ def test_filter_port_by_contr(self):
+ self.driver.restclient.login()
+ # Six ports in one fabric.
+ ports_in_fabric = ['1', '2', '3', '4', '5', '6']
+ # Ports 1,3,4,7 belonged to controller A
+ # Ports 2,5,8 belonged to controller B
+ # ports 6 belonged to controller C
+ total_port_contr_map = {'1': 'A', '3': 'A', '4': 'A', '7': 'A',
+ '2': 'B', '5': 'B', '8': 'B',
+ '6': 'C'}
+ zone_helper = fc_zone_helper.FCZoneHelper(None, None)
+ filtered_ports = zone_helper._filter_port_by_contr(
+ ports_in_fabric, total_port_contr_map)
+ expected_filtered_ports = ['1', '3', '2', '5', '6']
+ self.assertEqual(expected_filtered_ports, filtered_ports)
+
def create_fake_conf_file(self):
"""Create a fake Config file
MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_'
QOS_NAME_PREFIX = 'OpenStack_'
ARRAY_VERSION = 'V300R003C00'
+FC_PORT_CONNECTED = '10'
+FC_INIT_ONLINE = '27'
CAPACITY_UNIT = 1024.0 / 1024.0 / 2
DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30
DEFAULT_WAIT_INTERVAL = 5
'Mac OS X': '6',
'VMware ESX': '7'}
+CONTROLLER_LIST = ['A', 'B', 'C', 'D']
HUAWEI_VALID_KEYS = ['maxIOPS', 'minIOPS', 'minBandWidth',
'maxBandWidth', 'latency', 'IOType']
QOS_KEYS = ['MAXIOPS', 'MINIOPS', 'MINBANDWidth',
--- /dev/null
+# Copyright (c) 2015 Huawei Technologies Co., Ltd.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_log import log as logging
+
+from cinder.volume.drivers.huawei import constants
+
+LOG = logging.getLogger(__name__)
+
+
+class FCZoneHelper(object):
+ """FC zone helper for Huawei driver."""
+
+ def __init__(self, fcsan_lookup_service, restclient):
+ self.fcsan_lookup_service = fcsan_lookup_service
+ self.restclient = restclient
+
+ def _get_fc_port_contr_map(self):
+ port_list = []
+ port_contr_map = {}
+ data = self.restclient.get_fc_ports_on_array()
+ for item in data:
+ if item['RUNNINGSTATUS'] == constants.FC_PORT_CONNECTED:
+ port_list.append(item['WWN'])
+ location = item['PARENTID'].split('.')
+ port_contr_map[item['WWN']] = location[0][1]
+ return port_list, port_contr_map
+
+ def _filter_port_by_contr(self, ports_in_fabric, port_contr_map):
+ filtered_ports = []
+ for contr in constants.CONTROLLER_LIST:
+ found_port_per_contr = 0
+ for port in ports_in_fabric:
+ if port in port_contr_map and port_contr_map[port] == contr:
+ filtered_ports.append(port)
+ found_port_per_contr = found_port_per_contr + 1
+ # We select two ports per every controller.
+ if found_port_per_contr == 2:
+ break
+ return filtered_ports
+
+ def build_ini_targ_map(self, wwns):
+ tgt_wwns = []
+ init_targ_map = {}
+ port_lists, port_contr_map = self._get_fc_port_contr_map()
+ ini_tgt_map = (self.fcsan_lookup_service.
+ get_device_mapping_from_network(wwns, port_lists))
+
+ for fabric in ini_tgt_map:
+ ports_in_fabric = ini_tgt_map[fabric]['target_port_wwn_list']
+ contr_filtered_ports = self._filter_port_by_contr(ports_in_fabric,
+ port_contr_map)
+ tgt_wwns.extend(contr_filtered_ports)
+ for ini in ini_tgt_map[fabric]['initiator_port_wwn_list']:
+ init_targ_map[ini] = contr_filtered_ports
+
+ return (list(set(tgt_wwns)), init_targ_map)
from cinder import utils
from cinder.volume import driver
from cinder.volume.drivers.huawei import constants
+from cinder.volume.drivers.huawei import fc_zone_helper
from cinder.volume.drivers.huawei import huawei_utils
from cinder.volume.drivers.huawei import rest_client
from cinder.volume.drivers.huawei import smartx
smartcache = smartx.SmartCache(self.restclient)
smartcache.add(opts, lun_id)
except Exception as err:
- if lun_id:
- self._delete_lun_with_check(lun_id)
+ self._delete_lun_with_check(lun_id)
raise exception.InvalidInput(
reason=_('Create volume error. Because %s.') % err)
lun_list,
qos_id)
- def _delete_lun_with_check(self, dst_id):
- LOG.info(_LI("Try to delete lun %s if it exists."), dst_id)
- if self.restclient.check_lun_exist(dst_id):
- qos_id = self.restclient.get_qosid_by_lunid(dst_id)
- if qos_id:
- qos = smartx.SmartQos(self.restclient)
- qos.delete_qos(qos_id)
- self.restclient.delete_lun(dst_id)
+ def _delete_lun_with_check(self, lun_id):
+ if lun_id:
+ if self.restclient.check_lun_exist(lun_id):
+ qos_id = self.restclient.get_qosid_by_lunid(lun_id)
+ if qos_id:
+ self.remove_qos_lun(lun_id, qos_id)
+
+ self.restclient.delete_lun(lun_id)
def _is_lun_migration_complete(self, src_id, dst_id):
result = self.restclient.get_lun_migration_task()
return 'in-use'
def update_migrated_volume(self, ctxt, volume, new_volume,
- original_volume_status):
+ original_volume_status=None):
original_name = huawei_utils.encode_name(volume['id'])
current_name = huawei_utils.encode_name(new_volume['id'])
return True
- @utils.synchronized('huawei', external=True)
- def initialize_connection_fc(self, volume, connector):
- wwns = connector['wwpns']
- volume_name = huawei_utils.encode_name(volume['id'])
-
- LOG.info(_LI(
- 'initialize_connection_fc, initiator: %(wwpns)s,'
- ' volume name: %(volume)s.'),
- {'wwpns': wwns,
- 'volume': volume_name},)
-
- host_name_before_hash = None
- host_name = connector['host']
- if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
- host_name_before_hash = host_name
- host_name = six.text_type(hash(host_name))
-
- # Create hostgroup if not exist.
- host_id = self.restclient.add_host_with_check(host_name,
- host_name_before_hash)
-
- # Add host into hostgroup.
- hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
-
- free_wwns = self.restclient.get_connected_free_wwns()
- LOG.info(_LI("initialize_connection_fc, the array has free wwns: %s."),
- free_wwns)
- for wwn in wwns:
- if wwn in free_wwns:
- self.restclient.add_fc_port_to_host(host_id, wwn)
-
- lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
- hostgroup_id,
- host_id)
- host_lun_id = self.restclient.find_host_lun_id(host_id, lun_id)
-
- tgt_port_wwns = []
- for wwn in wwns:
- tgtwwpns = self.restclient.get_fc_target_wwpns(wwn)
- if tgtwwpns:
- tgt_port_wwns.append(tgtwwpns)
-
- init_targ_map = {}
- for initiator in wwns:
- init_targ_map[initiator] = tgt_port_wwns
-
- # Return FC properties.
- info = {'driver_volume_type': 'fibre_channel',
- 'data': {'target_lun': int(host_lun_id),
- 'target_discovered': True,
- 'target_wwn': tgt_port_wwns,
- 'volume_id': volume['id'],
- 'initiator_target_map': init_targ_map}, }
-
- LOG.info(_LI("initialize_connection_fc, return data is: %s."),
- info)
-
- return info
-
- @utils.synchronized('huawei', external=True)
- def initialize_connection_iscsi(self, volume, connector):
- """Map a volume to a host and return target iSCSI information."""
- LOG.info(_LI('Enter initialize_connection_iscsi.'))
- initiator_name = connector['initiator']
- volume_name = huawei_utils.encode_name(volume['id'])
-
- LOG.info(_LI(
- 'initiator name: %(initiator_name)s, '
- 'volume name: %(volume)s.'),
- {'initiator_name': initiator_name,
- 'volume': volume_name})
-
- (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, '
- 'portgroup_id: %(portgroup_id)s.'),
- {'iscsi_iqn': iscsi_iqns,
- 'target_ip': target_ips,
- 'portgroup_id': portgroup_id},)
-
- # Create hostgroup if not exist.
- host_name = connector['host']
- host_name_before_hash = None
- if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
- host_name_before_hash = host_name
- host_name = six.text_type(hash(host_name))
- host_id = self.restclient.add_host_with_check(host_name,
- host_name_before_hash)
-
- # Add initiator to the host.
- self.restclient.ensure_initiator_added(self.xml_file_path,
- initiator_name,
- host_id)
- hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
-
- # Mapping lungroup and hostgroup to view.
- lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
- hostgroup_id,
- host_id,
- portgroup_id)
-
- hostlun_id = self.restclient.find_host_lun_id(host_id, lun_id)
-
- LOG.info(_LI("initialize_connection_iscsi, host lun id is: %s."),
- hostlun_id)
-
- iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
- chapinfo = self.restclient.find_chap_info(iscsi_conf,
- initiator_name)
- # Return iSCSI properties.
- properties = {}
- properties['target_discovered'] = False
- 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:
- chap_username, chap_password = chapinfo.split(';')
- properties['auth_method'] = 'CHAP'
- properties['auth_username'] = chap_username
- properties['auth_password'] = chap_password
-
- LOG.info(_LI("initialize_connection_iscsi success. Return data: %s."),
- properties)
- return {'driver_volume_type': 'iscsi', 'data': properties}
-
- @utils.synchronized('huawei', external=True)
- def terminate_connection_iscsi(self, volume, connector):
- """Delete map between a volume and a host."""
- initiator_name = connector['initiator']
- volume_name = huawei_utils.encode_name(volume['id'])
- lun_id = volume.get('provider_location', None)
- host_name = connector['host']
-
- LOG.info(_LI(
- 'terminate_connection_iscsi: volume name: %(volume)s, '
- 'initiator name: %(ini)s, '
- 'lun_id: %(lunid)s.'),
- {'volume': volume_name,
- 'ini': initiator_name,
- 'lunid': lun_id},)
-
- iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
- portgroup = None
- portgroup_id = None
- left_lunnum = -1
- for ini in iscsi_conf['Initiator']:
- if ini['Name'] == initiator_name:
- for key in ini:
- if key == 'TargetPortGroup':
- portgroup = ini['TargetPortGroup']
- break
- # Remove lun from lungroup.
- if lun_id:
- if self.restclient.check_lun_exist(lun_id):
- # Get lungroup id by lun id.
- lungroup_id = self.restclient.get_lungroupid_by_lunid(lun_id)
- if lungroup_id:
- self.restclient.remove_lun_from_lungroup(lungroup_id,
- lun_id)
- else:
- LOG.warning(_LW("Can't find lun on the array."))
- # Remove portgroup from mapping view if no lun left in lungroup.
- if portgroup:
- portgroup_id = self.restclient.find_tgt_port_group(portgroup)
- host_id = self.restclient.find_host(host_name)
- if host_id:
- mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
- view_id = self.restclient.find_mapping_view(mapping_view_name)
- if view_id:
- lungroup_id = self.restclient.find_lungroup_from_map(view_id)
- if lungroup_id:
- left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
-
- if portgroup_id and view_id and (int(left_lunnum) <= 0):
- if self.restclient.is_portgroup_associated_to_view(view_id,
- portgroup_id):
- self.restclient.delete_portgroup_mapping_view(view_id,
- portgroup_id)
- if view_id and (int(left_lunnum) <= 0):
- self.restclient.remove_chap(initiator_name)
-
- if self.restclient.lungroup_associated(view_id, lungroup_id):
- self.restclient.delete_lungroup_mapping_view(view_id,
- lungroup_id)
- self.restclient.delete_lungroup(lungroup_id)
- if self.restclient.is_initiator_associated_to_host(initiator_name):
- self.restclient.remove_iscsi_from_host(initiator_name)
- hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
- hostgroup_id = self.restclient.find_hostgroup(hostgroup_name)
- if hostgroup_id:
- if self.restclient.hostgroup_associated(view_id, hostgroup_id):
- self.restclient.delete_hostgoup_mapping_view(view_id,
- hostgroup_id)
- self.restclient.remove_host_from_hostgroup(hostgroup_id,
- host_id)
- self.restclient.delete_hostgroup(hostgroup_id)
- self.restclient.remove_host(host_id)
- self.restclient.delete_mapping_view(view_id)
-
def retype(self, ctxt, volume, new_type, diff, host):
"""Convert the volume to be of the new type."""
LOG.debug("Enter retype: id=%(id)s, new_type=%(new_type)s, "
qos[key.upper()] = value
return qos
- def terminate_connection_fc(self, volume, connector):
- """Delete map between a volume and a host."""
- wwns = connector['wwpns']
- volume_name = huawei_utils.encode_name(volume['id'])
- lun_id = volume.get('provider_location', None)
- host_name = connector['host']
- left_lunnum = -1
-
- LOG.info(_LI('terminate_connection_fc: volume name: %(volume)s, '
- 'wwpns: %(wwns)s, '
- 'lun_id: %(lunid)s.'),
- {'volume': volume_name,
- 'wwns': wwns,
- 'lunid': lun_id},)
- if lun_id:
- if self.restclient.check_lun_exist(lun_id):
- # Get lungroup id by lun id.
- lungroup_id = self.restclient.get_lungroupid_by_lunid(lun_id)
- if not lungroup_id:
- LOG.info(_LI("Can't find lun in lungroup."))
- else:
- self.restclient.remove_lun_from_lungroup(lungroup_id,
- lun_id)
- else:
- LOG.warning(_LW("Can't find lun on the array."))
- tgt_port_wwns = []
- for wwn in wwns:
- tgtwwpns = self.restclient.get_fc_target_wwpns(wwn)
- if tgtwwpns:
- tgt_port_wwns.append(tgtwwpns)
-
- init_targ_map = {}
- for initiator in wwns:
- init_targ_map[initiator] = tgt_port_wwns
- host_id = self.restclient.find_host(host_name)
- if host_id:
- mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
- view_id = self.restclient.find_mapping_view(mapping_view_name)
- if view_id:
- lungroup_id = self.restclient.find_lungroup_from_map(view_id)
- if lungroup_id:
- left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
- if int(left_lunnum) > 0:
- info = {'driver_volume_type': 'fibre_channel',
- 'data': {}}
- else:
- info = {'driver_volume_type': 'fibre_channel',
- 'data': {'target_wwn': tgt_port_wwns,
- 'initiator_target_map': init_targ_map}, }
-
- return info
-
def create_export(self, context, volume, connector):
"""Export a volume."""
pass
data['vendor_name'] = 'Huawei'
return data
+ @utils.synchronized('huawei', external=True)
def initialize_connection(self, volume, connector):
- return HuaweiBaseDriver.initialize_connection_iscsi(self,
- volume,
- connector)
+ """Map a volume to a host and return target iSCSI information."""
+ LOG.info(_LI('Enter initialize_connection.'))
+ initiator_name = connector['initiator']
+ volume_name = huawei_utils.encode_name(volume['id'])
+
+ LOG.info(_LI(
+ 'initiator name: %(initiator_name)s, '
+ 'volume name: %(volume)s.'),
+ {'initiator_name': initiator_name,
+ 'volume': volume_name})
+
+ (iscsi_iqns,
+ target_ips,
+ portgroup_id) = self.restclient.get_iscsi_params(self.xml_file_path,
+ connector)
+ LOG.info(_LI('initialize_connection, iscsi_iqn: %(iscsi_iqn)s, '
+ 'target_ip: %(target_ip)s, '
+ 'portgroup_id: %(portgroup_id)s.'),
+ {'iscsi_iqn': iscsi_iqns,
+ 'target_ip': target_ips,
+ 'portgroup_id': portgroup_id},)
+
+ # Create hostgroup if not exist.
+ host_name = connector['host']
+ host_name_before_hash = None
+ if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
+ host_name_before_hash = host_name
+ host_name = six.text_type(hash(host_name))
+ host_id = self.restclient.add_host_with_check(host_name,
+ host_name_before_hash)
+
+ # Add initiator to the host.
+ self.restclient.ensure_initiator_added(self.xml_file_path,
+ initiator_name,
+ host_id)
+ hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
+
+ # Mapping lungroup and hostgroup to view.
+ lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
+ hostgroup_id,
+ host_id,
+ portgroup_id)
+
+ hostlun_id = self.restclient.find_host_lun_id(host_id, lun_id)
+
+ LOG.info(_LI("initialize_connection, host lun id is: %s."),
+ hostlun_id)
+
+ iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
+ chapinfo = self.restclient.find_chap_info(iscsi_conf,
+ initiator_name)
+ # Return iSCSI properties.
+ properties = {}
+ properties['target_discovered'] = False
+ 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:
+ chap_username, chap_password = chapinfo.split(';')
+ properties['auth_method'] = 'CHAP'
+ properties['auth_username'] = chap_username
+ properties['auth_password'] = chap_password
+
+ LOG.info(_LI("initialize_connection success. Return data: %s."),
+ properties)
+ return {'driver_volume_type': 'iscsi', 'data': properties}
+ @utils.synchronized('huawei', external=True)
def terminate_connection(self, volume, connector, **kwargs):
- return HuaweiBaseDriver.terminate_connection_iscsi(self,
- volume,
- connector)
+ """Delete map between a volume and a host."""
+ initiator_name = connector['initiator']
+ volume_name = huawei_utils.encode_name(volume['id'])
+ lun_id = volume.get('provider_location', None)
+ host_name = connector['host']
+ lungroup_id = None
+
+ LOG.info(_LI(
+ 'terminate_connection: volume name: %(volume)s, '
+ 'initiator name: %(ini)s, '
+ 'lun_id: %(lunid)s.'),
+ {'volume': volume_name,
+ 'ini': initiator_name,
+ 'lunid': lun_id},)
+
+ iscsi_conf = huawei_utils.get_iscsi_conf(self.xml_file_path)
+ portgroup = None
+ portgroup_id = None
+ view_id = None
+ left_lunnum = -1
+ for ini in iscsi_conf['Initiator']:
+ if ini['Name'] == initiator_name:
+ for key in ini:
+ if key == 'TargetPortGroup':
+ portgroup = ini['TargetPortGroup']
+ break
+
+ if portgroup:
+ portgroup_id = self.restclient.find_tgt_port_group(portgroup)
+ if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
+ host_name = six.text_type(hash(host_name))
+ host_id = self.restclient.find_host(host_name)
+ if host_id:
+ mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
+ view_id = self.restclient.find_mapping_view(mapping_view_name)
+ if view_id:
+ lungroup_id = self.restclient.find_lungroup_from_map(view_id)
+
+ # Remove lun from lungroup.
+ if lun_id and self.restclient.check_lun_exist(lun_id):
+ if lungroup_id:
+ lungroup_ids = self.restclient.get_lungroupids_by_lunid(lun_id)
+ if lungroup_id in lungroup_ids:
+ self.restclient.remove_lun_from_lungroup(lungroup_id,
+ lun_id)
+ else:
+ LOG.warning(_LW("Lun is not in lungroup. "
+ "Lun id: %(lun_id)s. "
+ "lungroup id: %(lungroup_id)s."),
+ {"lun_id": lun_id,
+ "lungroup_id": lungroup_id})
+ else:
+ LOG.warning(_LW("Can't find lun on the array."))
+
+ # Remove portgroup from mapping view if no lun left in lungroup.
+ if lungroup_id:
+ left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
+
+ if portgroup_id and view_id and (int(left_lunnum) <= 0):
+ if self.restclient.is_portgroup_associated_to_view(view_id,
+ portgroup_id):
+ self.restclient.delete_portgroup_mapping_view(view_id,
+ portgroup_id)
+ if view_id and (int(left_lunnum) <= 0):
+ self.restclient.remove_chap(initiator_name)
+
+ if self.restclient.lungroup_associated(view_id, lungroup_id):
+ self.restclient.delete_lungroup_mapping_view(view_id,
+ lungroup_id)
+ self.restclient.delete_lungroup(lungroup_id)
+ if self.restclient.is_initiator_associated_to_host(initiator_name):
+ self.restclient.remove_iscsi_from_host(initiator_name)
+ hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
+ hostgroup_id = self.restclient.find_hostgroup(hostgroup_name)
+ if hostgroup_id:
+ if self.restclient.hostgroup_associated(view_id, hostgroup_id):
+ self.restclient.delete_hostgoup_mapping_view(view_id,
+ hostgroup_id)
+ self.restclient.remove_host_from_hostgroup(hostgroup_id,
+ host_id)
+ self.restclient.delete_hostgroup(hostgroup_id)
+ self.restclient.remove_host(host_id)
+ self.restclient.delete_mapping_view(view_id)
class Huawei18000FCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
SmartX support
Volume migration support
Volume retype support
+ FC zone enhancement
"""
VERSION = "1.1.1"
def __init__(self, *args, **kwargs):
super(Huawei18000FCDriver, self).__init__(*args, **kwargs)
+ self.fcsan_lookup_service = None
def get_volume_stats(self, refresh=False):
"""Get volume status."""
data['vendor_name'] = 'Huawei'
return data
+ @utils.synchronized('huawei', external=True)
@fczm_utils.AddFCZone
def initialize_connection(self, volume, connector):
- return HuaweiBaseDriver.initialize_connection_fc(self,
- volume,
- connector)
+ wwns = connector['wwpns']
+ volume_name = huawei_utils.encode_name(volume['id'])
+ LOG.info(_LI(
+ 'initialize_connection, initiator: %(wwpns)s,'
+ ' volume name: %(volume)s.'),
+ {'wwpns': wwns,
+ 'volume': volume_name},)
+
+ host_name_before_hash = None
+ host_name = connector['host']
+ if host_name and (len(host_name) > constants.MAX_HOSTNAME_LENGTH):
+ host_name_before_hash = host_name
+ host_name = six.text_type(hash(host_name))
+
+ if not self.fcsan_lookup_service:
+ self.fcsan_lookup_service = fczm_utils.create_lookup_service()
+
+ if self.fcsan_lookup_service:
+ # Use FC switch.
+ host_id = self.restclient.add_host_with_check(
+ host_name, host_name_before_hash)
+ zone_helper = fc_zone_helper.FCZoneHelper(
+ self.fcsan_lookup_service, self.restclient)
+ (tgt_port_wwns, init_targ_map) = (
+ zone_helper.build_ini_targ_map(wwns))
+ for ini in init_targ_map:
+ self.restclient.ensure_fc_initiator_added(ini, host_id)
+ else:
+ # Not use FC switch.
+ host_id = self.restclient.add_host_with_check(
+ host_name, host_name_before_hash)
+ online_wwns_in_host = (
+ self.restclient.get_host_online_fc_initiators(host_id))
+ online_free_wwns = self.restclient.get_online_free_wwns()
+ for wwn in wwns:
+ if (wwn not in online_wwns_in_host
+ and wwn not in online_free_wwns):
+ wwns_in_host = (
+ self.restclient.get_host_fc_initiators(host_id))
+ iqns_in_host = (
+ self.restclient.get_host_iscsi_initiators(host_id))
+ if not wwns_in_host and not iqns_in_host:
+ self.restclient.remove_host(host_id)
+
+ msg = (_('Can not add FC initiator to host.'))
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ for wwn in wwns:
+ if wwn in online_free_wwns:
+ self.restclient.add_fc_port_to_host(host_id, wwn)
+
+ (tgt_port_wwns, init_targ_map) = (
+ self.restclient.get_init_targ_map(wwns))
+
+ # Add host into hostgroup.
+ hostgroup_id = self.restclient.add_host_into_hostgroup(host_id)
+ lun_id = self.restclient.mapping_hostgroup_and_lungroup(volume_name,
+ hostgroup_id,
+ host_id)
+ host_lun_id = self.restclient.find_host_lun_id(host_id, lun_id)
+
+ # Return FC properties.
+ info = {'driver_volume_type': 'fibre_channel',
+ 'data': {'target_lun': int(host_lun_id),
+ 'target_discovered': True,
+ 'target_wwn': tgt_port_wwns,
+ 'volume_id': volume['id'],
+ 'initiator_target_map': init_targ_map}, }
+
+ LOG.info(_LI("initialize_connection, return data is: %s."),
+ info)
+
+ return info
+ @utils.synchronized('huawei', external=True)
@fczm_utils.RemoveFCZone
def terminate_connection(self, volume, connector, **kwargs):
- return HuaweiBaseDriver.terminate_connection_fc(self,
- volume,
- connector)
+ """Delete map between a volume and a host."""
+ wwns = connector['wwpns']
+ volume_name = huawei_utils.encode_name(volume['id'])
+ lun_id = volume.get('provider_location', None)
+ host_name = connector['host']
+ left_lunnum = -1
+ lungroup_id = None
+ view_id = None
+ LOG.info(_LI('terminate_connection: volume name: %(volume)s, '
+ 'wwpns: %(wwns)s, '
+ 'lun_id: %(lunid)s.'),
+ {'volume': volume_name,
+ 'wwns': wwns,
+ 'lunid': lun_id},)
+
+ if host_name and len(host_name) > constants.MAX_HOSTNAME_LENGTH:
+ host_name = six.text_type(hash(host_name))
+ host_id = self.restclient.find_host(host_name)
+ if host_id:
+ mapping_view_name = constants.MAPPING_VIEW_PREFIX + host_id
+ view_id = self.restclient.find_mapping_view(mapping_view_name)
+ if view_id:
+ lungroup_id = self.restclient.find_lungroup_from_map(view_id)
+
+ if lun_id and self.restclient.check_lun_exist(lun_id):
+ if lungroup_id:
+ lungroup_ids = self.restclient.get_lungroupids_by_lunid(lun_id)
+ if lungroup_id in lungroup_ids:
+ self.restclient.remove_lun_from_lungroup(lungroup_id,
+ lun_id)
+ else:
+ LOG.warning(_LW("Lun is not in lungroup. "
+ "Lun id: %(lun_id)s. "
+ "Lungroup id: %(lungroup_id)s."),
+ {"lun_id": lun_id,
+ "lungroup_id": lungroup_id})
+ else:
+ LOG.warning(_LW("Can't find lun on the array."))
+ if lungroup_id:
+ left_lunnum = self.restclient.get_lunnum_from_lungroup(lungroup_id)
+ if int(left_lunnum) > 0:
+ info = {'driver_volume_type': 'fibre_channel',
+ 'data': {}}
+ else:
+ if not self.fcsan_lookup_service:
+ self.fcsan_lookup_service = fczm_utils.create_lookup_service()
+
+ if self.fcsan_lookup_service:
+ zone_helper = fc_zone_helper.FCZoneHelper(
+ self.fcsan_lookup_service, self.restclient)
+
+ (tgt_port_wwns, init_targ_map) = (
+ zone_helper.build_ini_targ_map(wwns))
+ else:
+ (tgt_port_wwns, init_targ_map) = (
+ self.restclient.get_init_targ_map(wwns))
+
+ for wwn in wwns:
+ if self.restclient.is_fc_initiator_associated_to_host(wwn):
+ self.restclient.remove_fc_from_host(wwn)
+ if lungroup_id:
+ if view_id and self.restclient.lungroup_associated(
+ view_id, lungroup_id):
+ self.restclient.delete_lungroup_mapping_view(view_id,
+ lungroup_id)
+ self.restclient.delete_lungroup(lungroup_id)
+
+ if host_id:
+ hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
+ hostgroup_id = self.restclient.find_hostgroup(hostgroup_name)
+ if hostgroup_id:
+ if view_id and self.restclient.hostgroup_associated(
+ view_id, hostgroup_id):
+ self.restclient.delete_hostgoup_mapping_view(
+ view_id, hostgroup_id)
+ self.restclient.remove_host_from_hostgroup(
+ hostgroup_id, host_id)
+ self.restclient.delete_hostgroup(hostgroup_id)
+
+ if not self.restclient.check_fc_initiators_exist_in_host(
+ host_id):
+ self.restclient.remove_host(host_id)
+
+ if view_id:
+ self.restclient.delete_mapping_view(view_id)
+
+ info = {'driver_volume_type': 'fibre_channel',
+ 'data': {'target_wwn': tgt_port_wwns,
+ 'initiator_target_map': init_targ_map}}
+ LOG.info(_LI("terminate_connection, return data is: %s."),
+ info)
+
+ return info
if key:
key = key.lower()
- if ((not scope or scope == 'capabilities') and
- key in opts_capability):
+ if ((not scope or scope == 'capabilities')
+ and key in opts_capability):
+
words = value.split()
if not (words and len(words) == 2 and words[0] == '<is>'):
"capabilities:%s='<is> True' or "
"'<is> true'."), key)
else:
- # Get the second value(true/True) of the Extra specs
- # value(<is> true/<is> True)
opts[key] = words[1].lower()
if (scope in opts_capability) and (key in opts_value):
lunsetinfo['MirrorSwitch'] = mirrorswitch.strip()
prefetch = root.find('LUN/Prefetch')
- fetchtype = prefetch.attrib['Type']
if prefetch is not None and prefetch.attrib['Type']:
+ fetchtype = prefetch.attrib['Type']
if fetchtype in ['0', '1', '2', '3']:
lunsetinfo['PrefetchType'] = fetchtype.strip()
typevalue = prefetch.attrib['Value'].strip()
def get_login_info(xml_file_path):
"""Get login IP, user name and password from config file."""
- logininfo = {}
+ login_info = {}
root = parse_xml_file(xml_file_path)
- logininfo['RestURL'] = root.findtext('Storage/RestURL').strip()
+ login_info['RestURL'] = root.findtext('Storage/RestURL').strip()
for key in ['UserName', 'UserPassword']:
node = root.find('Storage/%s' % key)
node_text = node.text
- logininfo[key] = node_text
+ login_info[key] = node_text
- return logininfo
+ return login_info
def _change_file_mode(filepath):
raise exception.VolumeBackendAPIException(data=msg)
def _assert_rest_result(self, result, err_str):
- error_code = result['error']['code']
- if error_code != 0:
+ if result['error']['code'] != 0:
msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str,
'res': result})
LOG.error(msg)
return True
def delete_lun(self, lun_id):
- lun_group_id = self.get_lungroupid_by_lunid(lun_id)
- if lun_group_id:
- self.remove_lun_from_lungroup(lun_group_id, lun_id)
+ lun_group_ids = self.get_lungroupids_by_lunid(lun_id)
+ if lun_group_ids and len(lun_group_ids) == 1:
+ self.remove_lun_from_lungroup(lun_group_ids[0], lun_id)
url = self.url + "/lun/" + lun_id
data = json.dumps({"TYPE": "11",
If hostgroup doesn't exist, create one.
"""
hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
- hostgroup_id = self._create_hostgroup_with_check(hostgroup_name)
+ 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:
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check portgroup associate error.'))
- if self._get_id_from_result(result, view_id, 'ID'):
- return True
-
+ if 'data' in result:
+ for item in result['data']:
+ if view_id == item['ID']:
+ return True
return False
def mapping_hostgroup_and_lungroup(self, volume_name, hostgroup_id,
return self._get_id_from_result(result, lungroup_name, 'NAME')
- def _create_hostgroup_with_check(self, hostgroup_name):
+ def create_hostgroup_with_check(self, hostgroup_name):
"""Check if host exists on the array, or create it."""
hostgroup_id = self.find_hostgroup(hostgroup_name)
if hostgroup_id:
LOG.info(_LI(
- '_create_hostgroup_with_check. '
+ 'create_hostgroup_with_check. '
'hostgroup name: %(name)s, '
'hostgroup id: %(id)s'),
{'name': hostgroup_name,
raise exception.VolumeBackendAPIException(data=err_msg)
LOG.info(_LI(
- '_create_hostgroup_with_check. '
+ 'create_hostgroup_with_check. '
'Create hostgroup success. '
'hostgroup name: %(name)s, '
'hostgroup id: %(id)s'),
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check lungroup associate error.'))
- if self._get_id_from_result(result, view_id, 'ID'):
- return True
+ if 'data' in result:
+ for item in result['data']:
+ if view_id == item['ID']:
+ return True
return False
def hostgroup_associated(self, view_id, hostgroup_id):
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check hostgroup associate error.'))
- if self._get_id_from_result(result, view_id, 'ID'):
- return True
+ if 'data' in result:
+ for item in result['data']:
+ if view_id == item['ID']:
+ return True
return False
def find_host_lun_id(self, host_id, lun_id):
raise
return host_lun_id
- def find_host(self, hostname):
+ def find_host(self, host_name):
"""Get the given host ID."""
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.'))
- return self._get_id_from_result(result, hostname, 'NAME')
+ return self._get_id_from_result(result, host_name, 'NAME')
def add_host_with_check(self, host_name, host_name_before_hash):
host_id = self.find_host(host_name)
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Check hostgroup associate error.'))
- if self._get_id_from_result(result, host_id, 'ID'):
- return True
+ if 'data' in result:
+ for item in result['data']:
+ if host_id == item['ID']:
+ return True
+
return False
def _is_lun_associated_to_lungroup(self, lungroup_id, lun_id):
def _get_capacity(self, pool_name, result):
"""Get free capacity and total capacity of the pool."""
- poolinfo = self.find_pool_info(pool_name, result)
+ pool_info = self.find_pool_info(pool_name, result)
pool_capacity = {'total_capacity': 0.0,
'free_capacity': 0.0}
- if poolinfo:
- total = int(poolinfo['TOTALCAPACITY']) / constants.CAPACITY_UNIT
- free = int(poolinfo['CAPACITY']) / constants.CAPACITY_UNIT
+ if pool_info:
+ total = float(pool_info['TOTALCAPACITY']) / constants.CAPACITY_UNIT
+ free = float(pool_info['CAPACITY']) / constants.CAPACITY_UNIT
pool_capacity['total_capacity'] = total
pool_capacity['free_capacity'] = free
result = self.call(url, None, "DELETE")
self._assert_rest_result(result, _('Delete LUNcopy error.'))
- def get_connected_free_wwns(self):
- """Get free connected FC port WWNs.
+ def get_init_targ_map(self, wwns):
+ init_targ_map = {}
+ tgt_port_wwns = []
+ for wwn in wwns:
+ tgtwwpns = self.get_fc_target_wwpns(wwn)
+ if not tgtwwpns:
+ continue
+
+ init_targ_map[wwn] = tgtwwpns
+ for tgtwwpn in tgtwwpns:
+ if tgtwwpn not in tgt_port_wwns:
+ tgt_port_wwns.append(tgtwwpn)
+
+ return (tgt_port_wwns, init_targ_map)
+
+ def get_online_free_wwns(self):
+ """Get online free WWNs.
If no new ports connected, return an empty list.
"""
msg = _('Get FC target wwpn error.')
self._assert_rest_result(result, msg)
- fc_wwpns = None
- if 'data' in result:
+ fc_wwpns = []
+ if "data" in result:
for item in result['data']:
if wwn == item['INITIATOR_PORT_WWN']:
- fc_wwpns = item['TARGET_PORT_WWN']
- break
+ fc_wwpns.append(item['TARGET_PORT_WWN'])
return fc_wwpns
return result['data']['IOCLASSID']
- def get_lungroupid_by_lunid(self, lun_id):
- """Get lungroup id by lun id."""
+ def get_lungroupids_by_lunid(self, lun_id):
+ """Get lungroup ids by lun id."""
+
url = self.url + ("/lungroup/associate?TYPE=256"
"&ASSOCIATEOBJTYPE=11&ASSOCIATEOBJID=%s" % lun_id)
result = self.call(url, None, "GET")
self._assert_rest_result(result, _('Get lungroup id by lun id error.'))
- lun_group_id = None
- # Lun only in one lungroup.
+ lungroup_ids = []
if 'data' in result:
for item in result['data']:
- lun_group_id = item['ID']
+ lungroup_ids.append(item['ID'])
- return lun_group_id
+ return lungroup_ids
def get_lun_info(self, lun_id):
url = self.url + "/lun/" + lun_id
if 'data' in result:
for item in result['data']:
- LOG.debug('get_partition_id_by_name item %(item)s.',
- {'item': item})
if name == item['NAME']:
return item['ID']
result = self.call(url, data, "PUT")
self._assert_rest_result(result, _('Remove iscsi from host error.'))
+ def get_host_online_fc_initiators(self, host_id):
+ url = self.url + "/fc_initiator"
+ data = json.dumps({'PARENTTYPE': 21,
+ 'PARENTID': host_id})
+ result = self.call(url, data, "GET")
+
+ initiators = []
+ if 'data' in result:
+ for item in result['data']:
+ if (('PARENTID' in item) and (item['PARENTID'] == host_id)
+ and (item['RUNNINGSTATUS'] == constants.FC_INIT_ONLINE)):
+ initiators.append(item['ID'])
+
+ return initiators
+
+ def get_host_fc_initiators(self, host_id):
+ url = self.url + "/fc_initiator"
+ data = json.dumps({'PARENTTYPE': 21,
+ 'PARENTID': host_id})
+ result = self.call(url, data, "GET")
+
+ initiators = []
+ if 'data' in result:
+ for item in result['data']:
+ if (('PARENTID' in item) and (item['PARENTID'] == host_id)):
+ initiators.append(item['ID'])
+
+ return initiators
+
+ def get_host_iscsi_initiators(self, host_id):
+ url = self.url + "/iscsi_initiator"
+ data = json.dumps({'PARENTTYPE': 21,
+ 'PARENTID': host_id})
+ result = self.call(url, data, "GET")
+
+ initiators = []
+ if 'data' in result:
+ for item in result['data']:
+ if (('PARENTID' in item) and (item['PARENTID'] == host_id)):
+ initiators.append(item['ID'])
+
+ return initiators
+
def rename_lun(self, lun_id, new_name):
url = self.url + "/lun/" + lun_id
data = json.dumps({"NAME": new_name})
msg = _('Rename lun on array error.')
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
+
+ def is_fc_initiator_associated_to_host(self, ininame):
+ """Check whether the initiator is associated to the host."""
+ url = self.url + '/fc_initiator?range=[0-256]'
+ result = self.call(url, None, "GET")
+ self._assert_rest_result(result,
+ 'Check initiator associated to host error.')
+
+ if "data" in result:
+ for item in result['data']:
+ if item['ID'] == ininame and item['ISFREE'] != "true":
+ return True
+ return False
+
+ def remove_fc_from_host(self, initiator):
+ url = self.url + '/fc_initiator/remove_fc_from_host'
+ data = json.dumps({"TYPE": '223',
+ "ID": initiator})
+ result = self.call(url, data, "PUT")
+ self._assert_rest_result(result, _('Remove fc from host error.'))
+
+ def check_fc_initiators_exist_in_host(self, host_id):
+ url = self.url + '/fc_initiator?range=[0-256]'
+ data = json.dumps({"PARENTID": host_id})
+ result = self.call(url, data, "GET")
+ self._assert_rest_result(result, _('Get host initiators info failed.'))
+ if 'data' in result:
+ return True
+
+ return False
+
+ def _fc_initiator_is_added_to_array(self, ininame):
+ """Check whether the fc initiator is already added on the array."""
+ url = self.url + '/fc_initiator/' + ininame
+ data = json.dumps({"TYPE": '223',
+ "ID": ininame})
+ result = self.call(url, data, "GET")
+ error_code = result['error']['code']
+ if error_code != 0:
+ return False
+
+ return True
+
+ def _add_fc_initiator_to_array(self, ininame):
+ """Add a fc initiator to storage device."""
+ url = self.url + '/fc_initiator/'
+ data = json.dumps({"TYPE": '223',
+ "ID": ininame})
+ result = self.call(url, data)
+ self._assert_rest_result(result, _('Add fc initiator to array error.'))
+
+ def ensure_fc_initiator_added(self, initiator_name, host_id):
+ added = self._fc_initiator_is_added_to_array(initiator_name)
+ if not added:
+ self._add_fc_initiator_to_array(initiator_name)
+ # Just add, no need to check whether have been added.
+ self.add_fc_port_to_host(host_id, initiator_name)
+
+ def get_fc_ports_on_array(self):
+ url = self.url + '/fc_port'
+ result = self.call(url, None, "GET")
+ msg = _('Get FC ports from array error.')
+ self._assert_rest_result(result, msg)
+
+ return result['data']
+
+ def get_fc_ports_from_contr(self, contr):
+ port_list_from_contr = []
+ location = []
+ data = self.get_fc_ports_on_array()
+ for item in data:
+ location = item['PARENTID'].split('.')
+ if (location[0][1] == contr) and (item['RUNNINGSTATUS'] ==
+ constants.FC_PORT_CONNECTED):
+ port_list_from_contr.append(item['WWN'])
+ return port_list_from_contr