Merge "Huawei: Balanced FC port selection when zoning"
This commit is contained in:
commit
55744deafb
@ -1008,7 +1008,16 @@ FAKE_GET_FC_PORT_RESPONSE = """
|
||||
"data":[{
|
||||
"RUNNINGSTATUS":"10",
|
||||
"WWN":"2000643e8c4c5f66",
|
||||
"PARENTID":"0A.1"
|
||||
"PARENTID":"0A.1",
|
||||
"ID": "1114368",
|
||||
"RUNSPEED": "16000"
|
||||
},
|
||||
{
|
||||
"RUNNINGSTATUS":"10",
|
||||
"WWN":"2009643e8c4c5f67",
|
||||
"PARENTID":"0A.1",
|
||||
"ID": "1114369",
|
||||
"RUNSPEED": "16000"
|
||||
}]
|
||||
}
|
||||
"""
|
||||
@ -1391,6 +1400,190 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/HyperMetroPair?range=[0-100]/GET'] = (
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/splitmirror?range=[0-100]/GET'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
FACK_GET_PORTG_BY_VIEW = """
|
||||
{
|
||||
"data": [{
|
||||
"DESCRIPTION": "Please do NOT modify this. Engine ID: 0",
|
||||
"ID": "0",
|
||||
"NAME": "OpenStack_PortGroup_1",
|
||||
"TYPE": 257
|
||||
}],
|
||||
"error": {
|
||||
"code": 0
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/portgroup/associate/mappingview?TYPE=257&AS'
|
||||
'SOCIATEOBJTYPE=245&ASSOCIATEOBJID=1/GET'] = (
|
||||
FACK_GET_PORTG_BY_VIEW)
|
||||
|
||||
FACK_GET_PORT_BY_PORTG = """
|
||||
{
|
||||
"data":[{
|
||||
"CONFSPEED":"0","FCCONFMODE":"3",
|
||||
"FCRUNMODE":"0","HEALTHSTATUS":"1","ID":"2000643e8c4c5f66",
|
||||
"MAXSUPPORTSPEED":"16000","NAME":"P0","PARENTID":"0B.1",
|
||||
"PARENTTYPE":209,"RUNNINGSTATUS":"10","RUNSPEED":"8000",
|
||||
"WWN":"2000643e8c4c5f66"
|
||||
}],
|
||||
"error":{
|
||||
"code":0,"description":"0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/fc_port/associate/portgroup?TYPE=212&ASSOCI'
|
||||
'ATEOBJTYPE=257&ASSOCIATEOBJID=0/GET'] = (
|
||||
FACK_GET_PORT_BY_PORTG)
|
||||
|
||||
FACK_GET_PORTG = """
|
||||
{
|
||||
"data": {
|
||||
"TYPE": 257,
|
||||
"NAME": "OpenStack_PortGroup_1",
|
||||
"DESCRIPTION": "Please DO NOT change thefollowing message: 0",
|
||||
"ID": "0"
|
||||
},
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/portgroup/0/GET'] = FACK_GET_PORTG
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/portgroup/0/PUT'] = FACK_GET_PORTG
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/port/associate/portgroup/POST'] = (
|
||||
FACK_GET_PORT_BY_PORTG)
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/port/associate/portgroup?ID=0&TYPE=257&ASSOCIA'
|
||||
'TEOBJTYPE=212&ASSOCIATEOBJID=2000643e8c4c5f66/DE'
|
||||
'LETE'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
FAKE_CREATE_PORTG = """
|
||||
{
|
||||
"data": {
|
||||
"DESCRIPTION": "Please DO NOT change the following message: 0",
|
||||
"ID": "0",
|
||||
"NAME": "OpenStack_PortGroup_1",
|
||||
"TYPE": 257
|
||||
},
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/PortGroup/POST'] = FAKE_CREATE_PORTG
|
||||
|
||||
|
||||
FAKE_GET_PORTG_FROM_PORT = """
|
||||
{
|
||||
"data": [{
|
||||
"TYPE": 257,
|
||||
"NAME": "OpenStack_PortGroup_1",
|
||||
"DESCRIPTION": "PleaseDONOTchangethefollowingmessage: 0",
|
||||
"ID": "0"
|
||||
}],
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/portgroup/associate/fc_port?TYPE=257&ASSOCIA'
|
||||
'TEOBJTYPE=212&ASSOCIATEOBJID=1114368/GET'] = (
|
||||
FAKE_GET_PORTG_FROM_PORT)
|
||||
|
||||
FACK_GET_VIEW_BY_PORTG = """
|
||||
{
|
||||
"data": [{
|
||||
"ASSOCIATEOBJID": "0",
|
||||
"COUNT": "0",
|
||||
"ASSOCIATEOBJTYPE": "0",
|
||||
"INBANDLUNWWN": "",
|
||||
"FORFILESYSTEM": "false",
|
||||
"ID": "2",
|
||||
"ENABLEINBANDCOMMAND": "false",
|
||||
"NAME": "OpenStack_Mapping_View_1",
|
||||
"WORKMODE": "0",
|
||||
"TYPE": 245,
|
||||
"HOSTLUNID": "0",
|
||||
"DESCRIPTION": ""
|
||||
}],
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/mappingview/associate/portgroup?TYPE=245&ASS'
|
||||
'OCIATEOBJTYPE=257&ASSOCIATEOBJID=0/GET'] = (
|
||||
FACK_GET_VIEW_BY_PORTG)
|
||||
|
||||
FACK_GET_LUNG_BY_VIEW = """
|
||||
{
|
||||
"data": [{
|
||||
"TYPE": 256,
|
||||
"NAME": "OpenStack_LunGroup_1",
|
||||
"DESCRIPTION": "OpenStack_LunGroup_1",
|
||||
"ID": "1"
|
||||
}],
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lungroup/associate/mappingview?TYPE=256&ASSO'
|
||||
'CIATEOBJTYPE=245&ASSOCIATEOBJID=2/GET'] = (
|
||||
FACK_GET_LUNG_BY_VIEW)
|
||||
|
||||
FAKE_LUN_COUNT_RESPONSE_1 = """
|
||||
{
|
||||
"data":{
|
||||
"COUNT":"2"
|
||||
},
|
||||
"error":{
|
||||
"code":0,
|
||||
"description":"0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/lun/count?TYPE=11&ASSOCIATEOB'
|
||||
'JTYPE=256&ASSOCIATEOBJID=1/GET'] = (
|
||||
FAKE_LUN_COUNT_RESPONSE_1)
|
||||
|
||||
FAKE_PORTS_IN_PG_RESPONSE = """
|
||||
{
|
||||
"data": [{
|
||||
"ID": "1114114",
|
||||
"WWN": "2002643e8c4c5f66"
|
||||
},
|
||||
{
|
||||
"ID": "1114113",
|
||||
"WWN": "2001643e8c4c5f66"
|
||||
}],
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/fc_port/associate?TYPE=213&ASSOCIATEOBJTYPE='
|
||||
'257&ASSOCIATEOBJID=0/GET'] = (
|
||||
FAKE_PORTS_IN_PG_RESPONSE)
|
||||
|
||||
|
||||
def Fake_sleep(time):
|
||||
pass
|
||||
@ -1530,7 +1723,7 @@ class FakeFCStorage(huawei_driver.HuaweiFCDriver):
|
||||
|
||||
def __init__(self, configuration):
|
||||
self.configuration = configuration
|
||||
self.fcsan_lookup_service = None
|
||||
self.fcsan = None
|
||||
self.db = FakeDB()
|
||||
self.huawei_conf = FakeHuaweiConf(self.configuration, 'iSCSI')
|
||||
|
||||
@ -2389,7 +2582,7 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
def test_get_volume_status(self):
|
||||
|
||||
data = self.driver.get_volume_stats()
|
||||
self.assertEqual('2.0.3', data['driver_version'])
|
||||
self.assertEqual('2.0.4', data['driver_version'])
|
||||
|
||||
def test_extend_volume(self):
|
||||
|
||||
@ -2614,37 +2807,60 @@ class HuaweiFCDriverTestCase(test.TestCase):
|
||||
test_new_type, None, test_host)
|
||||
self.assertFalse(retype)
|
||||
|
||||
def test_build_ini_targ_map(self):
|
||||
|
||||
@mock.patch.object(rest_client.RestClient, 'get_all_engines',
|
||||
return_value=[{'NODELIST': '["0A","0B"]', 'ID': '0'}])
|
||||
def test_build_ini_targ_map_engie_recorded(self, mock_engines):
|
||||
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.client)
|
||||
(tgt_port_wwns,
|
||||
init_targ_map) = (zone_helper.build_ini_targ_map(
|
||||
['10000090fa0d6754']))
|
||||
(tgt_wwns, portg_id, init_targ_map) = zone_helper.build_ini_targ_map(
|
||||
['10000090fa0d6754'], '1', '11')
|
||||
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)
|
||||
self.assertEqual(target_port_wwns, tgt_wwns)
|
||||
self.assertEqual({}, init_targ_map)
|
||||
|
||||
def test_filter_port_by_contr(self):
|
||||
@mock.patch.object(rest_client.RestClient, 'get_all_engines',
|
||||
return_value=[{'NODELIST': '["0A"]', 'ID': '0'},
|
||||
{'NODELIST': '["0B"]', 'ID': '1'}])
|
||||
def test_build_ini_targ_map_engie_not_recorded(self, mock_engines):
|
||||
fake_lookup_service = FCSanLookupService()
|
||||
|
||||
# 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)
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(
|
||||
fake_lookup_service, self.driver.client)
|
||||
(tgt_wwns, portg_id, init_targ_map) = zone_helper.build_ini_targ_map(
|
||||
['10000090fa0d6754'], '1', '11')
|
||||
expected_wwns = ['2000643e8c4c5f66']
|
||||
expected_map = {'10000090fa0d6754': ['2000643e8c4c5f66']}
|
||||
self.assertEqual(expected_wwns, tgt_wwns)
|
||||
self.assertEqual(expected_map, init_targ_map)
|
||||
|
||||
@mock.patch.object(rest_client.RestClient, 'get_all_engines',
|
||||
return_value=[{'NODELIST': '["0A", "0B"]', 'ID': '0'}])
|
||||
def test_build_ini_targ_map_no_map(self, mock_engines):
|
||||
fake_lookup_service = FCSanLookupService()
|
||||
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(
|
||||
fake_lookup_service, self.driver.client)
|
||||
# Host with id '5' has no map on the array.
|
||||
(tgt_wwns, portg_id, init_targ_map) = zone_helper.build_ini_targ_map(
|
||||
['10000090fa0d6754'], '5', '11')
|
||||
expected_wwns = ['2000643e8c4c5f66']
|
||||
expected_map = {'10000090fa0d6754': ['2000643e8c4c5f66']}
|
||||
self.assertEqual(expected_wwns, tgt_wwns)
|
||||
self.assertEqual(expected_map, init_targ_map)
|
||||
|
||||
def test_get_init_targ_map(self):
|
||||
fake_lookup_service = FCSanLookupService()
|
||||
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(
|
||||
fake_lookup_service, self.driver.client)
|
||||
(tgt_wwns, portg_id, init_targ_map) = zone_helper.get_init_targ_map(
|
||||
['10000090fa0d6754'], '1')
|
||||
expected_wwns = ['2000643e8c4c5f66']
|
||||
expected_map = {'10000090fa0d6754': ['2000643e8c4c5f66']}
|
||||
self.assertEqual(expected_wwns, tgt_wwns)
|
||||
self.assertEqual(expected_map, init_targ_map)
|
||||
|
||||
def test_multi_resturls_success(self):
|
||||
|
||||
|
@ -26,10 +26,13 @@ FILE_SYSTEM_POOL_TYPE = '2'
|
||||
HOSTGROUP_PREFIX = 'OpenStack_HostGroup_'
|
||||
LUNGROUP_PREFIX = 'OpenStack_LunGroup_'
|
||||
MAPPING_VIEW_PREFIX = 'OpenStack_Mapping_View_'
|
||||
PORTGROUP_PREFIX = 'OpenStack_PortGroup_'
|
||||
QOS_NAME_PREFIX = 'OpenStack_'
|
||||
PORTGROUP_DESCRIP_PREFIX = "Please do NOT modify this. Engine ID: "
|
||||
ARRAY_VERSION = 'V300R003C00'
|
||||
FC_PORT_CONNECTED = '10'
|
||||
FC_INIT_ONLINE = '27'
|
||||
FC_PORT_MODE_FABRIC = '0'
|
||||
CAPACITY_UNIT = 1024.0 * 1024.0 * 2
|
||||
DEFAULT_WAIT_TIMEOUT = 3600 * 24 * 30
|
||||
DEFAULT_WAIT_INTERVAL = 5
|
||||
@ -54,6 +57,7 @@ THICK_LUNTYPE = 0
|
||||
THIN_LUNTYPE = 1
|
||||
MAX_HOSTNAME_LENGTH = 31
|
||||
MAX_VOL_DESCRIPTION = 170
|
||||
PORT_NUM_PER_CONTR = 2
|
||||
|
||||
OS_TYPE = {'Linux': '0',
|
||||
'Windows': '1',
|
||||
|
@ -13,8 +13,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder.volume.drivers.huawei import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -24,46 +28,205 @@ class FCZoneHelper(object):
|
||||
"""FC zone helper for Huawei driver."""
|
||||
|
||||
def __init__(self, fcsan_lookup_service, client):
|
||||
self.fcsan_lookup_service = fcsan_lookup_service
|
||||
self.fcsan = fcsan_lookup_service
|
||||
self.client = client
|
||||
|
||||
def _get_fc_port_contr_map(self):
|
||||
port_list = []
|
||||
port_contr_map = {}
|
||||
def _get_fc_ports_info(self):
|
||||
ports_info = {}
|
||||
data = self.client.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
|
||||
port_info = {}
|
||||
port_info['id'] = item['ID']
|
||||
port_info['contr'] = location[0]
|
||||
port_info['bandwidth'] = item['RUNSPEED']
|
||||
ports_info[item['WWN']] = port_info
|
||||
return ports_info
|
||||
|
||||
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 _count_port_weight(self, port, ports_info):
|
||||
LOG.debug("Count weight for port: %s.", port)
|
||||
portgs = self.client.get_portgs_by_portid(ports_info[port]['id'])
|
||||
LOG.debug("Port %(port)s belongs to PortGroup %(portgs)s.",
|
||||
{"port": port, "portgs": portgs})
|
||||
weight = 0
|
||||
for portg in portgs:
|
||||
views = self.client.get_views_by_portg(portg)
|
||||
if not views:
|
||||
LOG.debug("PortGroup %s doesn't belong to any view.", portg)
|
||||
break
|
||||
|
||||
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))
|
||||
LOG.debug("PortGroup %(portg)s belongs to view %(views)s.",
|
||||
{"portg": portg, "views": views[0]})
|
||||
# In fact, there is just one view for one port group.
|
||||
lungroup = self.client.get_lungroup_by_view(views[0])
|
||||
lun_num = self.client.get_lunnum_from_lungroup(lungroup)
|
||||
ports_in_portg = self.client.get_ports_by_portg(portg)
|
||||
LOG.debug("PortGroup %(portg)s contains ports: %(ports)s.",
|
||||
{"portg": portg, "ports": ports_in_portg})
|
||||
total_bandwidth = 0
|
||||
for port_pg in ports_in_portg:
|
||||
if port_pg in ports_info:
|
||||
total_bandwidth += int(ports_info[port_pg]['bandwidth'])
|
||||
|
||||
LOG.debug("Total bandwidth for PortGroup %(portg)s is %(bindw)s.",
|
||||
{"portg": portg, "bindw": total_bandwidth})
|
||||
|
||||
if total_bandwidth:
|
||||
weight += float(lun_num) / float(total_bandwidth)
|
||||
|
||||
return weight
|
||||
|
||||
def _get_weighted_ports_per_contr(self, ports, ports_info):
|
||||
port_weight_map = {}
|
||||
for port in ports:
|
||||
port_weight_map[port] = self._count_port_weight(port, ports_info)
|
||||
|
||||
sorted_ports = sorted(port_weight_map.items(), key=lambda d: d[1])
|
||||
weighted_ports = []
|
||||
count = 0
|
||||
for port in sorted_ports:
|
||||
if count >= constants.PORT_NUM_PER_CONTR:
|
||||
break
|
||||
weighted_ports.append(port[0])
|
||||
count += 1
|
||||
return weighted_ports
|
||||
|
||||
def _get_weighted_ports(self, contr_port_map, ports_info, contrs):
|
||||
weighted_ports = []
|
||||
for contr in contrs:
|
||||
if contr in contr_port_map:
|
||||
weighted_ports_per_contr = self._get_weighted_ports_per_contr(
|
||||
contr_port_map[contr], ports_info)
|
||||
LOG.debug("Selected ports %(ports)s on controller %(contr)s.",
|
||||
{"ports": weighted_ports_per_contr,
|
||||
"contr": contr})
|
||||
weighted_ports.extend(weighted_ports_per_contr)
|
||||
return weighted_ports
|
||||
|
||||
def _filter_by_fabric(self, wwns, ports):
|
||||
"""Filter FC ports and initiators connected to fabrics."""
|
||||
ini_tgt_map = self.fcsan.get_device_mapping_from_network(wwns, ports)
|
||||
fabric_connected_ports = []
|
||||
fabric_connected_initiators = []
|
||||
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
|
||||
fabric_connected_ports.extend(
|
||||
ini_tgt_map[fabric]['target_port_wwn_list'])
|
||||
fabric_connected_initiators.extend(
|
||||
ini_tgt_map[fabric]['initiator_port_wwn_list'])
|
||||
|
||||
return (list(set(tgt_wwns)), init_targ_map)
|
||||
if not fabric_connected_ports:
|
||||
msg = _("No FC port connected to fabric.")
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
if not fabric_connected_initiators:
|
||||
msg = _("No initiator connected to fabric.")
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
LOG.debug("Fabric connected ports: %(ports)s, "
|
||||
"Fabric connected initiators: %(initiators)s.",
|
||||
{'ports': fabric_connected_ports,
|
||||
'initiators': fabric_connected_initiators})
|
||||
return fabric_connected_ports, fabric_connected_initiators
|
||||
|
||||
def build_ini_targ_map(self, wwns, host_id, lun_id):
|
||||
lun_info = self.client.get_lun_info(lun_id)
|
||||
lun_contr_id = lun_info['OWNINGCONTROLLER']
|
||||
engines = self.client.get_all_engines()
|
||||
|
||||
for engine in engines:
|
||||
contrs = json.loads(engine['NODELIST'])
|
||||
engine_id = engine['ID']
|
||||
if lun_contr_id in contrs:
|
||||
LOG.debug("LUN %(lun_id)s belongs to engine %(engine_id)s.",
|
||||
{"lun_id": lun_id, "engine_id": engine_id})
|
||||
break
|
||||
|
||||
# Check if there is already a port group in the view.
|
||||
# If yes and have already considered the engine,
|
||||
# we won't change anything about the port group and zone.
|
||||
view_name = constants.MAPPING_VIEW_PREFIX + host_id
|
||||
portg_name = constants.PORTGROUP_PREFIX + host_id
|
||||
view_id = self.client.find_mapping_view(view_name)
|
||||
portg_info = self.client.get_portgroup_by_view(view_id)
|
||||
portg_id = portg_info[0]['ID'] if portg_info else None
|
||||
|
||||
init_targ_map = {}
|
||||
if portg_id:
|
||||
description = portg_info[0].get("DESCRIPTION", '')
|
||||
engines = description.replace(constants.PORTGROUP_DESCRIP_PREFIX,
|
||||
"")
|
||||
engines = engines.split(',')
|
||||
ports = self.client.get_fc_ports_by_portgroup(portg_id)
|
||||
if engine_id in engines:
|
||||
LOG.debug("Have already selected ports for engine %s, just "
|
||||
"use them.", engine_id)
|
||||
return (list(ports.keys()), portg_id, init_targ_map)
|
||||
|
||||
# Filter initiators and ports that connected to fabrics.
|
||||
ports_info = self._get_fc_ports_info()
|
||||
(fabric_connected_ports, fabric_connected_initiators) = (
|
||||
self._filter_by_fabric(wwns, ports_info.keys()))
|
||||
|
||||
# Build a controller->ports map for convenience.
|
||||
contr_port_map = {}
|
||||
for port in fabric_connected_ports:
|
||||
contr = ports_info[port]['contr']
|
||||
if not contr_port_map.get(contr):
|
||||
contr_port_map[contr] = []
|
||||
contr_port_map[contr].append(port)
|
||||
LOG.debug("Controller port map: %s.", contr_port_map)
|
||||
|
||||
# Get the 'best' ports for the given controllers.
|
||||
weighted_ports = self._get_weighted_ports(contr_port_map, ports_info,
|
||||
contrs)
|
||||
|
||||
# Handle port group.
|
||||
port_list = [ports_info[port]['id'] for port in weighted_ports]
|
||||
|
||||
if portg_id:
|
||||
# Add engine ID to the description of the port group.
|
||||
self.client.append_portg_desc(portg_id, engine_id)
|
||||
# Extend the weighted_ports to include the ports already in the
|
||||
# port group.
|
||||
weighted_ports.extend(list(ports.keys()))
|
||||
else:
|
||||
portg_id = self.client.get_tgt_port_group(portg_name)
|
||||
if portg_id:
|
||||
LOG.debug("Found port group %s not belonged to any view, "
|
||||
"deleting it.", portg_name)
|
||||
ports = self.client.get_fc_ports_by_portgroup(portg_id)
|
||||
for port_id in ports.values():
|
||||
self.client.remove_port_from_portgroup(portg_id, port_id)
|
||||
self.client.delete_portgroup(portg_id)
|
||||
description = constants.PORTGROUP_DESCRIP_PREFIX + engine_id
|
||||
portg_id = self.client.create_portg(portg_name, description)
|
||||
|
||||
for port in port_list:
|
||||
self.client.add_port_to_portg(portg_id, port)
|
||||
|
||||
for ini in fabric_connected_initiators:
|
||||
init_targ_map[ini] = weighted_ports
|
||||
LOG.debug("build_ini_targ_map: Port group name: %(portg_name)s, "
|
||||
"init_targ_map: %(map)s.",
|
||||
{"portg_name": portg_name,
|
||||
"map": init_targ_map})
|
||||
return weighted_ports, portg_id, init_targ_map
|
||||
|
||||
def get_init_targ_map(self, wwns, host_id):
|
||||
error_ret = ([], None, {})
|
||||
if not host_id:
|
||||
return error_ret
|
||||
|
||||
view_name = constants.MAPPING_VIEW_PREFIX + host_id
|
||||
view_id = self.client.find_mapping_view(view_name)
|
||||
if not view_id:
|
||||
return error_ret
|
||||
port_group = self.client.get_portgroup_by_view(view_id)
|
||||
portg_id = port_group[0]['ID'] if port_group else None
|
||||
ports = self.client.get_fc_ports_by_portgroup(portg_id)
|
||||
for port_id in ports.values():
|
||||
self.client.remove_port_from_portgroup(portg_id, port_id)
|
||||
init_targ_map = {}
|
||||
for wwn in wwns:
|
||||
init_targ_map[wwn] = list(ports.keys())
|
||||
return list(ports.keys()), portg_id, init_targ_map
|
||||
|
@ -1451,13 +1451,14 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
2.0.1 - Manage/unmanage volume support
|
||||
2.0.2 - Refactor HuaweiFCDriver
|
||||
2.0.3 - Manage/unmanage snapshot support
|
||||
2.0.4 - Balanced FC port selection
|
||||
"""
|
||||
|
||||
VERSION = "2.0.3"
|
||||
VERSION = "2.0.4"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HuaweiFCDriver, self).__init__(*args, **kwargs)
|
||||
self.fcsan_lookup_service = None
|
||||
self.fcsan = None
|
||||
|
||||
def get_volume_stats(self, refresh=False):
|
||||
"""Get volume status."""
|
||||
@ -1481,23 +1482,23 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
'volume': volume_name},)
|
||||
|
||||
lun_id = self.client.get_lun_id(volume, volume_name)
|
||||
portg_id = None
|
||||
|
||||
original_host_name = connector['host']
|
||||
host_name = huawei_utils.encode_host_name(original_host_name)
|
||||
host_id = self.client.add_host_with_check(host_name,
|
||||
original_host_name)
|
||||
|
||||
if not self.fcsan_lookup_service:
|
||||
self.fcsan_lookup_service = fczm_utils.create_lookup_service()
|
||||
if not self.fcsan:
|
||||
self.fcsan = fczm_utils.create_lookup_service()
|
||||
|
||||
if self.fcsan_lookup_service:
|
||||
if self.fcsan:
|
||||
# Use FC switch.
|
||||
host_id = self.client.add_host_with_check(
|
||||
host_name, original_host_name)
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(
|
||||
self.fcsan_lookup_service, self.client)
|
||||
(tgt_port_wwns, init_targ_map) = (
|
||||
zone_helper.build_ini_targ_map(wwns))
|
||||
host_id = self.client.add_host_with_check(host_name,
|
||||
original_host_name)
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(self.fcsan, self.client)
|
||||
(tgt_port_wwns, portg_id, init_targ_map) = (
|
||||
zone_helper.build_ini_targ_map(wwns, host_id, lun_id))
|
||||
for ini in init_targ_map:
|
||||
self.client.ensure_fc_initiator_added(ini, host_id)
|
||||
else:
|
||||
@ -1530,9 +1531,8 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
|
||||
# Add host into hostgroup.
|
||||
hostgroup_id = self.client.add_host_to_hostgroup(host_id)
|
||||
map_info = self.client.do_mapping(lun_id,
|
||||
hostgroup_id,
|
||||
host_id)
|
||||
map_info = self.client.do_mapping(lun_id, hostgroup_id,
|
||||
host_id, portg_id)
|
||||
host_lun_id = self.client.get_host_lun_id(host_id, lun_id)
|
||||
|
||||
# Return FC properties.
|
||||
@ -1654,15 +1654,16 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
fc_info = {'driver_volume_type': 'fibre_channel',
|
||||
'data': {}}
|
||||
else:
|
||||
if not self.fcsan_lookup_service:
|
||||
self.fcsan_lookup_service = fczm_utils.create_lookup_service()
|
||||
portg_id = None
|
||||
if not self.fcsan:
|
||||
self.fcsan = fczm_utils.create_lookup_service()
|
||||
|
||||
if self.fcsan_lookup_service:
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(
|
||||
self.fcsan_lookup_service, self.client)
|
||||
if self.fcsan:
|
||||
zone_helper = fc_zone_helper.FCZoneHelper(self.fcsan,
|
||||
self.client)
|
||||
|
||||
(tgt_port_wwns, init_targ_map) = (
|
||||
zone_helper.build_ini_targ_map(wwns))
|
||||
(tgt_port_wwns, portg_id, init_targ_map) = (
|
||||
zone_helper.get_init_targ_map(wwns, host_id))
|
||||
else:
|
||||
(tgt_port_wwns, init_targ_map) = (
|
||||
self.client.get_init_targ_map(wwns))
|
||||
@ -1676,6 +1677,12 @@ class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver):
|
||||
self.client.delete_lungroup_mapping_view(view_id,
|
||||
lungroup_id)
|
||||
self.client.delete_lungroup(lungroup_id)
|
||||
if portg_id:
|
||||
if view_id and self.client.is_portgroup_associated_to_view(
|
||||
view_id, portg_id):
|
||||
self.client.delete_portgroup_mapping_view(view_id,
|
||||
portg_id)
|
||||
self.client.delete_portgroup(portg_id)
|
||||
|
||||
if host_id:
|
||||
hostgroup_name = constants.HOSTGROUP_PREFIX + host_id
|
||||
|
@ -922,13 +922,16 @@ class RestClient(object):
|
||||
|
||||
def get_lunnum_from_lungroup(self, lungroup_id):
|
||||
"""Check if there are still other luns associated to the lungroup."""
|
||||
lunnum = 0
|
||||
if not lungroup_id:
|
||||
return lunnum
|
||||
|
||||
url = ("/lun/count?TYPE=11&ASSOCIATEOBJTYPE=256&"
|
||||
"ASSOCIATEOBJID=%s" % lungroup_id)
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Find lun number error.'))
|
||||
lunnum = -1
|
||||
if 'data' in result:
|
||||
lunnum = result['data']['COUNT']
|
||||
lunnum = int(result['data']['COUNT'])
|
||||
return lunnum
|
||||
|
||||
def is_portgroup_associated_to_view(self, view_id, portgroup_id):
|
||||
@ -1885,3 +1888,129 @@ class RestClient(object):
|
||||
rss_obj.get('LUNMirror') == 'TRUE'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_portgs_by_portid(self, port_id):
|
||||
portgs = []
|
||||
if not port_id:
|
||||
return portgs
|
||||
url = ("/portgroup/associate/fc_port?TYPE=257&ASSOCIATEOBJTYPE=212&"
|
||||
"ASSOCIATEOBJID=%s") % port_id
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get port groups by port error.'))
|
||||
for item in result.get("data", []):
|
||||
portgs.append(item["ID"])
|
||||
return portgs
|
||||
|
||||
def get_views_by_portg(self, portg_id):
|
||||
views = []
|
||||
if not portg_id:
|
||||
return views
|
||||
url = ("/mappingview/associate/portgroup?TYPE=245&ASSOCIATEOBJTYPE="
|
||||
"257&ASSOCIATEOBJID=%s") % portg_id
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get views by port group error.'))
|
||||
for item in result.get("data", []):
|
||||
views.append(item["ID"])
|
||||
return views
|
||||
|
||||
def get_lungroup_by_view(self, view_id):
|
||||
if not view_id:
|
||||
return None
|
||||
url = ("/lungroup/associate/mappingview?TYPE=256&ASSOCIATEOBJTYPE="
|
||||
"245&ASSOCIATEOBJID=%s") % view_id
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get LUN group by view error.'))
|
||||
for item in result.get("data", []):
|
||||
# In fact, there is just one lungroup in a view.
|
||||
return item["ID"]
|
||||
|
||||
def get_portgroup_by_view(self, view_id):
|
||||
if not view_id:
|
||||
return None
|
||||
url = ("/portgroup/associate/mappingview?TYPE=257&ASSOCIATEOBJTYPE="
|
||||
"245&ASSOCIATEOBJID=%s") % view_id
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get port group by view error.'))
|
||||
return result.get("data", [])
|
||||
|
||||
def get_fc_ports_by_portgroup(self, portg_id):
|
||||
ports = {}
|
||||
if not portg_id:
|
||||
return ports
|
||||
url = ("/fc_port/associate/portgroup?TYPE=212&ASSOCIATEOBJTYPE=257"
|
||||
"&ASSOCIATEOBJID=%s") % portg_id
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get FC ports by port group '
|
||||
'error.'))
|
||||
for item in result.get("data", []):
|
||||
ports[item["WWN"]] = item["ID"]
|
||||
return ports
|
||||
|
||||
def create_portg(self, portg_name, description=""):
|
||||
url = "/PortGroup"
|
||||
data = {"DESCRIPTION": description,
|
||||
"NAME": portg_name,
|
||||
"TYPE": 257}
|
||||
result = self.call(url, data, "POST")
|
||||
self._assert_rest_result(result, _('Create port group error.'))
|
||||
if "data" in result:
|
||||
return result['data']['ID']
|
||||
|
||||
def add_port_to_portg(self, portg_id, port_id):
|
||||
url = "/port/associate/portgroup"
|
||||
data = {"ASSOCIATEOBJID": port_id,
|
||||
"ASSOCIATEOBJTYPE": 212,
|
||||
"ID": portg_id,
|
||||
"TYPE": 257}
|
||||
result = self.call(url, data, "POST")
|
||||
self._assert_rest_result(result, _('Add port to port group error.'))
|
||||
|
||||
def delete_portgroup(self, portg_id):
|
||||
url = "/PortGroup/%s" % portg_id
|
||||
result = self.call(url, None, "DELETE")
|
||||
self._assert_rest_result(result, _('Delete port group error.'))
|
||||
|
||||
def remove_port_from_portgroup(self, portg_id, port_id):
|
||||
url = (("/port/associate/portgroup?ID=%(portg_id)s&TYPE=257&"
|
||||
"ASSOCIATEOBJTYPE=212&ASSOCIATEOBJID=%(port_id)s")
|
||||
% {"portg_id": portg_id, "port_id": port_id})
|
||||
result = self.call(url, None, "DELETE")
|
||||
self._assert_rest_result(result, _('Remove port from port group'
|
||||
' error.'))
|
||||
|
||||
def get_all_engines(self):
|
||||
url = "/storageengine"
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get engines error.'))
|
||||
|
||||
return result.get("data", [])
|
||||
|
||||
def get_portg_info(self, portg_id):
|
||||
url = "/portgroup/%s" % portg_id
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, _('Get port group error.'))
|
||||
|
||||
return result.get("data", {})
|
||||
|
||||
def append_portg_desc(self, portg_id, description):
|
||||
portg_info = self.get_portg_info(portg_id)
|
||||
new_description = portg_info.get('DESCRIPTION') + ',' + description
|
||||
url = "/portgroup/%s" % portg_id
|
||||
data = {"DESCRIPTION": new_description,
|
||||
"ID": portg_id,
|
||||
"TYPE": 257}
|
||||
result = self.call(url, data, "PUT")
|
||||
self._assert_rest_result(result, _('Append port group description'
|
||||
' error.'))
|
||||
|
||||
def get_ports_by_portg(self, portg_id):
|
||||
wwns = []
|
||||
url = ("/fc_port/associate?TYPE=213&ASSOCIATEOBJTYPE=257"
|
||||
"&ASSOCIATEOBJID=%s" % portg_id)
|
||||
result = self.call(url, None, "GET")
|
||||
|
||||
msg = _('Get ports by port group error.')
|
||||
self._assert_rest_result(result, msg)
|
||||
for item in result.get('data', []):
|
||||
wwns.append(item['WWN'])
|
||||
return wwns
|
||||
|
@ -0,0 +1,2 @@
|
||||
features:
|
||||
- Support balanced FC port selection for Huawei drivers.
|
Loading…
x
Reference in New Issue
Block a user