Merge "Huawei: Balanced FC port selection when zoning"

This commit is contained in:
Jenkins 2016-01-31 20:43:21 +00:00 committed by Gerrit Code Review
commit 55744deafb
6 changed files with 603 additions and 82 deletions

View File

@ -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):

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,2 @@
features:
- Support balanced FC port selection for Huawei drivers.