From 2870a287406820fff5f5f36bd53e3ebeed2945f2 Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Sun, 3 Mar 2013 00:06:29 -0500 Subject: [PATCH] Fixed copy image to volume and clone volume. This patch fixed the following issues in EMC driver found during G3 testing: 1. VNX array has two storage processors and it is doing load balancing when attaching a LUN to a host. The storage processor used to attach a LUN to a host is different from the one discovered by the driver sometimes, causing Copy Image to Volume to fail. 2. The clone relationship is removed after a volume is successfully cloned. However, this happens too soon sometimes before the cloned volume is fully synced with the source volume. The fix is to wait until it is fully synced. Change-Id: I5b8fab263723c47faf30b950a611c5bde7f29092 Fixes: bug #1135221 --- cinder/tests/test_emc.py | 53 ++++++++++---------- cinder/volume/drivers/emc/emc_smis_common.py | 25 +++++++-- cinder/volume/drivers/emc/emc_smis_iscsi.py | 22 +++++--- 3 files changed, 63 insertions(+), 37 deletions(-) diff --git a/cinder/tests/test_emc.py b/cinder/tests/test_emc.py index 639528e48..e1098c9df 100644 --- a/cinder/tests/test_emc.py +++ b/cinder/tests/test_emc.py @@ -249,6 +249,8 @@ class FakeEcomConnection(): result = self._getinstance_lunmask() elif name == 'SE_ConcreteJob': result = self._getinstance_job(objectpath) + elif name == 'SE_StorageSynchronized_SV_SV': + result = self._getinstance_syncsvsv(objectpath) else: result = self._default_getinstance(objectpath) return result @@ -329,6 +331,16 @@ class FakeEcomConnection(): break return instance + def _getinstance_syncsvsv(self, objectpath): + foundsync = None + syncs = self._enum_syncsvsvs() + for sync in syncs: + if (sync['SyncedElement'] == objectpath['SyncedElement'] and + sync['SystemElement'] == objectpath['SystemElement']): + foundsync = sync + break + return foundsync + def _getinstance_lunmask(self): lunmask = {} lunmask['CreationClassName'] = lunmask_creationclass @@ -508,28 +520,13 @@ class FakeEcomConnection(): vols = self._enum_storagevolumes() - objpath1 = vols[0] - objpath2 = vols[1] - sync = {} - sync['SyncedElement'] = objpath2 - sync['SystemElement'] = objpath1 - sync['CreationClassName'] = 'SE_StorageSynchronized_SV_SV' + sync = self._create_sync(vols[0], vols[1], 100) syncs.append(sync) - objpath1 = vols[1] - objpath2 = vols[2] - sync2 = {} - sync2['SyncedElement'] = objpath2 - sync2['SystemElement'] = objpath1 - sync2['CreationClassName'] = 'SE_StorageSynchronized_SV_SV' + sync2 = self._create_sync(vols[1], vols[2], 100) syncs.append(sync2) - objpath1 = vols[0] - objpath2 = vols[3] - sync3 = {} - sync3['SyncedElement'] = objpath2 - sync3['SystemElement'] = objpath1 - sync3['CreationClassName'] = 'SE_StorageSynchronized_SV_SV' + sync3 = self._create_sync(vols[0], vols[3], 100) syncs.append(sync3) objpath1 = vols[1] @@ -537,10 +534,7 @@ class FakeEcomConnection(): if vol['ElementName'] == 'failed_snapshot_sync': objpath2 = vol break - sync4 = {} - sync4['SyncedElement'] = objpath2 - sync4['SystemElement'] = objpath1 - sync4['CreationClassName'] = 'SE_StorageSynchronized_SV_SV' + sync4 = self._create_sync(objpath1, objpath2, 100) syncs.append(sync4) objpath1 = vols[0] @@ -548,14 +542,19 @@ class FakeEcomConnection(): if vol['ElementName'] == 'failed_clone_sync': objpath2 = vol break - sync5 = {} - sync5['SyncedElement'] = objpath2 - sync5['SystemElement'] = objpath1 - sync5['CreationClassName'] = 'SE_StorageSynchronized_SV_SV' + sync5 = self._create_sync(objpath1, objpath2, 100) syncs.append(sync5) return syncs + def _create_sync(self, objpath1, objpath2, percentsynced): + sync = {} + sync['SyncedElement'] = objpath2 + sync['SystemElement'] = objpath1 + sync['CreationClassName'] = 'SE_StorageSynchronized_SV_SV' + sync['PercentSynced'] = percentsynced + return sync + def _enum_unitnames(self): return self._ref_unitnames() @@ -675,6 +674,8 @@ class EMCSMISISCSIDriverTestCase(test.TestCase): self.driver.create_volume(test_volume) export = self.driver.create_export(None, test_volume) test_volume['provider_location'] = export['provider_location'] + test_volume['EMCCurrentOwningStorageProcessor'] = \ + 'iqn.1993-08.org.debian:01:a1b2c3d4e5f6' connector = {'initiator': initiator1} connection_info = self.driver.initialize_connection(test_volume, connector) diff --git a/cinder/volume/drivers/emc/emc_smis_common.py b/cinder/volume/drivers/emc/emc_smis_common.py index 113200203..60c4c3452 100644 --- a/cinder/volume/drivers/emc/emc_smis_common.py +++ b/cinder/volume/drivers/emc/emc_smis_common.py @@ -552,7 +552,7 @@ class EMCSMISCommon(): 'volume': volumename}) sync_name, storage_system = self._find_storage_sync_sv_sv( - snapshotname, volumename) + snapshotname, volumename, False) if sync_name is None: LOG.error(_('Snapshot: %(snapshot)s: volume: %(volume)s ' 'not found on the array. No snapshot to delete.') @@ -1116,9 +1116,11 @@ class EMCSMISCommon(): return foundinstance - def _find_storage_sync_sv_sv(self, snapshotname, volumename): + def _find_storage_sync_sv_sv(self, snapshotname, volumename, + waitforsync=True): foundsyncname = None storage_system = None + percent_synced = 0 LOG.debug(_("Source: %(volumename)s Target: %(snapshotname)s.") % {'volumename': volumename, 'snapshotname': snapshotname}) @@ -1137,6 +1139,9 @@ class EMCSMISCommon(): if vol_instance['ElementName'] == volumename: foundsyncname = n storage_system = vol_instance['SystemName'] + if waitforsync: + sync_instance = self.conn.GetInstance(n, LocalOnly=False) + percent_synced = sync_instance['PercentSynced'] break if foundsyncname is None: @@ -1149,6 +1154,13 @@ class EMCSMISCommon(): "Storage Synchronized instance: %(sync)s.") % {'storage_system': storage_system, 'sync': str(foundsyncname)}) + # Wait for SE_StorageSynchronized_SV_SV to be fully synced + while waitforsync and percent_synced < 100: + time.sleep(10) + sync_instance = self.conn.GetInstance(foundsyncname, + LocalOnly=False) + percent_synced = sync_instance['PercentSynced'] + return foundsyncname, storage_system def _find_initiator_names(self, connector): @@ -1350,6 +1362,12 @@ class EMCSMISCommon(): volumename = volume['name'] vol_instance = self._find_lun(volume) storage_system = vol_instance['SystemName'] + sp = None + try: + sp = vol_instance['EMCCurrentOwningStorageProcessor'] + except KeyError: + # VMAX LUN doesn't have this property + pass unitnames = self.conn.ReferenceNames( vol_instance.path, @@ -1388,7 +1406,8 @@ class EMCSMISCommon(): 'vol_instance': str(vol_instance.path)}) data = {'hostlunid': out_num_device_number, - 'storagesystem': storage_system} + 'storagesystem': storage_system, + 'owningsp': sp} LOG.debug(_("Device info: %(data)s.") % {'data': data}) diff --git a/cinder/volume/drivers/emc/emc_smis_iscsi.py b/cinder/volume/drivers/emc/emc_smis_iscsi.py index 4f093b615..3046e041d 100644 --- a/cinder/volume/drivers/emc/emc_smis_iscsi.py +++ b/cinder/volume/drivers/emc/emc_smis_iscsi.py @@ -122,11 +122,11 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): '-t', 'sendtargets', '-p', self.configuration.iscsi_ip_address, run_as_root=True) + targets = [] for target in out.splitlines(): - index = target.find(self.configuration.iscsi_ip_address) - if index != -1: - return target - return None + targets.append(target) + + return targets def _get_iscsi_properties(self, volume): """Gets iscsi configuration. @@ -162,10 +162,6 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): LOG.debug(_("ISCSI Discovery: Found %s") % (location)) properties['target_discovered'] = True - results = location.split(" ") - properties['target_portal'] = results[0].split(",")[0] - properties['target_iqn'] = results[1] - device_info = self.common.find_device_number(volume) if device_info is None or device_info['hostlunid'] is None: exception_message = (_("Cannot find device number for volume %s") @@ -174,6 +170,16 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver): device_number = device_info['hostlunid'] + sp = device_info['owningsp'] + for loc in location: + results = loc.split(" ") + properties['target_portal'] = results[0].split(",")[0] + properties['target_iqn'] = results[1] + # owning sp is None for VMAX + # for VNX, find the owning sp in target_iqn + if not sp or -1 != properties['target_iqn'].find(sp): + break + properties['target_lun'] = device_number properties['volume_id'] = volume['id'] -- 2.45.2