class EMCVMAXCommonData():
+ wwpn1 = "123456789012345"
+ wwpn2 = "123456789054321"
connector = {'ip': '10.0.0.2',
'initiator': 'iqn.1993-08.org.debian: 01: 222',
- 'wwpns': ["123456789012345", "123456789054321"],
+ 'wwpns': [wwpn1, wwpn2],
'wwnns': ["223456789012345", "223456789054321"],
'host': 'fakehost'}
+
+ target_wwns = [wwn[::-1] for wwn in connector['wwpns']]
+
+ fabric_name_prefix = "fakeFabric"
+ end_point_map = {connector['wwpns'][0]: [target_wwns[0]],
+ connector['wwpns'][1]: [target_wwns[1]]}
+ device_map = {}
+ for wwn in connector['wwpns']:
+ fabric_name = ''.join([fabric_name_prefix,
+ wwn[-2:]])
+ target_wwn = wwn[::-1]
+ fabric_map = {'initiator_port_wwn_list': [wwn],
+ 'target_port_wwn_list': [target_wwn]
+ }
+ device_map[fabric_name] = fabric_map
+
default_storage_group = (
- u'//10.108.246.202/root/emc: SE_DeviceMaskingGroup.InstanceID='
+ u'//10.10.10.10/root/emc: SE_DeviceMaskingGroup.InstanceID='
'"SYMMETRIX+000198700440+OS_default_GOLD1_SG"')
storage_system = 'SYMMETRIX+000195900551'
lunmaskctrl_id =\
diff = {}
+class FakeLookupService():
+ def get_device_mapping_from_network(self, initiator_wwns, target_wwns):
+ return EMCVMAXCommonData.device_map
+
+
class FakeEcomConnection():
def __init__(self, *args, **kwargs):
targetendpoints = {}
endpoints = []
endpoint = {}
- endpoint['Name'] = '1234567890123'
+ endpoint['Name'] = (EMCVMAXCommonData.end_point_map[
+ EMCVMAXCommonData.connector['wwpns'][0]])
endpoints.append(endpoint)
endpoint2 = {}
- endpoint2['Name'] = '0987654321321'
+ endpoint2['Name'] = (EMCVMAXCommonData.end_point_map[
+ EMCVMAXCommonData.connector['wwpns'][1]])
endpoints.append(endpoint2)
targetendpoints['TargetEndpoints'] = endpoints
return rc, targetendpoints
result = self._enum_storage_extent()
elif ResultClass == 'SE_StorageHardwareID':
result = self._enum_storhdwids()
+ elif ResultClass == 'Symm_FCSCSIProtocolEndpoint':
+ result = self._enum_fcscsiendpoint()
else:
result = self._default_assocnames(objectpath)
foundinstance = None
else:
foundinstance = instance
-
return foundinstance
def _getinstance_lunmask(self):
initatorgroup['DeviceID'] = self.data.initiatorgroup_id
initatorgroup['SystemName'] = self.data.storage_system
initatorgroup['ElementName'] = self.data.initiatorgroup_name
-# initatorgroup.path = initatorgroup
-# initatorgroup.path.classname = initatorgroup['CreationClassName']
initatorgroups.append(initatorgroup)
return initatorgroups
storhdwids.append(hdwid)
return storhdwids
+ def _enum_fcscsiendpoint(self):
+ wwns = []
+ wwn = {}
+ wwn['Name'] = "5000090000000000"
+ wwns.append(wwn)
+ return wwns
+
def _default_enum(self):
names = []
name = {}
self.config_file_1364232 = self.tempdir + '/' + filename
text_file = open(self.config_file_1364232, "w")
text_file.write("<?xml version='1.0' encoding='UTF-8'?>\n<EMC>\n"
- "<EcomServerIp>10.108.246.202</EcomServerIp>\n"
+ "<EcomServerIp>10.10.10.10</EcomServerIp>\n"
"<EcomServerPort>5988</EcomServerPort>\n"
- "<EcomUserName>admin\t</EcomUserName>\n"
- "<EcomPassword>#1Password</EcomPassword>\n"
+ "<EcomUserName>user\t</EcomUserName>\n"
+ "<EcomPassword>password</EcomPassword>\n"
"<PortGroups><PortGroup>OS-PORTGROUP1-PG"
"</PortGroup><PortGroup>OS-PORTGROUP2-PG"
" </PortGroup>\n"
driver = EMCVMAXFCDriver(configuration=configuration)
driver.db = FakeDB()
+ driver.common.conn = FakeEcomConnection()
+ driver.zonemanager_lookup_service = FakeLookupService()
self.driver = driver
def create_fake_config_file_no_fast(self):
@mock.patch.object(
volume_types,
'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'FCNoFAST'})
+ return_value={'volume_backend_name': 'FCNoFAST',
+ 'FASTPOLICY': 'FC_GOLD1'})
@mock.patch.object(
EMCVMAXMasking,
- '_wrap_get_storage_group_from_volume',
- return_value=None)
- @mock.patch.object(
- EMCVMAXCommon,
- '_wrap_find_device_number',
- return_value={'hostlunid': 1,
- 'storagesystem': EMCVMAXCommonData.storage_system})
- def test_map_no_fast_success(self, _mock_volume_type, mock_wrap_group,
- mock_wrap_device):
- self.driver.initialize_connection(self.data.test_volume,
- self.data.connector)
+ 'get_masking_view_from_storage_group',
+ return_value=EMCVMAXCommonData.lunmaskctrl_name)
+ def test_map_lookup_service_no_fast_success(
+ self, _mock_volume_type, mock_maskingview):
+ self.data.test_volume['volume_name'] = "vmax-1234567"
+ common = self.driver.common
+ common.get_target_wwns_from_masking_view = mock.Mock(
+ return_value=EMCVMAXCommonData.target_wwns)
+ lookup_service = self.driver.zonemanager_lookup_service
+ lookup_service.get_device_mapping_from_network = mock.Mock(
+ return_value=EMCVMAXCommonData.device_map)
+ data = self.driver.initialize_connection(self.data.test_volume,
+ self.data.connector)
+ common.get_target_wwns_from_masking_view.assert_called_once_with(
+ EMCVMAXCommonData.storage_system, self.data.test_volume,
+ EMCVMAXCommonData.connector)
+ lookup_service.get_device_mapping_from_network.assert_called_once_with(
+ EMCVMAXCommonData.connector['wwpns'],
+ EMCVMAXCommonData.target_wwns)
+
+ # Test the lookup service code path.
+ for init, target in data['data']['initiator_target_map'].items():
+ self.assertEqual(init, target[0][::-1])
@mock.patch.object(
- EMCVMAXMasking,
- '_wrap_get_storage_group_from_volume',
- return_value=None)
+ volume_types,
+ 'get_volume_type_extra_specs',
+ return_value={'volume_backend_name': 'FCNoFAST',
+ 'FASTPOLICY': 'FC_GOLD1'})
@mock.patch.object(
EMCVMAXCommon,
- '_wrap_find_device_number',
- return_value={'storagesystem': EMCVMAXCommonData.storage_system})
- def test_map_no_fast_failed(self, mock_wrap_group, mock_wrap_device):
+ 'find_device_number',
+ return_value={'Name': "0001"})
+ def test_map_no_fast_failed(self, mock_wrap_group, mock_maskingview):
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
self.data.test_volume,
return_value={'volume_backend_name': 'FCNoFAST',
'FASTPOLICY': 'FC_GOLD1'})
@mock.patch.object(
- EMCVMAXUtils,
- 'find_storage_masking_group',
- return_value=EMCVMAXCommonData.storagegroupname)
- def test_detach_no_fast_success(self, mock_volume_type,
- mock_storage_group):
-
+ EMCVMAXMasking,
+ 'get_masking_view_by_volume',
+ return_value=EMCVMAXCommonData.lunmaskctrl_name)
+ def test_detach_no_fast_success(self, mock_volume_type, mock_maskingview):
self.driver.terminate_connection(self.data.test_volume,
self.data.connector)
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'FCNoFAST'})
@mock.patch.object(
- EMCVMAXUtils, 'find_storage_system',
- return_value={'Name': EMCVMAXCommonData.storage_system})
- @mock.patch.object(
- EMCVMAXUtils,
- 'find_storage_masking_group',
- return_value=EMCVMAXCommonData.storagegroupname)
- def test_detach_no_fast_last_volume_success(self, mock_volume_type,
- mock_storage_system,
- mock_storage_group):
- self.driver.terminate_connection(self.data.test_volume,
+ EMCVMAXMasking,
+ 'get_masking_view_by_volume',
+ return_value=EMCVMAXCommonData.lunmaskctrl_name)
+ def test_detach_no_fast_last_volume_success(
+ self, mock_volume_type, mock_mv):
+ self.driver.terminate_connection(self.data.test_source_volume,
self.data.connector)
@mock.patch.object(
driver = EMCVMAXFCDriver(configuration=configuration)
driver.db = FakeDB()
+ driver.common.conn = FakeEcomConnection()
+ driver.zonemanager_lookup_service = None
self.driver = driver
def create_fake_config_file_fast(self):
@mock.patch.object(
volume_types,
'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'FCFAST'})
+ return_value={'volume_backend_name': 'FCFAST',
+ 'FASTPOLICY': 'FC_GOLD1'})
@mock.patch.object(
EMCVMAXMasking,
- '_wrap_get_storage_group_from_volume',
- return_value=None)
- @mock.patch.object(
- EMCVMAXCommon,
- '_wrap_find_device_number',
- return_value={'hostlunid': 1,
- 'storagesystem': EMCVMAXCommonData.storage_system})
- def test_map_fast_success(self, _mock_volume_type, mock_wrap_group,
- mock_wrap_device):
- self.driver.initialize_connection(self.data.test_volume,
- self.data.connector)
+ 'get_masking_view_from_storage_group',
+ return_value=EMCVMAXCommonData.lunmaskctrl_name)
+ def test_map_fast_success(self, _mock_volume_type, mock_maskingview):
+ self.data.test_volume['volume_name'] = "vmax-1234567"
+ common = self.driver.common
+ common.get_target_wwns = mock.Mock(
+ return_value=EMCVMAXCommonData.target_wwns)
+ data = self.driver.initialize_connection(
+ self.data.test_volume, self.data.connector)
+ # Test the no lookup service, pre-zoned case.
+ common.get_target_wwns.assert_called_once_with(
+ EMCVMAXCommonData.storage_system, EMCVMAXCommonData.connector)
+ for init, target in data['data']['initiator_target_map'].items():
+ self.assertIn(init[::-1], target)
@mock.patch.object(
- EMCVMAXMasking,
- '_wrap_get_storage_group_from_volume',
- return_value=None)
+ volume_types,
+ 'get_volume_type_extra_specs',
+ return_value={'volume_backend_name': 'FCFAST',
+ 'FASTPOLICY': 'FC_GOLD1'})
@mock.patch.object(
EMCVMAXCommon,
- '_wrap_find_device_number',
- return_value={'storagesystem': EMCVMAXCommonData.storage_system})
- def test_map_fast_failed(self, mock_wrap_group, mock_wrap_device):
+ 'find_device_number',
+ return_value={'Name': "0001"})
+ def test_map_fast_failed(self, mock_wrap_group, mock_maskingview):
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
self.data.test_volume,
return_value={'volume_backend_name': 'FCFAST',
'FASTPOLICY': 'FC_GOLD1'})
@mock.patch.object(
- EMCVMAXUtils,
- 'find_storage_masking_group',
- return_value=EMCVMAXCommonData.storagegroupname)
- def test_detach_fast_success(self, mock_volume_type,
- mock_storage_group):
-
- self.driver.terminate_connection(self.data.test_volume,
- self.data.connector)
-
- @mock.patch.object(
- volume_types,
- 'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'FCFAST'})
- @mock.patch.object(
- EMCVMAXUtils, 'find_storage_system',
- return_value={'Name': EMCVMAXCommonData.storage_system})
- @mock.patch.object(
- EMCVMAXUtils,
- 'find_storage_masking_group',
- return_value=EMCVMAXCommonData.storagegroupname)
- def test_detach_fast_last_volume_success(
- self, mock_volume_type,
- mock_storage_system, mock_storage_group):
- self.driver.terminate_connection(self.data.test_volume,
- self.data.connector)
+ EMCVMAXMasking,
+ 'get_masking_view_by_volume',
+ return_value=EMCVMAXCommonData.lunmaskctrl_name)
+ def test_detach_fast_success(self, mock_volume_type, mock_maskingview):
+ common = self.driver.common
+ common.get_target_wwns = mock.Mock(
+ return_value=EMCVMAXCommonData.target_wwns)
+ data = self.driver.terminate_connection(self.data.test_volume,
+ self.data.connector)
+ common.get_target_wwns.assert_called_once_with(
+ EMCVMAXCommonData.storage_system, EMCVMAXCommonData.connector)
+
+ self.assertEqual(0, len(data['data']))
@mock.patch.object(
volume_types,
from cinder.volume.drivers.emc import emc_vmax_common
from cinder.zonemanager import utils as fczm_utils
-
LOG = logging.getLogger(__name__)
self.common = emc_vmax_common.EMCVMAXCommon(
'FC',
configuration=self.configuration)
+ self.zonemanager_lookup_service = fczm_utils.create_lookup_service()
def check_for_setup_error(self):
pass
device_number = device_info['hostlunid']
storage_system = device_info['storagesystem']
target_wwns, init_targ_map = self._build_initiator_target_map(
- storage_system, connector)
+ storage_system, volume, connector)
data = {'driver_volume_type': 'fibre_channel',
'data': {'target_lun': device_number,
'target_wwn': target_wwns,
'initiator_target_map': init_targ_map}}
- LOG.debug("Return FC data: %(data)s."
+ LOG.debug("Return FC data for zone addition: %(data)s."
% {'data': data})
return data
:returns: data - the target_wwns and initiator_target_map if the
zone is to be removed, otherwise empty
"""
- self.common.terminate_connection(volume, connector)
-
loc = volume['provider_location']
name = eval(loc)
storage_system = name['keybindings']['SystemName']
+ LOG.info("Start FC detach process for volume: %(volume)s"
+ % {'volume': volume['name']})
+
+ target_wwns, init_targ_map = self._build_initiator_target_map(
+ storage_system, volume, connector)
+
+ mvInstanceName = self.common.get_masking_view_by_volume(volume)
+ portGroupInstanceName = self.common.get_port_group_from_masking_view(
+ mvInstanceName)
+
+ LOG.info("Found port group: %(portGroup)s "
+ "in masking view %(maskingView)s"
+ % {'portGroup': portGroupInstanceName,
+ 'maskingView': mvInstanceName})
- numVolumes = self.common.get_num_volumes_mapped(volume, connector)
- if numVolumes > 0:
+ self.common.terminate_connection(volume, connector)
+
+ LOG.info("Looking for masking views still associated with"
+ "Port Group %s" % portGroupInstanceName)
+ mvInstances = self.common.get_masking_views_by_port_group(
+ portGroupInstanceName)
+ if len(mvInstances) > 0:
+ LOG.debug("Found %(numViews)lu maskingviews."
+ % {'numViews': len(mvInstances)})
data = {'driver_volume_type': 'fibre_channel',
'data': {}}
- else:
- target_wwns, init_targ_map = self._build_initiator_target_map(
- storage_system, connector)
+ else: # no views found
+ LOG.debug("No Masking Views were found. Deleting zone.")
data = {'driver_volume_type': 'fibre_channel',
'data': {'target_wwn': target_wwns,
'initiator_target_map': init_targ_map}}
- LOG.debug("Return FC data: %(data)s."
+ LOG.debug("Return FC data for zone removal: %(data)s."
% {'data': data})
return data
- def _build_initiator_target_map(self, storage_system, connector):
+ def _build_initiator_target_map(self, storage_system, volume, connector):
"""Build the target_wwns and the initiator target map."""
-
- target_wwns = self.common.get_target_wwns(storage_system, connector)
+ target_wwns = []
+ init_targ_map = {}
initiator_wwns = connector['wwpns']
- init_targ_map = {}
- for initiator in initiator_wwns:
- init_targ_map[initiator] = target_wwns
-
- return target_wwns, init_targ_map
+ if self.zonemanager_lookup_service:
+ fc_targets = self.common.get_target_wwns_from_masking_view(
+ storage_system, volume, connector)
+ mapping = (
+ self.zonemanager_lookup_service.
+ get_device_mapping_from_network(initiator_wwns, fc_targets))
+ for entry in mapping:
+ map_d = mapping[entry]
+ target_wwns.extend(map_d['target_port_wwn_list'])
+ for initiator in map_d['initiator_port_wwn_list']:
+ init_targ_map[initiator] = map_d['target_port_wwn_list']
+ else: # no lookup service, pre-zoned case
+ target_wwns = self.common.get_target_wwns(storage_system,
+ connector)
+ for initiator in initiator_wwns:
+ init_targ_map[initiator] = target_wwns
+
+ return list(set(target_wwns)), init_targ_map
def extend_volume(self, volume, new_size):
"""Extend an existing volume."""
LOG.debug(
"end: number of volumes in default storage group: %(numVol)d"
% {'numVol': len(volumeInstanceNames)})
+
+ def get_target_wwns(self, conn, mvInstanceName):
+ """Get the DA ports' wwns.
+
+ :param conn: the ecom connection
+ :param mvInstanceName: masking view instance name
+ """
+ targetWwns = []
+ targetPortInstanceNames = conn.AssociatorNames(
+ mvInstanceName,
+ ResultClass='Symm_FCSCSIProtocolEndpoint')
+ numberOfPorts = len(targetPortInstanceNames)
+ if numberOfPorts <= 0:
+ LOG.warn("No target ports found in "
+ "masking view %(maskingView)s"
+ % {'numPorts': len(targetPortInstanceNames),
+ 'maskingView': mvInstanceName})
+ for targetPortInstanceName in targetPortInstanceNames:
+ targetWwns.append(targetPortInstanceName['Name'])
+ return targetWwns
+
+ def get_masking_view_by_volume(self, conn, volumeInstance):
+ """Given volume, retrieve the masking view instance name.
+
+ :param volume: the volume instance
+ :param mvInstanceName: masking view instance name
+ """
+ sgInstanceName = self.get_associated_masking_group_from_device(
+ conn, volumeInstance.path)
+ mvInstanceName = self.get_masking_view_from_storage_group(
+ conn, sgInstanceName)
+ LOG.debug("Found Masking View %(mv)s: " % {'mv': mvInstanceName})
+ return mvInstanceName
+
+ def get_masking_views_by_port_group(self, conn, portGroupInstanceName):
+ """Given port group, retrieve the masking view instance name.
+
+ :param : the volume
+ :param mvInstanceName: masking view instance name
+ :returns: maksingViewInstanceNames
+ """
+ mvInstanceNames = conn.AssociatorNames(
+ portGroupInstanceName, ResultClass='Symm_LunMaskingView')
+ return mvInstanceNames
+
+ def get_port_group_from_masking_view(self, conn, maskingViewInstanceName):
+ """Get the port group in a masking view.
+
+ :param maskingViewInstanceName: masking view instance name
+ :returns: portGroupInstanceName
+ """
+ portGroupInstanceNames = conn.AssociatorNames(
+ maskingViewInstanceName, ResultClass='SE_TargetMaskingGroup')
+ if len(portGroupInstanceNames) > 0:
+ LOG.debug("Found port group %(pg)s in masking view %(mv)s"
+ % {'pg': portGroupInstanceNames[0],
+ 'mv': maskingViewInstanceName})
+ return portGroupInstanceNames[0]
+ else:
+ LOG.warn("No port group found in masking view %(mv)s"
+ % {'mv': maskingViewInstanceName})