"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"
}]
}
"""
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
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')
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):
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)
-
- def test_filter_port_by_contr(self):
-
- # 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)
+ self.assertEqual(target_port_wwns, tgt_wwns)
+ self.assertEqual({}, init_targ_map)
+
+ @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()
+
+ 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):
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
THIN_LUNTYPE = 1
MAX_HOSTNAME_LENGTH = 31
MAX_VOL_DESCRIPTION = 170
+PORT_NUM_PER_CONTR = 2
OS_TYPE = {'Linux': '0',
'Windows': '1',
# 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__)
"""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
-
- 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))
+ 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 _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
+
+ 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
-
- return (list(set(tgt_wwns)), init_targ_map)
+ fabric_connected_ports.extend(
+ ini_tgt_map[fabric]['target_port_wwn_list'])
+ fabric_connected_initiators.extend(
+ ini_tgt_map[fabric]['initiator_port_wwn_list'])
+
+ 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
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."""
'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:
# 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.
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))
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
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):
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
--- /dev/null
+features:
+ - Support balanced FC port selection for Huawei drivers.