From 7320422e5a496350906dcd2a1cd5487088cf63e5 Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Sat, 22 Mar 2014 15:05:42 -0400 Subject: [PATCH] Fixes a problem in attach volume in EMC driver. This patch fixes a problem in attach volume in EMC SMI-S driver. The existing logic checks if a volume is already attached to any host, but it doesn't check whether a volume is already attached to the specific host that nova wants cinder to attach. As a result, initialize_connection could return success (thinking it is already attached), but nova will fail to discover the LUN later and fail the attach. This patch adds a check to see if a volume is already attached to a specific host. If not, it will do the attach. The reason that the volume being attached already could be due to a nova live-migration use case. Cinder doesn't support multiple attaches currently, but allows a volume to be attached multiple times from nova during live-migration. Change-Id: I05a2f57cd8708d7fcbe902ec13665a9cfb44db07 Closes-Bug: #1295906 --- cinder/tests/test_emc_smis.py | 6 +- cinder/volume/drivers/emc/emc_smis_common.py | 62 +++++++++++++------- cinder/volume/drivers/emc/emc_smis_iscsi.py | 16 ++--- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/cinder/tests/test_emc_smis.py b/cinder/tests/test_emc_smis.py index aa4372b6d..9bbde8acc 100644 --- a/cinder/tests/test_emc_smis.py +++ b/cinder/tests/test_emc_smis.py @@ -398,8 +398,12 @@ class FakeEcomConnection(): def _assoc_hdwid(self): assocs = [] assoc = {} - assoc['StorageID'] = self.data.initiator1 + assoc['StorageID'] = self.data.connector['initiator'] assocs.append(assoc) + for wwpn in self.data.connector['wwpns']: + assoc2 = {} + assoc2['StorageID'] = wwpn + assocs.append(assoc2) return assocs def _assoc_endpoint(self): diff --git a/cinder/volume/drivers/emc/emc_smis_common.py b/cinder/volume/drivers/emc/emc_smis_common.py index 29ce057e6..736d068dc 100644 --- a/cinder/volume/drivers/emc/emc_smis_common.py +++ b/cinder/volume/drivers/emc/emc_smis_common.py @@ -901,7 +901,7 @@ class EMCSMISCommon(): LOG.info(_('Unmap volume: %(volume)s') % {'volume': volumename}) - device_info = self.find_device_number(volume) + device_info = self.find_device_number(volume, connector) device_number = device_info['hostlunid'] if device_number is None: LOG.info(_("Volume %s is not mapped. No volume to unmap.") @@ -931,7 +931,7 @@ class EMCSMISCommon(): LOG.info(_('Initialize connection: %(volume)s') % {'volume': volumename}) self.conn = self._get_ecom_connection() - device_info = self.find_device_number(volume) + device_info = self.find_device_number(volume, connector) device_number = device_info['hostlunid'] if device_number is not None: LOG.info(_("Volume %s is already mapped.") @@ -939,7 +939,7 @@ class EMCSMISCommon(): else: self._map_lun(volume, connector) # Find host lun id again after the volume is exported to the host - device_info = self.find_device_number(volume) + device_info = self.find_device_number(volume, connector) return device_info @@ -1533,7 +1533,7 @@ class EMCSMISCommon(): return out_device_number # Find a device number that a host can see for a volume - def find_device_number(self, volume): + def find_device_number(self, volume, connector): out_num_device_number = None volumename = volume['name'] @@ -1546,29 +1546,47 @@ class EMCSMISCommon(): # VMAX LUN doesn't have this property pass - unitnames = self.conn.ReferenceNames( - vol_instance.path, - ResultClass='CIM_ProtocolControllerForUnit') - - for unitname in unitnames: - controller = unitname['Antecedent'] - classname = controller['CreationClassName'] - index = classname.find('LunMaskingSCSIProtocolController') - if index > -1: # VNX - # Get an instance of CIM_ProtocolControllerForUnit - unitinstance = self.conn.GetInstance(unitname, - LocalOnly=False) - numDeviceNumber = int(unitinstance['DeviceNumber'], 16) - out_num_device_number = numDeviceNumber - break - else: - index = classname.find('Symm_LunMaskingView') - if index > -1: # VMAX + indexVMAX = storage_system.find('SYMMETRIX') + if indexVMAX == -1: + # find out whether the volume is already attached to the host + ctrl = self._find_lunmasking_scsi_protocol_controller_for_vol( + vol_instance, + connector) + + LOG.debug(_("LunMaskingSCSIProtocolController for " + "volume %(vol)s and connector %(connector)s " + "is %(ctrl)s.") + % {'vol': vol_instance.path, + 'connector': connector, + 'ctrl': ctrl}) + + if indexVMAX > -1 or ctrl: + unitnames = self.conn.ReferenceNames( + vol_instance.path, + ResultClass='CIM_ProtocolControllerForUnit') + + for unitname in unitnames: + controller = unitname['Antecedent'] + classname = controller['CreationClassName'] + index = classname.find('LunMaskingSCSIProtocolController') + if index > -1: # VNX + if ctrl['DeviceID'] != controller['DeviceID']: + continue + # Get an instance of CIM_ProtocolControllerForUnit unitinstance = self.conn.GetInstance(unitname, LocalOnly=False) numDeviceNumber = int(unitinstance['DeviceNumber'], 16) out_num_device_number = numDeviceNumber break + else: + index = classname.find('Symm_LunMaskingView') + if index > -1: # VMAX + unitinstance = self.conn.GetInstance(unitname, + LocalOnly=False) + numDeviceNumber = int(unitinstance['DeviceNumber'], + 16) + out_num_device_number = numDeviceNumber + break if out_num_device_number is None: LOG.info(_("Device number not found for volume " diff --git a/cinder/volume/drivers/emc/emc_smis_iscsi.py b/cinder/volume/drivers/emc/emc_smis_iscsi.py index 9548d49c0..205daafd0 100644 --- a/cinder/volume/drivers/emc/emc_smis_iscsi.py +++ b/cinder/volume/drivers/emc/emc_smis_iscsi.py @@ -127,7 +127,7 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): """Initializes the connection and returns connection info. The iscsi driver returns a driver_volume_type of 'iscsi'. - the format of the driver data is defined in _get_iscsi_properties. + the format of the driver data is defined in smis_get_iscsi_properties. Example return value:: { @@ -136,14 +136,14 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): 'target_discovered': True, 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000001', 'target_portal': '127.0.0.0.1:3260', - 'volume_id': 1, + 'volume_id': '12345678-1234-4321-1234-123456789012', } } """ self.common.initialize_connection(volume, connector) - iscsi_properties = self._get_iscsi_properties(volume) + iscsi_properties = self.smis_get_iscsi_properties(volume, connector) return { 'driver_volume_type': 'iscsi', 'data': iscsi_properties @@ -163,7 +163,7 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): return targets - def _get_iscsi_properties(self, volume): + def smis_get_iscsi_properties(self, volume, connector): """Gets iscsi configuration. We ideally get saved information in the volume entity, but fall back @@ -178,7 +178,7 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): :target_lun: the lun of the iSCSI target - :volume_id: the id of the volume (currently used by xen) + :volume_id: the UUID of the volume :auth_method:, :auth_username:, :auth_password: @@ -197,7 +197,7 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): LOG.debug(_("ISCSI Discovery: Found %s") % (location)) properties['target_discovered'] = True - device_info = self.common.find_device_number(volume) + device_info = self.common.find_device_number(volume, connector) if device_info is None or device_info['hostlunid'] is None: exception_message = (_("Cannot find device number for volume %s") % volume['name']) @@ -245,6 +245,8 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): properties['volume_id'] = volume['id'] + LOG.debug(_("ISCSI properties: %s") % (properties)) + auth = volume['provider_auth'] if auth: (auth_method, auth_username, auth_secret) = auth.split() @@ -253,8 +255,6 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): properties['auth_username'] = auth_username properties['auth_password'] = auth_secret - LOG.debug(_("ISCSI properties: %s") % (properties)) - return properties def terminate_connection(self, volume, connector, **kwargs): -- 2.45.2