From a75fe96c3f387630d1c786acf2171e63287f628a Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Fri, 20 Feb 2015 01:28:10 -0500 Subject: [PATCH] Make Interval and Retries Configurable for VMAX JOB_RETRIES=60 and INTERVAL_10_SEC=10 are hard-coded in VMAX driver. This patch makes them configurable. Change-Id: I39930580b1aa4f62b51d7eb332ba96791a3bf4c3 Closes-Bug: #1401279 --- cinder/tests/test_emc_vmax.py | 75 ++- cinder/volume/drivers/emc/emc_vmax_common.py | 428 ++++++++++-------- cinder/volume/drivers/emc/emc_vmax_fast.py | 44 +- cinder/volume/drivers/emc/emc_vmax_masking.py | 83 ++-- .../volume/drivers/emc/emc_vmax_provision.py | 138 ++++-- .../drivers/emc/emc_vmax_provision_v3.py | 71 ++- cinder/volume/drivers/emc/emc_vmax_utils.py | 82 +++- 7 files changed, 582 insertions(+), 339 deletions(-) diff --git a/cinder/tests/test_emc_vmax.py b/cinder/tests/test_emc_vmax.py index f492250fd..68a36e394 100644 --- a/cinder/tests/test_emc_vmax.py +++ b/cinder/tests/test_emc_vmax.py @@ -1424,7 +1424,29 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): doc = Document() emc = doc.createElement("EMC") doc.appendChild(emc) + doc = self.add_array_info(doc, emc) + filename = 'cinder_emc_config_ISCSINoFAST.xml' + self.config_file_path = self.tempdir + '/' + filename + + f = open(self.config_file_path, 'w') + doc.writexml(f) + f.close() + + def create_fake_config_file_no_fast_with_add_ons(self): + + doc = Document() + emc = doc.createElement("EMC") + doc.appendChild(emc) + doc = self.add_array_info(doc, emc) + doc = self.add_interval_and_retries(doc, emc) + filename = 'cinder_emc_config_ISCSINoFAST.xml' + self.config_file_path = self.tempdir + '/' + filename + + f = open(self.config_file_path, 'w') + doc.writexml(f) + f.close() + def add_array_info(self, doc, emc): array = doc.createElement("Array") arraytext = doc.createTextNode("1234567891011") emc.appendChild(array) @@ -1472,14 +1494,19 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): timeouttext = doc.createTextNode("0") emc.appendChild(timeout) timeout.appendChild(timeouttext) + return doc - filename = 'cinder_emc_config_ISCSINoFAST.xml' + def add_interval_and_retries(self, doc, emc): + interval = doc.createElement("Interval") + intervaltext = doc.createTextNode("5") + emc.appendChild(interval) + interval.appendChild(intervaltext) - self.config_file_path = self.tempdir + '/' + filename - - f = open(self.config_file_path, 'w') - doc.writexml(f) - f.close() + retries = doc.createElement("Retries") + retriestext = doc.createTextNode("40") + emc.appendChild(retries) + retries.appendChild(retriestext) + return doc # fix for https://bugs.launchpad.net/cinder/+bug/1364232 def create_fake_config_file_1364232(self): @@ -1633,8 +1660,8 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): # Tests removal of last volume in a storage group V2 def test_remove_and_reset_members(self): - fastPolicyName = 'gold' - isV3 = False + extraSpecs = {'volume_backend_name': 'GOLD_BE', + 'isV3': False} conn = self.fake_ecom_connection() controllerConfigService = ( self.driver.utils.find_controller_configuration_service( @@ -1642,7 +1669,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): volumeInstanceName = ( conn.EnumerateInstanceNames("EMC_StorageVolume")[0]) volumeInstance = conn.GetInstance(volumeInstanceName) - volumeName = "last-Vol" + volumeName = "1416035-Vol" self.driver.common.masking.get_devices_from_storage_group = mock.Mock( return_value=['one_value']) self.driver.common.masking.utils.get_existing_instance = mock.Mock( @@ -1650,7 +1677,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): self.driver.common.masking.remove_and_reset_members( conn, controllerConfigService, volumeInstance, - fastPolicyName, volumeName, isV3) + volumeName, extraSpecs) # Bug 1393555 - masking view has been deleted by another process. def test_find_maskingview(self): @@ -1931,6 +1958,31 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): if bExists: os.remove(self.config_file_1364232) + @mock.patch.object( + emc_vmax_common.EMCVMAXCommon, + '_get_pool_and_storage_system', + return_value=(None, EMCVMAXCommonData.storage_system)) + @mock.patch.object( + volume_types, + 'get_volume_type_extra_specs', + return_value={'volume_backend_name': 'ISCSINoFAST'}) + def test_intervals_and_retries( + self, _mock_volume_type, mock_storage_system): + save_config_path = self.config_file_path + self.create_fake_config_file_no_fast_with_add_ons() + self.driver.create_volume(self.data.test_volume_v2) + extraSpecs = self.driver.common.extraSpecs + self.assertEqual(40, + self.driver.utils._get_max_job_retries(extraSpecs)) + self.assertEqual(5, + self.driver.utils._get_interval_in_secs(extraSpecs)) + + bExists = os.path.exists(self.config_file_path) + if bExists: + os.remove(self.config_file_path) + + self.config_file_path = save_config_path + @mock.patch.object( emc_vmax_utils.EMCVMAXUtils, 'find_storageSystem', @@ -4587,7 +4639,8 @@ class EMCV3DriverTestCase(test.TestCase): provisionv3.create_group_replica.assert_called_once_with( self.conn, repServ, (None, EMCVMAXCommonData.test_CG), - (None, EMCVMAXCommonData.test_CG), '12de') + (None, EMCVMAXCommonData.test_CG), '12de', + EMCVMAXCommonData.extra_specs) @mock.patch.object( volume_types, diff --git a/cinder/volume/drivers/emc/emc_vmax_common.py b/cinder/volume/drivers/emc/emc_vmax_common.py index d1c3e32ec..bbaa0dd04 100644 --- a/cinder/volume/drivers/emc/emc_vmax_common.py +++ b/cinder/volume/drivers/emc/emc_vmax_common.py @@ -56,6 +56,8 @@ CONCATENATED = 'concatenated' # V3 SLO = 'storagetype:slo' WORKLOAD = 'storagetype:workload' +INTERVAL = 'storagetype:interval' +RETRIES = 'storagetype:retries' ISV3 = 'isV3' emc_opts = [ @@ -105,6 +107,7 @@ class EMCVMAXCommon(object): self.url = None self.user = None self.passwd = None + self.extraSpecs = {} self.masking = emc_vmax_masking.EMCVMAXMasking(prtcl) self.utils = emc_vmax_utils.EMCVMAXUtils(prtcl) self.fast = emc_vmax_fast.EMCVMAXFast(prtcl) @@ -176,17 +179,15 @@ class EMCVMAXCommon(object): """ volumeSize = int(self.utils.convert_gb_to_bits(volume['size'])) volumeName = volume['id'] - extraSpecs = self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) self.conn = self._get_ecom_connection() - if extraSpecs[ISV3]: + if self.extraSpecs[ISV3]: rc, volumeDict, storageSystemName = ( - self._create_v3_volume(volume, extraSpecs, - volumeName, volumeSize)) + self._create_v3_volume(volume, volumeName, volumeSize)) else: rc, volumeDict, storageSystemName = ( - self._create_composite_volume(volume, extraSpecs, - volumeName, volumeSize)) + self._create_composite_volume(volume, volumeName, volumeSize)) # If volume is created as part of a consistency group. if 'consistencygroup_id' in volume and volume['consistencygroup_id']: @@ -204,7 +205,8 @@ class EMCVMAXCommon(object): cgInstanceName, volumeInstance.path, cgName, - volumeName) + volumeName, + self.extraSpecs) LOG.info(_LI("Leaving create_volume: %(volumeName)s " "Return code: %(rc)lu " @@ -225,7 +227,7 @@ class EMCVMAXCommon(object): :returns: cloneVolumeDict - the cloned volume dictionary """ LOG.debug("Entering create_volume_from_snapshot.") - self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) self.conn = self._get_ecom_connection() snapshotInstance = self._find_lun(snapshot) storageSystem = snapshotInstance['SystemName'] @@ -243,9 +245,9 @@ class EMCVMAXCommon(object): data=exception_message) self.provision.delete_clone_relationship( - self.conn, repservice, syncName) + self.conn, repservice, syncName, self.extraSpecs) - return self._create_cloned_volume(volume, snapshot) + return self._create_cloned_volume(volume, snapshot, False) def create_cloned_volume(self, cloneVolume, sourceVolume): """Creates a clone of the specified volume. @@ -254,7 +256,7 @@ class EMCVMAXCommon(object): :param sourceVolume - volume object :returns: cloneVolumeDict - the cloned volume dictionary """ - return self._create_cloned_volume(cloneVolume, sourceVolume) + return self._create_cloned_volume(cloneVolume, sourceVolume, False) def delete_volume(self, volume): """Deletes a EMC(VMAX) volume. @@ -292,7 +294,7 @@ class EMCVMAXCommon(object): self._delete_snapshot(snapshot) def _remove_members(self, controllerConfigService, - volumeInstance, extraSpecs, connector): + volumeInstance, connector): """This method unmaps a volume from a host. Removes volume from the Device Masking Group that belongs to @@ -310,11 +312,9 @@ class EMCVMAXCommon(object): """ volumeName = volumeInstance['ElementName'] LOG.debug("Detaching volume %s.", volumeName) - fastPolicyName = extraSpecs.get(FASTPOLICY, None) - isV3 = extraSpecs[ISV3] return self.masking.remove_and_reset_members( self.conn, controllerConfigService, volumeInstance, - fastPolicyName, volumeName, isV3, connector) + volumeName, self.extraSpecs, connector) def _unmap_lun(self, volume, connector): """Unmaps a volume from the host. @@ -323,7 +323,7 @@ class EMCVMAXCommon(object): :param connector: the connector Object :raises: VolumeBackendAPIException """ - extraSpecs = self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) volumename = volume['name'] LOG.info(_LI("Unmap volume: %(volume)s."), {'volume': volumename}) @@ -347,8 +347,7 @@ class EMCVMAXCommon(object): % {'storage_system': storage_system}) raise exception.VolumeBackendAPIException(data=exception_message) - self._remove_members(configservice, vol_instance, - extraSpecs, connector) + self._remove_members(configservice, vol_instance, connector) def initialize_connection(self, volume, connector): """Initializes the connection and returns device and connection info. @@ -376,7 +375,7 @@ class EMCVMAXCommon(object): :returns: deviceInfoDict, device information tuple :raises: VolumeBackendAPIException """ - extraSpecs = self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) volumeName = volume['name'] LOG.info(_LI("Initialize connection: %(volume)s."), @@ -398,14 +397,13 @@ class EMCVMAXCommon(object): 'deviceNumber': deviceNumber}) else: deviceInfoDict = self._attach_volume( - volume, connector, extraSpecs, True) + volume, connector, True) else: - deviceInfoDict = self._attach_volume(volume, connector, extraSpecs) + deviceInfoDict = self._attach_volume(volume, connector) return deviceInfoDict - def _attach_volume(self, volume, connector, extraSpecs, - isLiveMigration=None): + def _attach_volume(self, volume, connector, isLiveMigration=None): """Attach a volume to a host. If live migration is being undertaken then the volume @@ -419,14 +417,14 @@ class EMCVMAXCommon(object): """ volumeName = volume['name'] maskingViewDict = self._populate_masking_dict( - volume, connector, extraSpecs) + volume, connector) if isLiveMigration: maskingViewDict['isLiveMigration'] = True else: maskingViewDict['isLiveMigration'] = False rollbackDict = self.masking.get_or_create_masking_view_and_map_lun( - self.conn, maskingViewDict) + self.conn, maskingViewDict, self.extraSpecs) # Find host lun id again after the volume is exported to the host. deviceInfoDict = self.find_device_number(volume) @@ -472,7 +470,7 @@ class EMCVMAXCommon(object): :params volume: the volume Object :params connectorL the connector Object """ - self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) volumename = volume['name'] LOG.info(_LI("Terminate connection: %(volume)s."), @@ -495,7 +493,7 @@ class EMCVMAXCommon(object): """ originalVolumeSize = volume['size'] volumeName = volume['name'] - self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) self.conn = self._get_ecom_connection() volumeInstance = self._find_lun(volume) if volumeInstance is None: @@ -637,7 +635,7 @@ class EMCVMAXCommon(object): LOG.info(_LI("Migrating using retype Volume: %(volume)s."), {'volume': volumeName}) - extraSpecs = self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) self.conn = self._get_ecom_connection() volumeInstance = self._find_lun(volume) @@ -647,14 +645,14 @@ class EMCVMAXCommon(object): {'name': volumeName}) return False - if extraSpecs[ISV3]: + if self.extraSpecs[ISV3]: return self._slo_workload_migration(volumeInstance, volume, host, volumeName, volumeStatus, - extraSpecs, new_type) + new_type) else: return self._pool_migration(volumeInstance, volume, host, volumeName, volumeStatus, - extraSpecs[FASTPOLICY], new_type) + self.extraSpecs[FASTPOLICY], new_type) def migrate_volume(self, ctxt, volume, host, new_type=None): """Migrate volume to another host. @@ -769,7 +767,7 @@ class EMCVMAXCommon(object): try: self.provision.migrate_volume_to_storage_pool( conn, storageRelocationService, volumeInstance.path, - sourcePoolInstanceName) + sourcePoolInstanceName, self.extraSpecs) except Exception: LOG.error(_LE( "Failed to return volume %(volumeName)s to " @@ -825,7 +823,8 @@ class EMCVMAXCommon(object): defaultStorageGroupInstanceName)): self.provision.remove_device_from_storage_group( conn, controllerConfigurationService, - assocStorageGroupInstanceName, volumeInstance.path, volumeName) + assocStorageGroupInstanceName, + volumeInstance.path, volumeName, self.extraSpecs) self.add_to_default_SG( conn, volumeInstance, storageSystemName, sourceFastPolicyName, @@ -859,7 +858,7 @@ class EMCVMAXCommon(object): defaultStorageGroupInstanceName = ( self.fast.get_or_create_default_storage_group( self.conn, controllerConfigurationService, - targetFastPolicyName, volumeInstance)) + targetFastPolicyName, volumeInstance, self.extraSpecs)) if defaultStorageGroupInstanceName is None: LOG.error(_LE( "Unable to create or get default storage group for FAST policy" @@ -871,7 +870,7 @@ class EMCVMAXCommon(object): defaultStorageGroupInstanceName = ( self.fast.add_volume_to_default_storage_group_for_fast_policy( self.conn, controllerConfigurationService, volumeInstance, - volumeName, targetFastPolicyName)) + volumeName, targetFastPolicyName, self.extraSpecs)) if defaultStorageGroupInstanceName is None: LOG.error(_LE( "Failed to verify that volume was added to storage group for " @@ -921,7 +920,7 @@ class EMCVMAXCommon(object): try: rc = self.provision.migrate_volume_to_storage_pool( self.conn, storageRelocationService, volumeInstance.path, - targetPoolInstanceName) + targetPoolInstanceName, self.extraSpecs) except Exception as e: # Rollback by deleting the volume if adding the volume to the # default storage group were to fail. @@ -952,7 +951,7 @@ class EMCVMAXCommon(object): LOG.debug("Terminating migration session on: %(volumeName)s.", {'volumeName': volumeName}) self.provision._terminate_migrate_session( - self.conn, volumeInstance.path) + self.conn, volumeInstance.path, self.extraSpecs) if rc == 0: moved = True @@ -980,7 +979,8 @@ class EMCVMAXCommon(object): defaultStorageGroupInstanceName = ( self.masking.remove_device_from_default_storage_group( conn, controllerConfigurationService, - volumeInstance.path, volumeName, sourceFastPolicyName)) + volumeInstance.path, volumeName, sourceFastPolicyName, + self.extraSpecs)) except Exception as ex: LOG.error(_LE("Exception: %s"), ex) exceptionMessage = (_( @@ -1022,7 +1022,7 @@ class EMCVMAXCommon(object): self.fast .add_volume_to_default_storage_group_for_fast_policy( conn, controllerConfigurationService, volumeInstance, - volumeName, targetFastPolicyName)) + volumeName, targetFastPolicyName, self.extraSpecs)) if assocDefaultStorageGroupName is None: LOG.error(_LE( "Failed to add %(volumeName)s " @@ -1603,7 +1603,7 @@ class EMCVMAXCommon(object): :returns: string the configuration file """ try: - extraSpecs, configurationFile = ( + self.extraSpecs, configurationFile = ( self._set_config_file_and_get_extra_specs( volume, volumeTypeId)) @@ -1621,12 +1621,12 @@ class EMCVMAXCommon(object): isV3 = self.utils.isArrayV3(self.conn, arrayName) if isV3: - extraSpecs = self._set_v3_extra_specs( - extraSpecs, configurationFile, arrayName) + self.extraSpecs = self._set_v3_extra_specs( + configurationFile, arrayName) else: # V2 extra specs. - extraSpecs = self._set_v2_extra_specs( - extraSpecs, configurationFile, arrayName) + self.extraSpecs = self._set_v2_extra_specs( + configurationFile, arrayName) except Exception: exceptionMessage = (_( "Unable to get configuration information necessary to create " @@ -1636,20 +1636,19 @@ class EMCVMAXCommon(object): "/etc/cinder/cinder_emc_config_.xml.")) raise exception.VolumeBackendAPIException(data=exceptionMessage) - return extraSpecs + return self.extraSpecs - def _get_pool_and_storage_system(self, extraSpecs): + def _get_pool_and_storage_system(self): """Given the extra specs get the pool and storage system name. - :params extraSpecs: the extra spec tuple :returns: poolInstanceName The pool instance name :returns: String the storage system name """ try: - array = extraSpecs[ARRAY] + array = self.extraSpecs[ARRAY] poolInstanceName, storageSystemStr = self._find_pool_in_array( - array, extraSpecs[POOL], extraSpecs[ISV3]) + array, self.extraSpecs[POOL], self.extraSpecs[ISV3]) except Exception: exceptionMessage = (_( "You must supply an array in your EMC configuration file.")) @@ -1664,7 +1663,7 @@ class EMCVMAXCommon(object): return poolInstanceName, storageSystemStr - def _populate_masking_dict(self, volume, connector, extraSpecs): + def _populate_masking_dict(self, volume, connector): """Get all the names of the maskingView and subComponents. :param volume: the volume object @@ -1674,14 +1673,14 @@ class EMCVMAXCommon(object): """ maskingViewDict = {} hostName = connector['host'] - poolName = extraSpecs[POOL] - isV3 = extraSpecs[ISV3] + poolName = self.extraSpecs[POOL] + isV3 = self.extraSpecs[ISV3] maskingViewDict['isV3'] = isV3 protocol = self.utils.get_short_protocol_type(self.protocol) shortHostName = self.utils.get_host_short_name(hostName) if isV3: - slo = extraSpecs[SLO] - workload = extraSpecs[WORKLOAD] + slo = self.extraSpecs[SLO] + workload = self.extraSpecs[WORKLOAD] maskingViewDict['slo'] = slo maskingViewDict['workload'] = workload maskingViewDict['pool'] = poolName @@ -1747,7 +1746,7 @@ class EMCVMAXCommon(object): self.fast.add_volume_to_default_storage_group_for_fast_policy( self.conn, controllerConfigurationService, volumeInstance, - volumeName, fastPolicyName) + volumeName, fastPolicyName, self.extraSpecs) foundStorageGroupInstanceName = ( self.utils.get_storage_group_from_volume( self.conn, volumeInstance.path)) @@ -1771,7 +1770,7 @@ class EMCVMAXCommon(object): LOG.error(errorMessage) self.provision.delete_volume_from_pool( self.conn, storageConfigService, volumeInstance.path, - volumeName) + volumeName, self.extraSpecs) raise exception.VolumeBackendAPIException(data=errorMessage) def _create_and_get_unbound_volume( @@ -1819,7 +1818,7 @@ class EMCVMAXCommon(object): volumeDict, _ = ( self.provision.create_volume_from_pool( self.conn, storageConfigService, volumeName, poolInstanceName, - volumeSize)) + volumeSize, self.extraSpecs)) volumeInstance = self.utils.find_volume_instance( self.conn, volumeDict, volumeName) return volumeInstance @@ -1841,7 +1840,7 @@ class EMCVMAXCommon(object): self.provision.unbind_volume_from_storage_pool( conn, storageConfigService, poolInstanceName, volumeInstanceName, - volumeName)) + volumeName, self.extraSpecs)) volumeDict = self.provision.get_volume_dict_from_job(conn, job['Job']) volumeInstance = self.utils.find_volume_instance( self.conn, volumeDict, volumeName) @@ -1868,11 +1867,12 @@ class EMCVMAXCommon(object): if 'True' in isComposite: rc, job = self.provision.modify_composite_volume( conn, elementCompositionServiceInstanceName, - volumeInstance.path, appendVolumeInstanceName) + volumeInstance.path, appendVolumeInstanceName, self.extraSpecs) elif 'False' in isComposite: rc, job = self.provision.create_new_composite_volume( conn, elementCompositionServiceInstanceName, - volumeInstance.path, appendVolumeInstanceName, compositeType) + volumeInstance.path, appendVolumeInstanceName, compositeType, + self.extraSpecs) else: LOG.error(_LE( "Unable to determine whether %(volumeName)s is " @@ -1906,7 +1906,7 @@ class EMCVMAXCommon(object): defaultStorageGroupInstanceName = ( self.fast.get_or_create_default_storage_group( self.conn, controllerConfigService, fastPolicyName, - volumeInstance)) + volumeInstance, self.extraSpecs)) return defaultStorageGroupInstanceName def _create_cloned_volume( @@ -1917,7 +1917,7 @@ class EMCVMAXCommon(object): :param sourceVolume: source of the clone volume :returns: cloneDict the cloned volume dictionary """ - extraSpecs = self._initial_setup(cloneVolume) + self.extraSpecs = self._initial_setup(cloneVolume) sourceName = sourceVolume['name'] cloneName = cloneVolume['name'] @@ -1948,7 +1948,7 @@ class EMCVMAXCommon(object): 'elementname': cloneName, 'sourceelement': sourceInstance.path}) - if extraSpecs[ISV3]: + if self.extraSpecs[ISV3]: rc, cloneDict = self._create_replica_v3(repServiceInstanceName, cloneVolume, sourceVolume, @@ -1959,8 +1959,7 @@ class EMCVMAXCommon(object): cloneVolume, sourceVolume, sourceInstance, - isSnapshot, - extraSpecs) + isSnapshot) LOG.debug("Leaving _create_cloned_volume: Volume: " "%(cloneName)s Source Volume: %(sourceName)s " "Return code: %(rc)lu.", @@ -2024,7 +2023,7 @@ class EMCVMAXCommon(object): rc = -1 errorRet = (rc, volumeName) - extraSpecs = self._initial_setup(volume) + self.extraSpecs = self._initial_setup(volume) self.conn = self._get_ecom_connection() volumeInstance = self._find_lun(volume) @@ -2040,16 +2039,17 @@ class EMCVMAXCommon(object): deviceId = volumeInstance['DeviceID'] - if extraSpecs[ISV3]: + if self.extraSpecs[ISV3]: storageGroupName = self.utils.get_v3_storage_group_name( - extraSpecs[POOL], extraSpecs[SLO], extraSpecs[WORKLOAD]) + self.extraSpecs[POOL], self.extraSpecs[SLO], + self.extraSpecs[WORKLOAD]) rc = self._delete_from_pool_v3( storageConfigService, volumeInstance, volumeName, deviceId, storageGroupName) else: rc = self._delete_from_pool(storageConfigService, volumeInstance, volumeName, deviceId, - extraSpecs[FASTPOLICY]) + self.extraSpecs[FASTPOLICY]) return (rc, volumeName) def _remove_device_from_storage_group( @@ -2077,7 +2077,7 @@ class EMCVMAXCommon(object): self.provision.remove_device_from_storage_group( self.conn, controllerConfigurationService, storageGroupInstanceName, - volumeInstanceName, volumeName) + volumeInstanceName, volumeName, self.extraSpecs) def _find_lunmasking_scsi_protocol_controller(self, storageSystemName, connector): @@ -2209,10 +2209,10 @@ class EMCVMAXCommon(object): LOG.info(_LI("Delete Snapshot: %(snapshot)s."), {'snapshot': snapshotname}) - extraSpecs = self._initial_setup(snapshot) + self.extraSpecs = self._initial_setup(snapshot) self.conn = self._get_ecom_connection() - if not extraSpecs[ISV3]: + if not self.extraSpecs[ISV3]: snapshotInstance = self._find_lun(snapshot) storageSystem = snapshotInstance['SystemName'] @@ -2245,7 +2245,7 @@ class EMCVMAXCommon(object): 'syncName': str(syncName)}) self.provision.delete_clone_relationship( - self.conn, repservice, syncName, True) + self.conn, repservice, syncName, self.extraSpecs, True) # Delete the target device. self._delete_volume(snapshot) @@ -2265,10 +2265,10 @@ class EMCVMAXCommon(object): cgName = self.utils.truncate_string(group['id'], 8) - extraSpecs = self._initial_setup(None, volumeTypeId) + self.extraSpecs = self._initial_setup(None, volumeTypeId) _, storageSystem = ( - self._get_pool_and_storage_system(extraSpecs)) + self._get_pool_and_storage_system()) self.conn = self._get_ecom_connection() @@ -2277,7 +2277,7 @@ class EMCVMAXCommon(object): replicationService = self.utils.find_replication_service( self.conn, storageSystem) self.provision.create_consistency_group( - self.conn, replicationService, cgName) + self.conn, replicationService, cgName, self.extraSpecs) except Exception as ex: LOG.error(_LE("Exception: %(ex)s"), {'ex': ex}) exceptionMessage = (_("Failed to create consistency group:" @@ -2304,10 +2304,10 @@ class EMCVMAXCommon(object): modelUpdate['status'] = group['status'] volumeTypeId = group['volume_type_id'].replace(",", "") - extraSpecs = self._initial_setup(None, volumeTypeId) + self.extraSpecs = self._initial_setup(None, volumeTypeId) _, storageSystem = ( - self._get_pool_and_storage_system(extraSpecs)) + self._get_pool_and_storage_system()) try: replicationService = self.utils.find_replication_service( @@ -2329,13 +2329,14 @@ class EMCVMAXCommon(object): self.provision.delete_consistency_group(self.conn, replicationService, - cgInstanceName, cgName) + cgInstanceName, cgName, + self.extraSpecs) # Do a bulk delete, a lot faster than single deletes. if memberInstanceNames: volumes, modelUpdate = self._do_bulk_delete( storageSystem, memberInstanceNames, storageConfigservice, - volumes, modelUpdate, extraSpecs[ISV3]) + volumes, modelUpdate, self.extraSpecs[ISV3]) except Exception as ex: LOG.error(_LE("Exception: %s"), ex) @@ -2370,11 +2371,11 @@ class EMCVMAXCommon(object): if isV3: self.provisionv3.delete_volume_from_pool( self.conn, storageConfigservice, - memberInstanceNames, None) + memberInstanceNames, None, self.extraSpecs) else: self.provision.delete_volume_from_pool( self.conn, storageConfigservice, - memberInstanceNames, None) + memberInstanceNames, None, self.extraSpecs) for volumeRef in volumes: volumeRef['status'] = 'deleted' except Exception: @@ -2406,11 +2407,11 @@ class EMCVMAXCommon(object): modelUpdate = {'status': 'available'} volumeTypeId = consistencyGroup['volume_type_id'].replace(",", "") - extraSpecs = self._initial_setup(None, volumeTypeId) + self.extraSpecs = self._initial_setup(None, volumeTypeId) self.conn = self._get_ecom_connection() _, storageSystem = ( - self._get_pool_and_storage_system(extraSpecs)) + self._get_pool_and_storage_system()) try: replicationService = self.utils.find_replication_service( @@ -2429,7 +2430,7 @@ class EMCVMAXCommon(object): # Create the target consistency group. targetCgName = self.utils.truncate_string(cgsnapshot['id'], 8) self.provision.create_consistency_group( - self.conn, replicationService, targetCgName) + self.conn, replicationService, targetCgName, self.extraSpecs) targetCgInstanceName = self._find_consistency_group( replicationService, targetCgName) LOG.info(_LI("Create target consistency group %(targetCg)s."), @@ -2446,16 +2447,14 @@ class EMCVMAXCommon(object): volume = {'size': int(self.utils.convert_bits_to_gbs( volumeSizeInbits))} - if extraSpecs[ISV3]: + if self.extraSpecs[ISV3]: _, volumeDict, _ = ( self._create_v3_volume( - volume, extraSpecs, - targetVolumeName, volumeSizeInbits)) + volume, targetVolumeName, volumeSizeInbits)) else: _, volumeDict, _ = ( self._create_composite_volume( - volume, extraSpecs, - targetVolumeName, volumeSizeInbits)) + volume, targetVolumeName, volumeSizeInbits)) targetVolumeInstance = self.utils.find_volume_instance( self.conn, volumeDict, targetVolumeName) LOG.debug("Create target volume for member volume " @@ -2468,18 +2467,19 @@ class EMCVMAXCommon(object): targetCgInstanceName, targetVolumeInstance.path, targetCgName, - targetVolumeName) + targetVolumeName, + self.extraSpecs) # Less than 5 characters relationship name. relationName = self.utils.truncate_string(cgsnapshot['id'], 5) - if extraSpecs[ISV3]: - self.provisionv3.create_group_replica( - self.conn, replicationService, cgInstanceName, - targetCgInstanceName, relationName) + if self.extraSpecs[ISV3]: + self.provisionv3.create_group_replica( + self.conn, replicationService, cgInstanceName, + targetCgInstanceName, relationName, self.extraSpecs) else: - self.provision.create_group_replica( - self.conn, replicationService, cgInstanceName, - targetCgInstanceName, relationName) + self.provision.create_group_replica( + self.conn, replicationService, cgInstanceName, + targetCgInstanceName, relationName, self.extraSpecs) # Break the replica group relationship. rgSyncInstanceName = self.utils.find_group_sync_rg_by_target( self.conn, storageSystem, targetCgInstanceName, True) @@ -2492,14 +2492,16 @@ class EMCVMAXCommon(object): storageSystem) raise exception.VolumeBackendAPIException( data=exception_message) - if extraSpecs[ISV3]: + if self.extraSpecs[ISV3]: # Operation 7: dissolve for snapVx. operation = self.utils.get_num(9, '16') self.provisionv3.break_replication_relationship( - self.conn, repservice, rgSyncInstanceName, operation) + self.conn, repservice, rgSyncInstanceName, operation, + self.extraSpecs) else: self.provision.delete_clone_relationship(self.conn, repservice, - rgSyncInstanceName) + rgSyncInstanceName, + self.extraSpecs) except Exception as ex: modelUpdate['status'] = 'error' @@ -2537,16 +2539,16 @@ class EMCVMAXCommon(object): modelUpdate = {'status': 'deleted'} volumeTypeId = consistencyGroup['volume_type_id'].replace(",", "") - extraSpecs = self._initial_setup(None, volumeTypeId) + self.extraSpecs = self._initial_setup(None, volumeTypeId) self.conn = self._get_ecom_connection() _, storageSystem = ( - self._get_pool_and_storage_system(extraSpecs)) + self._get_pool_and_storage_system()) try: targetCgName = self.utils.truncate_string(cgsnapshot['id'], 8) modelUpdate, snapshots = self._delete_cg_and_members( - storageSystem, extraSpecs, targetCgName, modelUpdate, + storageSystem, targetCgName, modelUpdate, snapshots) except Exception as ex: modelUpdate['status'] = 'error_deleting' @@ -2596,7 +2598,7 @@ class EMCVMAXCommon(object): return memberInstanceNames def _create_composite_volume( - self, volume, extraSpecs, volumeName, volumeSize): + self, volume, volumeName, volumeSize): """Create a composite volume (V2). :param volume: the volume object @@ -2606,7 +2608,8 @@ class EMCVMAXCommon(object): :returns: """ memberCount, errorDesc = self.utils.determine_member_count( - volume['size'], extraSpecs[MEMBERCOUNT], extraSpecs[COMPOSITETYPE]) + volume['size'], self.extraSpecs[MEMBERCOUNT], + self.extraSpecs[COMPOSITETYPE]) if errorDesc is not None: exceptionMessage = (_("The striped meta count of %(memberCount)s " "is too small for volume: %(volumeName)s " @@ -2618,7 +2621,7 @@ class EMCVMAXCommon(object): raise exception.VolumeBackendAPIException(data=exceptionMessage) poolInstanceName, storageSystemName = ( - self._get_pool_and_storage_system(extraSpecs)) + self._get_pool_and_storage_system()) LOG.debug("Create Volume: %(volume)s Pool: %(pool)s " "Storage System: %(storageSystem)s " @@ -2637,79 +2640,79 @@ class EMCVMAXCommon(object): # If FAST is intended to be used we must first check that the pool # is associated with the correct storage tier. - if extraSpecs[FASTPOLICY] is not None: + if self.extraSpecs[FASTPOLICY] is not None: foundPoolInstanceName = self.fast.get_pool_associated_to_policy( - self.conn, extraSpecs[FASTPOLICY], extraSpecs[ARRAY], + self.conn, self.extraSpecs[FASTPOLICY], self.extraSpecs[ARRAY], storageConfigService, poolInstanceName) if foundPoolInstanceName is None: exceptionMessage = (_("Pool: %(poolName)s. " "is not associated to storage tier for " "fast policy %(fastPolicy)s.") - % {'poolName': extraSpecs[POOL], - 'fastPolicy': extraSpecs[FASTPOLICY]}) + % {'poolName': self.extraSpecs[POOL], + 'fastPolicy': + self.extraSpecs[FASTPOLICY]}) LOG.error(exceptionMessage) raise exception.VolumeBackendAPIException( data=exceptionMessage) compositeType = self.utils.get_composite_type( - extraSpecs[COMPOSITETYPE]) + self.extraSpecs[COMPOSITETYPE]) volumeDict, rc = self.provision.create_composite_volume( self.conn, elementCompositionService, volumeSize, volumeName, - poolInstanceName, compositeType, memberCount) + poolInstanceName, compositeType, memberCount, self.extraSpecs) # Now that we have already checked that the pool is associated with # the correct storage tier and the volume was successfully created # add the volume to the default storage group created for # volumes in pools associated with this fast policy. - if extraSpecs[FASTPOLICY]: + if self.extraSpecs[FASTPOLICY]: LOG.info(_LI( "Adding volume: %(volumeName)s to default storage group" " for FAST policy: %(fastPolicyName)s."), {'volumeName': volumeName, - 'fastPolicyName': extraSpecs[FASTPOLICY]}) + 'fastPolicyName': self.extraSpecs[FASTPOLICY]}) defaultStorageGroupInstanceName = ( self._get_or_create_default_storage_group( self.conn, storageSystemName, volumeDict, - volumeName, extraSpecs[FASTPOLICY])) + volumeName, self.extraSpecs[FASTPOLICY])) if not defaultStorageGroupInstanceName: exceptionMessage = (_( "Unable to create or get default storage group for " "FAST policy: %(fastPolicyName)s.") - % {'fastPolicyName': extraSpecs[FASTPOLICY]}) + % {'fastPolicyName': self.extraSpecs[FASTPOLICY]}) LOG.error(exceptionMessage) raise exception.VolumeBackendAPIException( data=exceptionMessage) self._add_volume_to_default_storage_group_on_create( volumeDict, volumeName, storageConfigService, - storageSystemName, extraSpecs[FASTPOLICY]) + storageSystemName, self.extraSpecs[FASTPOLICY]) return rc, volumeDict, storageSystemName def _create_v3_volume( - self, volume, extraSpecs, volumeName, volumeSize): - """create a volume (V3). + self, volume, volumeName, volumeSize): + """Create a volume (V3). :param volume: the volume object - :param extraSpecs: - :param volumeName: - :param volumeSize: + :param volumeName: the volume name + :param volumeSize: the volume size :returns: """ isValidSLO, isValidWorkload = self.utils.verify_slo_workload( - extraSpecs[SLO], extraSpecs[WORKLOAD]) + self.extraSpecs[SLO], self.extraSpecs[WORKLOAD]) if not isValidSLO or not isValidWorkload: exceptionMessage = (_( "Either SLO: %(slo)s or workload %(workload)s is invalid. " "Examine previous error statement for valid values.") - % {'slo': extraSpecs[SLO], - 'workload': extraSpecs[WORKLOAD]}) + % {'slo': self.extraSpecs[SLO], + 'workload': self.extraSpecs[WORKLOAD]}) LOG.error(exceptionMessage) raise exception.VolumeBackendAPIException(data=exceptionMessage) poolInstanceName, storageSystemName = ( - self._get_pool_and_storage_system(extraSpecs)) + self._get_pool_and_storage_system()) LOG.debug("Create Volume: %(volume)s Pool: %(pool)s " "Storage System: %(storageSystem)s " @@ -2726,7 +2729,8 @@ class EMCVMAXCommon(object): maximumVolumeSize, minimumVolumeSize = ( self.provisionv3.get_volume_range( self.conn, storageConfigService, poolInstanceName, - extraSpecs[SLO], extraSpecs[WORKLOAD])) + self.extraSpecs[SLO], self.extraSpecs[WORKLOAD], + self.extraSpecs)) if not self.utils.is_in_range( volumeSize, maximumVolumeSize, minimumVolumeSize): LOG.warn(_LW( @@ -2739,19 +2743,19 @@ class EMCVMAXCommon(object): 'volumeSize': volumeSize, 'minimumVolumeSize': minimumVolumeSize, 'maximumVolumeSize': maximumVolumeSize, - 'slo': extraSpecs[SLO], - 'workload': extraSpecs[WORKLOAD] + 'slo': self.extraSpecs[SLO], + 'workload': self.extraSpecs[WORKLOAD] }) # A volume created without specifying a storage group during # creation time is allocated from the default SRP pool and # assigned the optimized SLO. sgInstanceName = self._get_or_create_storage_group_v3( - extraSpecs[POOL], extraSpecs[SLO], extraSpecs[WORKLOAD], - storageSystemName) + self.extraSpecs[POOL], self.extraSpecs[SLO], + self.extraSpecs[WORKLOAD], storageSystemName) volumeDict, rc = self.provisionv3.create_volume_from_sg( self.conn, storageConfigService, volumeName, - sgInstanceName, volumeSize) + sgInstanceName, volumeSize, self.extraSpecs) return rc, volumeDict, storageSystemName @@ -2775,7 +2779,7 @@ class EMCVMAXCommon(object): if sgInstanceName is None: sgInstanceName = self.provisionv3.create_storage_group_v3( self.conn, controllerConfigService, storageGroupName, - poolName, slo, workload) + poolName, slo, workload, self.extraSpecs) return sgInstanceName @@ -2849,8 +2853,7 @@ class EMCVMAXCommon(object): return rc, modifiedVolumeDict def _slo_workload_migration(self, volumeInstance, volume, host, - volumeName, volumeStatus, - extraSpecs, newType): + volumeName, volumeStatus, newType): """Migrate from SLO/Workload combination to another (V3). :param volumeInstance: the volume instance @@ -2858,15 +2861,14 @@ class EMCVMAXCommon(object): :param host: the host object :param volumeName: the name of the volume :param volumeStatus: the volume status - :param extraSpecs: the extra specs dict :param newType: :returns: boolean """ volumeInstanceName = volumeInstance.path isValid, targetSlo, targetWorkload = ( self._is_valid_for_storage_assisted_migration_v3( - volumeInstanceName, host, extraSpecs[ARRAY], - extraSpecs[POOL], volumeName, volumeStatus)) + volumeInstanceName, host, self.extraSpecs[ARRAY], + self.extraSpecs[POOL], volumeName, volumeStatus)) storageSystemName = volumeInstance['SystemName'] @@ -2884,7 +2886,7 @@ class EMCVMAXCommon(object): 'sourceHost': volume['host'], 'targetHost': host['host']}) return self._migrate_volume_v3( - volume, volumeInstance, extraSpecs[POOL], targetSlo, + volume, volumeInstance, self.extraSpecs[POOL], targetSlo, targetWorkload, storageSystemName, newType) return False @@ -2926,7 +2928,7 @@ class EMCVMAXCommon(object): controllerConfigService, foundStorageGroupInstanceName, volumeInstance.path, - volumeName) + volumeName, self.extraSpecs) # Check that it has been removed. sgFromVolRemovedInstanceName = ( self.utils.wrap_get_storage_group_from_volume( @@ -2952,7 +2954,7 @@ class EMCVMAXCommon(object): self.masking.add_volume_to_storage_group( self.conn, controllerConfigService, targetSgInstanceName, - volumeInstance, volumeName, storageGroupName) + volumeInstance, volumeName, storageGroupName, self.extraSpecs) # Check that it has been added. sgFromVolAddedInstanceName = ( self.utils.get_storage_group_from_volume( @@ -3077,18 +3079,17 @@ class EMCVMAXCommon(object): return location_info, total_capacity_gb, free_capacity_gb - def _set_v2_extra_specs(self, extraSpecs, configurationFile, arrayName): + def _set_v2_extra_specs(self, configurationFile, arrayName): """Set the VMAX V2 extra specs. - :param extraSpecs: the extraSpecs (input) :param configurationFile: the EMC configuration file :param arrayName: the array serial number :returns: extraSpecs (out) """ try: - stripedMetaCount = extraSpecs[STRIPECOUNT] - extraSpecs[MEMBERCOUNT] = stripedMetaCount - extraSpecs[COMPOSITETYPE] = STRIPED + stripedMetaCount = self.extraSpecs[STRIPECOUNT] + self.extraSpecs[MEMBERCOUNT] = stripedMetaCount + self.extraSpecs[COMPOSITETYPE] = STRIPED LOG.debug( "There are: %(stripedMetaCount)s striped metas in " @@ -3096,8 +3097,8 @@ class EMCVMAXCommon(object): {'stripedMetaCount': stripedMetaCount}) except KeyError: memberCount = '1' - extraSpecs[MEMBERCOUNT] = memberCount - extraSpecs[COMPOSITETYPE] = CONCATENATED + self.extraSpecs[MEMBERCOUNT] = memberCount + self.extraSpecs[COMPOSITETYPE] = CONCATENATED LOG.debug("StripedMetaCount is not in the extra specs.") poolName = self.utils.parse_pool_name_from_file(configurationFile) @@ -3118,24 +3119,25 @@ class EMCVMAXCommon(object): LOG.debug("The fast policy name is: %(fastPolicyName)s.", {'fastPolicyName': fastPolicyName}) - extraSpecs[POOL] = poolName - extraSpecs[ARRAY] = arrayName - extraSpecs[FASTPOLICY] = fastPolicyName - extraSpecs[ISV3] = False + self.extraSpecs[POOL] = poolName + self.extraSpecs[ARRAY] = arrayName + self.extraSpecs[FASTPOLICY] = fastPolicyName + self.extraSpecs[ISV3] = False + self.extraSpecs = self._get_job_extra_specs(configurationFile) LOG.debug("Pool is: %(pool)s " "Array is: %(array)s " "FastPolicy is: %(fastPolicy)s " "CompositeType is: %(compositeType)s " "MemberCount is: %(memberCount)s.", - {'pool': extraSpecs[POOL], - 'array': extraSpecs[ARRAY], - 'fastPolicy': extraSpecs[FASTPOLICY], - 'compositeType': extraSpecs[COMPOSITETYPE], - 'memberCount': extraSpecs[MEMBERCOUNT]}) - return extraSpecs - - def _set_v3_extra_specs(self, extraSpecs, configurationFile, arrayName): + {'pool': self.extraSpecs[POOL], + 'array': self.extraSpecs[ARRAY], + 'fastPolicy': self.extraSpecs[FASTPOLICY], + 'compositeType': self.extraSpecs[COMPOSITETYPE], + 'memberCount': self.extraSpecs[MEMBERCOUNT]}) + return self.extraSpecs + + def _set_v3_extra_specs(self, configurationFile, arrayName): """Set the VMAX V3 extra specs. If SLO or workload are not specified then the default @@ -3147,23 +3149,48 @@ class EMCVMAXCommon(object): :param arrayName: the array serial number :returns: extraSpecs (out) """ - extraSpecs[SLO] = self.utils.parse_slo_from_file(configurationFile) - extraSpecs[WORKLOAD] = self.utils.parse_workload_from_file( + self.extraSpecs[SLO] = self.utils.parse_slo_from_file( + configurationFile) + self.extraSpecs[WORKLOAD] = self.utils.parse_workload_from_file( configurationFile) - extraSpecs[POOL] = self.utils.parse_pool_name_from_file( + self.extraSpecs[POOL] = self.utils.parse_pool_name_from_file( configurationFile) - extraSpecs[ARRAY] = arrayName - extraSpecs[ISV3] = True + self.extraSpecs[ARRAY] = arrayName + self.extraSpecs[ISV3] = True + self.extraSpecs = self._get_job_extra_specs(configurationFile) LOG.debug("Pool is: %(pool)s " "Array is: %(array)s " "SLO is: %(slo)s " "Workload is: %(workload)s.", - {'pool': extraSpecs[POOL], - 'array': extraSpecs[ARRAY], - 'slo': extraSpecs[SLO], - 'workload': extraSpecs[WORKLOAD]}) - return extraSpecs + {'pool': self.extraSpecs[POOL], + 'array': self.extraSpecs[ARRAY], + 'slo': self.extraSpecs[SLO], + 'workload': self.extraSpecs[WORKLOAD]}) + return self.extraSpecs + + def _get_job_extra_specs(self, configurationFile): + """Get user defined extra specs around job intervals and retries. + + :param configurationFile: the EMC configuration file + :param arrayName: the array serial number + :returns: extraSpecs (out) + """ + intervalInSecs = self.utils.parse_interval_from_file( + configurationFile) + if intervalInSecs is not None: + LOG.debug("The user defined interval is : %(intervalInSecs)s.", + {'intervalInSecs': intervalInSecs}) + self.extraSpecs[INTERVAL] = intervalInSecs + + retries = self.utils.parse_retries_from_file( + configurationFile) + if retries is not None: + LOG.debug("The user defined retries is : %(retries)s.", + {'retries': retries}) + self.extraSpecs[RETRIES] = retries + + return self.extraSpecs def _delete_from_pool(self, storageConfigService, volumeInstance, volumeName, deviceId, fastPolicyName): @@ -3184,7 +3211,8 @@ class EMCVMAXCommon(object): defaultStorageGroupInstanceName = ( self.masking.remove_device_from_default_storage_group( self.conn, controllerConfigurationService, - volumeInstance.path, volumeName, fastPolicyName)) + volumeInstance.path, volumeName, fastPolicyName, + self.extraSpecs)) if defaultStorageGroupInstanceName is None: LOG.warn(_LW( "The volume: %(volumename)s. was not first part of the " @@ -3213,7 +3241,7 @@ class EMCVMAXCommon(object): try: rc = self.provision.delete_volume_from_pool( self.conn, storageConfigService, volumeInstance.path, - volumeName) + volumeName, self.extraSpecs) except Exception as e: # If we cannot successfully delete the volume then we want to @@ -3225,7 +3253,8 @@ class EMCVMAXCommon(object): self.fast .add_volume_to_default_storage_group_for_fast_policy( self.conn, controllerConfigurationService, - volumeInstance, volumeName, fastPolicyName)) + volumeInstance, volumeName, fastPolicyName, + self.extraSpecs)) if assocDefaultStorageGroupName is None: LOG.error(_LE( "Failed to Roll back to re-add volume %(volumeName)s " @@ -3264,7 +3293,7 @@ class EMCVMAXCommon(object): # extra logic for case when volume is the last member. sgFromVolInstanceName = self.masking.remove_and_reset_members( self.conn, controllerConfigurationService, volumeInstance, - None, volumeName, True, None, 'noReset') + volumeName, self.extraSpecs, None, 'noReset') LOG.debug("Delete Volume: %(name)s Method: EMCReturnToStoragePool " "ConfigServic: %(service)s TheElement: %(vol_instance)s " @@ -3276,7 +3305,7 @@ class EMCVMAXCommon(object): try: rc = self.provisionv3.delete_volume_from_pool( self.conn, storageConfigService, volumeInstance.path, - volumeName) + volumeName, self.extraSpecs) except Exception as e: # If we cannot successfully delete the volume, then we want to @@ -3298,7 +3327,7 @@ class EMCVMAXCommon(object): self.masking.add_volume_to_storage_group( self.conn, controllerConfigurationService, storageGroupInstanceName, volumeInstance, volumeName, - storageGroupName) + storageGroupName, self.extraSpecs) LOG.error(_LE("Exception: %s."), e) errorMessage = (_("Failed to delete volume %(volumeName)s.") % @@ -3309,8 +3338,7 @@ class EMCVMAXCommon(object): return rc def _create_clone_v2(self, repServiceInstanceName, cloneVolume, - sourceVolume, sourceInstance, isSnapshot, - extraSpecs): + sourceVolume, sourceInstance, isSnapshot): """Create a clone (v2). :param repServiceInstanceName: the replication service @@ -3318,7 +3346,6 @@ class EMCVMAXCommon(object): :param sourceVolume: the source volume object :param sourceInstance: the device ID of the volume :param isSnapshot: check to see if it is a snapshot - :param fastPolicyName: the FAST policy name(if it exists) :returns: rc """ # Check if the source volume contains any meta devices. @@ -3328,7 +3355,7 @@ class EMCVMAXCommon(object): if metaHeadInstanceName is None: # Simple volume. return self._create_v2_replica_and_delete_clone_relationship( repServiceInstanceName, cloneVolume, sourceVolume, - sourceInstance, None, isSnapshot, extraSpecs) + sourceInstance, None, isSnapshot) else: # Composite volume with meta device members. # Check if the meta members capacity. metaMemberInstanceNames = ( @@ -3342,7 +3369,7 @@ class EMCVMAXCommon(object): LOG.debug("Meta volume all of the same size.") return self._create_v2_replica_and_delete_clone_relationship( repServiceInstanceName, cloneVolume, sourceVolume, - sourceInstance, None, isSnapshot, extraSpecs) + sourceInstance, None, isSnapshot) LOG.debug("Meta volumes are of different sizes, " "%d different sizes.", len(set(volumeCapacities))) @@ -3355,8 +3382,7 @@ class EMCVMAXCommon(object): volumeSizeInbits))} rc, baseVolumeDict, storageSystemName = ( self._create_composite_volume( - volume, extraSpecs, - baseVolumeName, volumeSizeInbits)) + volume, baseVolumeName, volumeSizeInbits)) baseTargetVolumeInstance = self.utils.find_volume_instance( self.conn, baseVolumeDict, baseVolumeName) LOG.debug("Base target volume %(targetVol)s created. " @@ -3389,7 +3415,7 @@ class EMCVMAXCommon(object): self.utils.find_element_composition_service( self.conn, storageSystemName)) compositeType = self.utils.get_composite_type( - extraSpecs[COMPOSITETYPE]) + self.extraSpecs[COMPOSITETYPE]) rc, modifiedVolumeDict = ( self._modify_and_get_composite_volume_instance( self.conn, @@ -3410,12 +3436,11 @@ class EMCVMAXCommon(object): LOG.debug("Create V2 replica for meta members of different sizes.") return self._create_v2_replica_and_delete_clone_relationship( repServiceInstanceName, cloneVolume, sourceVolume, - sourceInstance, baseTargetVolumeInstance, isSnapshot, - extraSpecs) + sourceInstance, baseTargetVolumeInstance, isSnapshot) def _create_v2_replica_and_delete_clone_relationship( self, repServiceInstanceName, cloneVolume, sourceVolume, - sourceInstance, targetInstance, isSnapshot, extraSpecs): + sourceInstance, targetInstance, isSnapshot=False): """Create clone and delete relationship (v2). :param repServiceInstanceName: the replication service @@ -3424,18 +3449,17 @@ class EMCVMAXCommon(object): :param sourceInstance: the source volume instance :param targetInstance: the target volume instance :param isSnapshot: check to see if it is a snapshot - :param extraSpecs: additional information :returns: rc """ sourceName = sourceVolume['name'] cloneName = cloneVolume['name'] rc, job = self.provision.create_element_replica( self.conn, repServiceInstanceName, cloneName, sourceName, - sourceInstance, targetInstance) + sourceInstance, targetInstance, self.extraSpecs) cloneDict = self.provision.get_volume_dict_from_job( self.conn, job['Job']) - fastPolicyName = extraSpecs[FASTPOLICY] + fastPolicyName = self.extraSpecs[FASTPOLICY] if isSnapshot: if fastPolicyName is not None: storageSystemName = sourceInstance['SystemName'] @@ -3455,7 +3479,8 @@ class EMCVMAXCommon(object): # Remove the Clone relationship so it can be used as a regular lun. # 8 - Detach operation. rc, job = self.provision.delete_clone_relationship( - self.conn, repServiceInstanceName, syncInstanceName) + self.conn, repServiceInstanceName, syncInstanceName, + self.extraSpecs) if fastPolicyName is not None: self._add_clone_to_default_storage_group( fastPolicyName, storageSystemName, cloneDict, cloneName) @@ -3528,7 +3553,7 @@ class EMCVMAXCommon(object): syncType = self.utils.get_num(8, '16') # Default syncType 8: clone. # Create target volume - extraSpecs = self._initial_setup(cloneVolume) + self.extraSpecs = self._initial_setup(cloneVolume) numOfBlocks = sourceInstance['NumberOfBlocks'] blockSize = sourceInstance['BlockSize'] @@ -3538,7 +3563,7 @@ class EMCVMAXCommon(object): int(self.utils.convert_bits_to_gbs(volumeSizeInbits))} _, volumeDict, storageSystemName = ( self._create_v3_volume( - volume, extraSpecs, cloneName, volumeSizeInbits)) + volume, cloneName, volumeSizeInbits)) targetInstance = self.utils.find_volume_instance( self.conn, volumeDict, cloneName) LOG.debug("Create replica target volume " @@ -3553,7 +3578,7 @@ class EMCVMAXCommon(object): _, job = ( self.provisionv3.create_element_replica( self.conn, repServiceInstanceName, cloneName, syncType, - sourceInstance, targetInstance)) + sourceInstance, self.extraSpecs, targetInstance)) cloneDict = self.provisionv3.get_volume_dict_from_job( self.conn, job['Job']) @@ -3574,11 +3599,11 @@ class EMCVMAXCommon(object): rc, job = self.provisionv3.break_replication_relationship( self.conn, repServiceInstanceName, syncInstanceName, - operation) + operation, self.extraSpecs) return rc, cloneDict def _delete_cg_and_members( - self, storageSystem, extraSpecs, cgName, modelUpdate, volumes): + self, storageSystem, cgName, modelUpdate, volumes): """Helper function to delete a consistency group and its member volumes. :param storageSystem: storage system @@ -3601,7 +3626,8 @@ class EMCVMAXCommon(object): cgInstanceName) self.provision.delete_consistency_group( - self.conn, replicationService, cgInstanceName, cgName) + self.conn, replicationService, cgInstanceName, cgName, + self.extraSpecs) if memberInstanceNames: try: @@ -3617,14 +3643,14 @@ class EMCVMAXCommon(object): {'cg': cgInstanceName, 'numVols': len(memberInstanceNames), 'memVols': memberInstanceNames}) - if extraSpecs[ISV3]: + if self.extraSpecs[ISV3]: self.provisionv3.delete_volume_from_pool( self.conn, storageConfigservice, - memberInstanceNames, None) + memberInstanceNames, None, self.extraSpecs) else: self.provision.delete_volume_from_pool( self.conn, storageConfigservice, - memberInstanceNames, None) + memberInstanceNames, None, self.extraSpecs) for volumeRef in volumes: volumeRef['status'] = 'deleted' except Exception: diff --git a/cinder/volume/drivers/emc/emc_vmax_fast.py b/cinder/volume/drivers/emc/emc_vmax_fast.py index d0446acf0..e0505337b 100644 --- a/cinder/volume/drivers/emc/emc_vmax_fast.py +++ b/cinder/volume/drivers/emc/emc_vmax_fast.py @@ -142,7 +142,7 @@ class EMCVMAXFast(object): def add_volume_to_default_storage_group_for_fast_policy( self, conn, controllerConfigService, volumeInstance, - volumeName, fastPolicyName): + volumeName, fastPolicyName, extraSpecs): """Add a volume to the default storage group for FAST policy. The storage group must pre-exist. Once added to the storage group, @@ -153,6 +153,7 @@ class EMCVMAXFast(object): :param volumeInstance: the volume instance :param volumeName: the volume name (String) :param fastPolicyName: the fast policy name (String) + :param extraSpecs: additional info :returns: assocStorageGroupInstanceName - the storage group associated with the volume """ @@ -170,7 +171,7 @@ class EMCVMAXFast(object): self.provision.add_members_to_masking_group( conn, controllerConfigService, storageGroupInstanceName, - volumeInstance.path, volumeName) + volumeInstance.path, volumeName, extraSpecs) # Check to see if the volume is in the storage group. assocStorageGroupInstanceName = ( self.utils.get_storage_group_from_volume(conn, @@ -179,7 +180,7 @@ class EMCVMAXFast(object): def _create_default_storage_group(self, conn, controllerConfigService, fastPolicyName, storageGroupName, - volumeInstance): + volumeInstance, extraSpecs): """Create a first volume for the storage group. This is necessary because you cannot remove a volume if it is the @@ -191,12 +192,13 @@ class EMCVMAXFast(object): :param fastPolicyName: the fast policy name (String) :param storageGroupName: the storage group name (String) :param volumeInstance: the volume instance + :param extraSpecs: additional info :returns: defaultstorageGroupInstanceName - instance name of the default storage group """ failedRet = None firstVolumeInstance = self._create_volume_for_default_volume_group( - conn, controllerConfigService, volumeInstance.path) + conn, controllerConfigService, volumeInstance.path, extraSpecs) if firstVolumeInstance is None: LOG.error(_LE( "Failed to create a first volume for storage " @@ -207,7 +209,7 @@ class EMCVMAXFast(object): defaultStorageGroupInstanceName = ( self.provision.create_and_get_storage_group( conn, controllerConfigService, storageGroupName, - firstVolumeInstance.path)) + firstVolumeInstance.path, extraSpecs)) if defaultStorageGroupInstanceName is None: LOG.error(_LE( "Failed to create default storage group for " @@ -234,12 +236,13 @@ class EMCVMAXFast(object): self.add_storage_group_to_tier_policy_rule( conn, tierPolicyServiceInstanceName, defaultStorageGroupInstanceName, tierPolicyRuleInstanceName, - storageGroupName, fastPolicyName) + storageGroupName, fastPolicyName, extraSpecs) return defaultStorageGroupInstanceName def _create_volume_for_default_volume_group( - self, conn, controllerConfigService, volumeInstanceName): + self, conn, controllerConfigService, volumeInstanceName, + extraSpecs): """Creates a volume for the default storage group for a fast policy. Creates a small first volume for the default storage group for a @@ -249,6 +252,7 @@ class EMCVMAXFast(object): :param conn: the connection information to the ecom server :param controllerConfigService: the controller configuration service :param volumeInstanceName: the volume instance name + :param extraSpecs: additional info :returns: firstVolumeInstanceName - instance name of the first volume in the storage group """ @@ -270,7 +274,7 @@ class EMCVMAXFast(object): volumeDict, _ = ( self.provision.create_volume_from_pool( conn, storageConfigurationInstanceName, volumeName, - poolInstanceName, volumeSize)) + poolInstanceName, volumeSize, extraSpecs)) firstVolumeInstanceName = self.utils.find_volume_instance( conn, volumeDict, volumeName) return firstVolumeInstanceName @@ -278,7 +282,7 @@ class EMCVMAXFast(object): def add_storage_group_to_tier_policy_rule( self, conn, tierPolicyServiceInstanceName, storageGroupInstanceName, tierPolicyRuleInstanceName, - storageGroupName, fastPolicyName): + storageGroupName, fastPolicyName, extraSpecs): """Add the storage group to the tier policy rule. :param conn: the connection information to the ecom server @@ -287,6 +291,7 @@ class EMCVMAXFast(object): :param tierPolicyRuleInstanceName: tier policy instance name :param storageGroupName: the storage group name (String) :param fastPolicyName: the fast policy name (String) + :param extraSpecs: additional info """ # 5 is ("Add InElements to Policy"). modificationType = '5' @@ -297,7 +302,8 @@ class EMCVMAXFast(object): Operation=self.utils.get_num(modificationType, '16'), InElements=[storageGroupInstanceName]) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error associating storage group : %(storageGroupName)s. " @@ -463,7 +469,7 @@ class EMCVMAXFast(object): def add_storage_group_and_verify_tier_policy_assoc( self, conn, controllerConfigService, storageGroupInstanceName, - storageGroupName, fastPolicyName): + storageGroupName, fastPolicyName, extraSpecs): """Adds a storage group to a tier policy and verifies success. Add a storage group to a tier policy rule and verify that it was @@ -474,6 +480,7 @@ class EMCVMAXFast(object): :param storageGroupInstanceName: the storage group instance name :param storageGroupName: the storage group name (String) :param fastPolicyName: the fast policy name (String) + :param extraSpecs: additional info :returns: assocTierPolicyInstanceName """ failedRet = None @@ -502,7 +509,7 @@ class EMCVMAXFast(object): self.add_storage_group_to_tier_policy_rule( conn, tierPolicyServiceInstanceName, storageGroupInstanceName, tierPolicyRuleInstanceName, - storageGroupName, fastPolicyName) + storageGroupName, fastPolicyName, extraSpecs) except Exception as ex: LOG.error(_LE("Exception: %s"), ex) LOG.error(_LE( @@ -547,7 +554,8 @@ class EMCVMAXFast(object): def delete_storage_group_from_tier_policy_rule( self, conn, tierPolicyServiceInstanceName, - storageGroupInstanceName, tierPolicyRuleInstanceName): + storageGroupInstanceName, tierPolicyRuleInstanceName, + extraSpecs): """Disassociate the storage group from its tier policy rule. :param conn: connection the ecom server @@ -556,6 +564,7 @@ class EMCVMAXFast(object): :param storageGroupInstanceName: instance name of the storage group :param tierPolicyRuleInstanceName: instance name of the tier policy associated with the storage group + :param extraSpecs: additional information """ modificationType = '6' LOG.debug("Invoking ModifyStorageTierPolicyRule %s.", @@ -567,7 +576,8 @@ class EMCVMAXFast(object): Operation=self.utils.get_num(modificationType, '16'), InElements=[storageGroupInstanceName]) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: LOG.error(_LE("Error disassociating storage group from " "policy: %s."), errordesc) @@ -717,13 +727,14 @@ class EMCVMAXFast(object): def get_or_create_default_storage_group( self, conn, controllerConfigService, fastPolicyName, - volumeInstance): + volumeInstance, extraSpecs): """Create or get a default storage group for FAST policy. :param conn: the ecom connection :param controllerConfigService: the controller configuration service :param fastPolicyName: the fast policy name (String) :param volumeInstance: the volume instance + :param extraSpecs: additional info :returns: defaultStorageGroupInstanceName - the default storage group instance name """ @@ -740,7 +751,8 @@ class EMCVMAXFast(object): controllerConfigService, fastPolicyName, defaultSgGroupName, - volumeInstance)) + volumeInstance, + extraSpecs)) return defaultStorageGroupInstanceName diff --git a/cinder/volume/drivers/emc/emc_vmax_masking.py b/cinder/volume/drivers/emc/emc_vmax_masking.py index 41f3702a2..7c3fecd84 100644 --- a/cinder/volume/drivers/emc/emc_vmax_masking.py +++ b/cinder/volume/drivers/emc/emc_vmax_masking.py @@ -33,6 +33,8 @@ ISCSI = 'iscsi' FC = 'fc' EMC_ROOT = 'root/emc' +FASTPOLICY = 'storagetype:fastpolicy' +ISV3 = 'isV3' class EMCVMAXMasking(object): @@ -49,7 +51,8 @@ class EMCVMAXMasking(object): self.provision = emc_vmax_provision.EMCVMAXProvision(prtcl) self.provisionv3 = emc_vmax_provision_v3.EMCVMAXProvisionV3(prtcl) - def get_or_create_masking_view_and_map_lun(self, conn, maskingViewDict): + def get_or_create_masking_view_and_map_lun(self, conn, maskingViewDict, + extraSpecs): """Get or Create a masking view and add a volume to the storage group. Given a masking view tuple either get or create a masking view and add @@ -59,6 +62,7 @@ class EMCVMAXMasking(object): :param conn: the connection to ecom :para maskingViewDict: the masking view tuple + :param extraSpecs: additional info :returns: dict rollbackDict """ rollbackDict = {} @@ -69,6 +73,7 @@ class EMCVMAXMasking(object): volumeName = maskingViewDict['volumeName'] isV3 = maskingViewDict['isV3'] isLiveMigration = maskingViewDict['isLiveMigration'] + maskingViewDict['extraSpecs'] = extraSpecs defaultStorageGroupInstanceName = None fastPolicyName = None assocStorageGroupName = None @@ -105,7 +110,8 @@ class EMCVMAXMasking(object): self._get_and_remove_from_storage_group_v2( conn, controllerConfigService, volumeInstance.path, - volumeName, fastPolicyName)) + volumeName, fastPolicyName, + extraSpecs)) # Validate new or existing masking view. # Return the storage group so we can add the volume to it. @@ -132,6 +138,7 @@ class EMCVMAXMasking(object): rollbackDict['volumeName'] = volumeName rollbackDict['fastPolicyName'] = fastPolicyName rollbackDict['isV3'] = isV3 + rollbackDict['extraSpecs'] = extraSpecs if errorMessage: # Rollback code if we cannot complete any of the steps above @@ -440,7 +447,6 @@ class EMCVMAXMasking(object): controllerConfigService = maskingViewDict['controllerConfigService'] sgGroupName = maskingViewDict['sgGroupName'] volumeInstance = maskingViewDict['volumeInstance'] - storageSystemName = maskingViewDict['storageSystemName'] volumeName = maskingViewDict['volumeName'] msg = None if self._is_volume_in_storage_group( @@ -455,7 +461,7 @@ class EMCVMAXMasking(object): self.add_volume_to_storage_group( conn, controllerConfigService, storageGroupInstanceName, volumeInstance, volumeName, - sgGroupName, storageSystemName) + sgGroupName, maskingViewDict['extraSpecs']) if not self._is_volume_in_storage_group( conn, storageGroupInstanceName, volumeInstance): @@ -476,13 +482,14 @@ class EMCVMAXMasking(object): def _get_and_remove_from_storage_group_v2( self, conn, controllerConfigService, volumeInstanceName, - volumeName, fastPolicyName): + volumeName, fastPolicyName, extraSpecs): """Get the storage group and remove volume from it. :param controllerConfigService - controller configuration service :param volumeInstanceName - volume instance name :param volumeName - volume name :param fastPolicyName - fast name + :param extraSpecs: additional info """ defaultStorageGroupInstanceName = ( self.fast.get_and_verify_default_storage_group( @@ -500,7 +507,7 @@ class EMCVMAXMasking(object): retStorageGroupInstanceName = ( self.remove_device_from_default_storage_group( conn, controllerConfigService, volumeInstanceName, - volumeName, fastPolicyName)) + volumeName, fastPolicyName, extraSpecs)) if retStorageGroupInstanceName is None: exceptionMessage = (_( "Failed to remove volume %(volumeName)s from default SG: " @@ -532,7 +539,7 @@ class EMCVMAXMasking(object): self.provision.remove_device_from_storage_group( conn, controllerConfigService, storageGroupInstanceName, - volumeInstanceName, volumeName) + volumeInstanceName, volumeName, maskingViewDict['extraSpecs']) assocVolumeInstanceNames = self.get_devices_from_storage_group( conn, storageGroupInstanceName) @@ -661,21 +668,22 @@ class EMCVMAXMasking(object): foundStorageGroupInstanceName = ( self.provisionv3.create_storage_group_v3( conn, controllerConfigService, storageGroupName, - pool, slo, workload)) + pool, slo, workload, maskingViewDict['extraSpecs'])) else: fastPolicyName = maskingViewDict['fastPolicy'] volumeInstance = maskingViewDict['volumeInstance'] foundStorageGroupInstanceName = ( self.provision.create_and_get_storage_group( conn, controllerConfigService, storageGroupName, - volumeInstance.path)) + volumeInstance.path, maskingViewDict['extraSpecs'])) if (fastPolicyName is not None and defaultStorageGroupInstanceName is not None): assocTierPolicyInstanceName = ( self.fast.add_storage_group_and_verify_tier_policy_assoc( conn, controllerConfigService, foundStorageGroupInstanceName, - storageGroupName, fastPolicyName)) + storageGroupName, fastPolicyName, + maskingViewDict['extraSpecs'])) if assocTierPolicyInstanceName is None: LOG.error(_LE( "Cannot add and verify tier policy association for " @@ -1190,7 +1198,8 @@ class EMCVMAXMasking(object): rollbackDict['controllerConfigService'], rollbackDict['volumeInstance'], rollbackDict['volumeName'], - rollbackDict['fastPolicyName'])) + rollbackDict['fastPolicyName'], + rollbackDict['extraSpecs'])) if assocDefaultStorageGroupName is None: LOG.error(_LE( "Failed to Roll back to re-add volume " @@ -1218,7 +1227,8 @@ class EMCVMAXMasking(object): rollbackDict['controllerConfigService'], rollbackDict['volumeInstance'], rollbackDict['fastPolicyName'], - rollbackDict['volumeName'], False) + rollbackDict['volumeName'], rollbackDict['extraSpecs'], + False) except Exception as e: LOG.error(_LE("Exception: %s."), e) errorMessage = (_( @@ -1487,7 +1497,7 @@ class EMCVMAXMasking(object): def add_volume_to_storage_group( self, conn, controllerConfigService, storageGroupInstanceName, - volumeInstance, volumeName, sgGroupName, storageSystemName=None): + volumeInstance, volumeName, sgGroupName, extraSpecs): """Add a volume to an existing storage group. :param conn: connection to ecom server @@ -1496,14 +1506,13 @@ class EMCVMAXMasking(object): :param volumeInstance: the volume instance :param volumeName: the name of the volume (String) :param sgGroupName: the name of the storage group (String) - :param storageSystemName: the storage system name (Optional Parameter), - if None plain operation assumed + :param extraSpecs: additional info :returns: int rc the return code of the job :returns: dict the job dict """ self.provision.add_members_to_masking_group( conn, controllerConfigService, storageGroupInstanceName, - volumeInstance.path, volumeName) + volumeInstance.path, volumeName, extraSpecs) LOG.info(_LI( "Added volume: %(volumeName)s to existing storage group " @@ -1513,7 +1522,7 @@ class EMCVMAXMasking(object): def remove_device_from_default_storage_group( self, conn, controllerConfigService, volumeInstanceName, - volumeName, fastPolicyName): + volumeName, fastPolicyName, extraSpecs): """Remove the volume from the default storage group. Remove the volume from the default storage group for the FAST @@ -1524,6 +1533,7 @@ class EMCVMAXMasking(object): :param volumeInstanceName: the volume instance name :param volumeName: the volume name (String) :param fastPolicyName: the fast policy name (String) + :param extraSpecs: additional info :returns: instance name defaultStorageGroupInstanceName """ failedRet = None @@ -1550,7 +1560,7 @@ class EMCVMAXMasking(object): self.provision.remove_device_from_storage_group( conn, controllerConfigService, defaultStorageGroupInstanceName, - volumeInstanceName, volumeName) + volumeInstanceName, volumeName, extraSpecs) assocVolumeInstanceNames = self.get_devices_from_storage_group( conn, defaultStorageGroupInstanceName) @@ -1627,7 +1637,7 @@ class EMCVMAXMasking(object): def remove_and_reset_members( self, conn, controllerConfigService, volumeInstance, - fastPolicyName, volumeName, isV3, connector=None, noReset=None): + volumeName, extraSpecs, connector=None, noReset=None): """Part of unmap device or rollback. Removes volume from the Device Masking Group that belongs to a @@ -1639,13 +1649,14 @@ class EMCVMAXMasking(object): :param conn: connection the the ecom server :param controllerConfigService: the controller configuration service :param volumeInstance: the volume Instance - :param fastPolicyName: the fast policy name (if it exists) :param volumeName: the volume name - :param isV3: is array v2 or v3 :param connector: optional :param noReset: optional, if none, then reset :returns: maskingGroupInstanceName """ + isV3 = extraSpecs[ISV3] + fastPolicyName = extraSpecs.get(FASTPOLICY, None) + storageGroupInstanceName = None if connector is not None: storageGroupInstanceName = self._get_sg_associated_with_connector( @@ -1691,11 +1702,11 @@ class EMCVMAXMasking(object): isTieringPolicySupported, tierPolicyServiceInstanceName, storageSystemInstanceName['Name'], - storageGroupInstanceName) + storageGroupInstanceName, extraSpecs) self.provision.remove_device_from_storage_group( conn, controllerConfigService, storageGroupInstanceName, - volumeInstance.path, volumeName) + volumeInstance.path, volumeName, extraSpecs) LOG.debug( "Remove the last volume %(volumeName)s completed " @@ -1708,12 +1719,13 @@ class EMCVMAXMasking(object): if noReset is None: self._return_volume_to_default_storage_group_v3( conn, controllerConfigService, storageGroupName, - volumeInstance, volumeName, storageSystemInstanceName) + volumeInstance, volumeName, storageSystemInstanceName, + extraSpecs) else: if isTieringPolicySupported: self._cleanup_tiering( conn, controllerConfigService, fastPolicyName, - volumeInstance, volumeName) + volumeInstance, volumeName, extraSpecs) else: # Not the last volume so remove it from storage group in # the masking view. @@ -1721,7 +1733,7 @@ class EMCVMAXMasking(object): "%(numVol)d.", {'numVol': len(volumeInstanceNames)}) self.provision.remove_device_from_storage_group( conn, controllerConfigService, storageGroupInstanceName, - volumeInstance.path, volumeName) + volumeInstance.path, volumeName, extraSpecs) LOG.debug( "RemoveMembers for volume %(volumeName)s completed " @@ -1731,14 +1743,15 @@ class EMCVMAXMasking(object): if isV3: self._return_volume_to_default_storage_group_v3( conn, controllerConfigService, storageGroupName, - volumeInstance, volumeName, storageSystemInstanceName) + volumeInstance, volumeName, storageSystemInstanceName, + extraSpecs) else: # V2 if FAST POLICY enabled, move the volume to the default # SG. if fastPolicyName is not None and isTieringPolicySupported: self._cleanup_tiering( conn, controllerConfigService, fastPolicyName, - volumeInstance, volumeName) + volumeInstance, volumeName, extraSpecs) volumeInstanceNames = self.get_devices_from_storage_group( conn, storageGroupInstanceName) @@ -1816,7 +1829,7 @@ class EMCVMAXMasking(object): def _get_and_remove_rule_association( self, conn, fastPolicyName, isTieringPolicySupported, tierPolicyServiceInstanceName, storageSystemName, - storageGroupInstanceName): + storageGroupInstanceName, extraSpecs): """Remove the storage group from the policy rule. :param conn: the ecom connection @@ -1824,6 +1837,7 @@ class EMCVMAXMasking(object): :param tierPolicyServiceInstanceName: the tier policy instance name :param storageSystemName: storage system name :param storageGroupInstanceName: the storage group instance name + :param extraSpecs: additional info """ # Disassociate storage group from FAST policy. if fastPolicyName is not None and isTieringPolicySupported is True: @@ -1839,11 +1853,12 @@ class EMCVMAXMasking(object): self.fast.delete_storage_group_from_tier_policy_rule( conn, tierPolicyServiceInstanceName, - storageGroupInstanceName, tierPolicyInstanceName) + storageGroupInstanceName, tierPolicyInstanceName, extraSpecs) def _return_volume_to_default_storage_group_v3( self, conn, controllerConfigService, storageGroupName, - volumeInstance, volumeName, storageSystemInstanceName): + volumeInstance, volumeName, storageSystemInstanceName, + extraSpecs): """Return volume to the default storage group in v3. :param conn: the ecom connection @@ -1852,6 +1867,7 @@ class EMCVMAXMasking(object): :param volumeInstance: volumeInstance :param volumeName: the volume name :param storageSystemInstanceName: the storage system instance name + :param extraSpecs: additional info """ # First strip the shortHostname from the storage group name. defaultStorageGroupName, shorthostName = ( @@ -1886,7 +1902,7 @@ class EMCVMAXMasking(object): def _cleanup_tiering( self, conn, controllerConfigService, fastPolicyName, - volumeInstance, volumeName): + volumeInstance, volumeName, extraSpecs): """Clea nup tiering. :param conn: the ecom connection @@ -1894,6 +1910,7 @@ class EMCVMAXMasking(object): :param fastPolicyName: the fast policy name :param volumeInstance: volume instance :param volumeName: the volume name + :param extraSpecs: additional info """ defaultStorageGroupInstanceName = ( self.fast.get_policy_default_storage_group( @@ -1906,7 +1923,7 @@ class EMCVMAXMasking(object): defaultStorageGroupInstanceName = ( self.fast.add_volume_to_default_storage_group_for_fast_policy( conn, controllerConfigService, volumeInstance, volumeName, - fastPolicyName)) + fastPolicyName, extraSpecs)) # Check default storage group number of volumes. volumeInstanceNames = self.get_devices_from_storage_group( conn, defaultStorageGroupInstanceName) diff --git a/cinder/volume/drivers/emc/emc_vmax_provision.py b/cinder/volume/drivers/emc/emc_vmax_provision.py index 9b2bfb9d2..ddaf38540 100644 --- a/cinder/volume/drivers/emc/emc_vmax_provision.py +++ b/cinder/volume/drivers/emc/emc_vmax_provision.py @@ -43,14 +43,15 @@ class EMCVMAXProvision(object): self.utils = emc_vmax_utils.EMCVMAXUtils(prtcl) def delete_volume_from_pool( - self, conn, storageConfigservice, volumeInstanceName, volumeName): + self, conn, storageConfigservice, volumeInstanceName, volumeName, + extraSpecs): """Given the volume instance remove it from the pool. :param conn: connection the the ecom server :param storageConfigservice: volume created from job :param volumeInstanceName: the volume instance name :param volumeName: the volume name (String) - :param + :param extraSpecs: additional info :param rc: return code """ startTime = time.time() @@ -66,7 +67,8 @@ class EMCVMAXProvision(object): TheElements=theElements) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Delete Volume: %(volumeName)s. " @@ -87,7 +89,7 @@ class EMCVMAXProvision(object): def create_volume_from_pool( self, conn, storageConfigService, volumeName, - poolInstanceName, volumeSize): + poolInstanceName, volumeSize, extraSpecs): """Create the volume in the specified pool. :param conn: the connection information to the ecom server @@ -96,6 +98,7 @@ class EMCVMAXProvision(object): :param poolInstanceName: the pool instance name to create the dummy volume in :param volumeSize: volume size (String) + :param extraSpecs: additional info :returns: volumeDict - the volume dict """ startTime = time.time() @@ -113,7 +116,8 @@ class EMCVMAXProvision(object): 'rc': rc}) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Create Volume: %(volumeName)s. " @@ -135,13 +139,15 @@ class EMCVMAXProvision(object): return volumeDict, rc def create_and_get_storage_group(self, conn, controllerConfigService, - storageGroupName, volumeInstanceName): + storageGroupName, volumeInstanceName, + extraSpecs): """Create a storage group and return it. :param conn: the connection information to the ecom server :param controllerConfigService: the controller configuration service :param storageGroupName: the storage group name (String :param volumeInstanceName: the volume instance name + :param extraSpecs: additional info :returns: foundStorageGroupInstanceName - instance name of the default storage group """ @@ -153,7 +159,8 @@ class EMCVMAXProvision(object): Members=[volumeInstanceName]) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Create Group: %(groupName)s. " @@ -175,12 +182,13 @@ class EMCVMAXProvision(object): return foundStorageGroupInstanceName def create_storage_group_no_members( - self, conn, controllerConfigService, groupName): + self, conn, controllerConfigService, groupName, extraSpecs): """Create a new storage group that has no members. :param conn: connection the ecom server :param controllerConfigService: the controller configuration service :param groupName: the proposed group name + :param extraSpecs: additional info :returns: foundStorageGroupInstanceName - the instance Name of the storage group """ @@ -192,7 +200,8 @@ class EMCVMAXProvision(object): DeleteWhenBecomesUnassociated=False) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Create Group: %(groupName)s. " @@ -253,7 +262,7 @@ class EMCVMAXProvision(object): def remove_device_from_storage_group( self, conn, controllerConfigService, storageGroupInstanceName, - volumeInstanceName, volumeName): + volumeInstanceName, volumeName, extraSpecs): """Remove a volume from a storage group. :param conn: the connection to the ecom server @@ -261,6 +270,7 @@ class EMCVMAXProvision(object): :param storageGroupInstanceName: the instance name of the storage group :param volumeInstanceName: the instance name of the volume :param volumeName: the volume name (String) + :param extraSpecs: additional info :returns: rc - the return code of the job """ startTime = time.time() @@ -270,7 +280,8 @@ class EMCVMAXProvision(object): MaskingGroup=storageGroupInstanceName, Members=[volumeInstanceName]) if rc != 0L: - rc, errorDesc = self.utils.wait_for_job_complete(conn, jobDict) + rc, errorDesc = self.utils.wait_for_job_complete(conn, jobDict, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error removing volume %(vol)s. %(error)s.") @@ -288,7 +299,7 @@ class EMCVMAXProvision(object): def add_members_to_masking_group( self, conn, controllerConfigService, storageGroupInstanceName, - volumeInstanceName, volumeName): + volumeInstanceName, volumeName, extraSpecs): """Add a member to a masking group group. :param conn: the connection to the ecom server @@ -296,6 +307,7 @@ class EMCVMAXProvision(object): :param storageGroupInstanceName: the instance name of the storage group :param volumeInstanceName: the instance name of the volume :param volumeName: the volume name (String) + :param extraSpecs: additional info """ startTime = time.time() @@ -305,7 +317,8 @@ class EMCVMAXProvision(object): Members=[volumeInstanceName]) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error mapping volume %(vol)s. %(error)s.") @@ -321,7 +334,7 @@ class EMCVMAXProvision(object): def unbind_volume_from_storage_pool( self, conn, storageConfigService, poolInstanceName, - volumeInstanceName, volumeName): + volumeInstanceName, volumeName, extraSpecs): """Unbind a volume from a pool and return the unbound volume. :param conn: the connection information to the ecom server @@ -330,6 +343,7 @@ class EMCVMAXProvision(object): :param poolInstanceName: the pool instance name :param volumeInstanceName: the volume instance name :param volumeName: the volume name + :param extraSpecs: additional info :returns: unboundVolumeInstance - the unbound volume instance """ startTime = time.time() @@ -341,7 +355,8 @@ class EMCVMAXProvision(object): TheElement=volumeInstanceName) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error unbinding volume %(vol)s from pool. %(error)s.") @@ -359,7 +374,7 @@ class EMCVMAXProvision(object): def modify_composite_volume( self, conn, elementCompositionService, theVolumeInstanceName, - inVolumeInstanceName): + inVolumeInstanceName, extraSpecs): """Given a composite volume add a storage volume to it. @@ -368,6 +383,7 @@ class EMCVMAXProvision(object): :param theVolumeInstanceName: the existing composite volume :param inVolumeInstanceName: the volume you wish to add to the composite volume + :param extraSpecs: additional info :returns: rc - return code :returns: job - job """ @@ -380,7 +396,8 @@ class EMCVMAXProvision(object): InElements=[inVolumeInstanceName]) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error adding volume to composite volume. " @@ -398,7 +415,7 @@ class EMCVMAXProvision(object): def create_composite_volume( self, conn, elementCompositionService, volumeSize, volumeName, - poolInstanceName, compositeType, numMembers): + poolInstanceName, compositeType, numMembers, extraSpecs): """Create a new volume using the auto meta feature. :param conn: the connection the the ecom server @@ -410,6 +427,7 @@ class EMCVMAXProvision(object): e.g striped/concatenated :param numMembers: the number of meta members to make up the composite. If it is 1 then a non composite is created + :param extraSpecs: additional info :returns: rc :returns: errordesc """ @@ -444,7 +462,8 @@ class EMCVMAXProvision(object): EMCNumberOfMembers=self.utils.get_num(numMembers, '32')) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Create Volume: %(volumename)s. " @@ -468,7 +487,7 @@ class EMCVMAXProvision(object): def create_new_composite_volume( self, conn, elementCompositionService, compositeHeadInstanceName, - compositeMemberInstanceName, compositeType): + compositeMemberInstanceName, compositeType, extraSpecs): """Creates a new composite volume. Given a bound composite head and an unbound composite member @@ -480,6 +499,7 @@ class EMCVMAXProvision(object): :param compositeMemberInstanceName: the composite member. This must be unbound :param compositeType: the composite type e.g striped or concatenated + :param extraSpecs: additional info :returns: rc - return code :returns: errordesc - descriptions of the error """ @@ -493,7 +513,8 @@ class EMCVMAXProvision(object): CompositeType=self.utils.get_num(compositeType, '16')) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Creating new composite Volume Return code: " @@ -513,7 +534,7 @@ class EMCVMAXProvision(object): def _migrate_volume( self, conn, storageRelocationServiceInstanceName, - volumeInstanceName, targetPoolInstanceName): + volumeInstanceName, targetPoolInstanceName, extraSpecs): """Migrate a volume to another pool. :param conn: the connection to the ecom server @@ -521,6 +542,7 @@ class EMCVMAXProvision(object): service :param volumeInstanceName: the volume to be migrated :param targetPoolInstanceName: the target pool to migrate the volume to + :param extraSpecs: additional info :returns: rc - return code """ startTime = time.time() @@ -532,7 +554,8 @@ class EMCVMAXProvision(object): TargetPool=targetPoolInstanceName) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Migrating volume from one pool to another. " @@ -551,7 +574,7 @@ class EMCVMAXProvision(object): def migrate_volume_to_storage_pool( self, conn, storageRelocationServiceInstanceName, - volumeInstanceName, targetPoolInstanceName): + volumeInstanceName, targetPoolInstanceName, extraSpecs): """Given the storage system name, get the storage relocation service. :param conn: the connection to the ecom server @@ -560,6 +583,7 @@ class EMCVMAXProvision(object): :param volumeInstanceName: the volume to be migrated :param targetPoolInstanceName: the target pool to migrate the volume to. + :param extraSpecs: additional info :returns: rc """ LOG.debug( @@ -571,12 +595,12 @@ class EMCVMAXProvision(object): try: rc = self._migrate_volume( conn, storageRelocationServiceInstanceName, - volumeInstanceName, targetPoolInstanceName) + volumeInstanceName, targetPoolInstanceName, extraSpecs) except Exception as ex: if 'source of a migration session' in six.text_type(ex): try: rc = self._terminate_migrate_session( - conn, volumeInstanceName) + conn, volumeInstanceName, extraSpecs) except Exception as ex: LOG.error(_LE('Exception: %s.'), ex) exceptionMessage = (_( @@ -587,7 +611,8 @@ class EMCVMAXProvision(object): try: rc = self._migrate_volume( conn, storageRelocationServiceInstanceName, - volumeInstanceName, targetPoolInstanceName) + volumeInstanceName, targetPoolInstanceName, + extraSpecs) except Exception as ex: LOG.error(_LE('Exception: %s'), ex) exceptionMessage = (_( @@ -606,11 +631,13 @@ class EMCVMAXProvision(object): return rc - def _terminate_migrate_session(self, conn, volumeInstanceName): + def _terminate_migrate_session(self, conn, volumeInstanceName, + extraSpecs): """Given the volume instance terminate a migrate session. :param conn: the connection to the ecom server :param volumeInstanceName: the volume to be migrated + :param extraSpecs: additional info :returns: rc """ startTime = time.time() @@ -619,7 +646,8 @@ class EMCVMAXProvision(object): 'RequestStateChange', volumeInstanceName, RequestedState=self.utils.get_num(32769, '16')) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Terminating migrate session. " @@ -639,7 +667,8 @@ class EMCVMAXProvision(object): def create_element_replica( self, conn, repServiceInstanceName, cloneName, - sourceName, sourceInstance, targetInstance, copyOnWrite=False): + sourceName, sourceInstance, targetInstance, extraSpecs, + copyOnWrite=False): """Make SMI-S call to create replica for source element. :param conn - the connection to the ecom server @@ -647,6 +676,8 @@ class EMCVMAXProvision(object): :param cloneName - replica name :param sourceName - source volume name :param sourceInstance - source volume instance + :param extraSpecs: additional info + :param copyOnWrite: optional :returns: rc - return code :returns: job - job object of the replica creation operation @@ -667,7 +698,8 @@ class EMCVMAXProvision(object): ReplicationType=self.utils.get_num(10, '16')) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, rsd) + rc, errordesc = self.utils.wait_for_job_complete(conn, rsd, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error creating cloned volume using " @@ -717,7 +749,8 @@ class EMCVMAXProvision(object): TargetElement=targetInstance.path) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Create Cloned Volume: " @@ -739,7 +772,8 @@ class EMCVMAXProvision(object): return rc, job def delete_clone_relationship( - self, conn, repServiceInstanceName, syncInstanceName, force=False): + self, conn, repServiceInstanceName, syncInstanceName, extraSpecs, + force=False): """Deletes the relationship between the clone and source volume. Makes an SMI-S call to break clone relationship between the clone @@ -751,6 +785,8 @@ class EMCVMAXProvision(object): :param repServiceInstanceName: instance name of the replication service :param syncInstanceName: instance name of the SE_StorageSynchronized_SV_SV object + :param extraSpecs - additional info + :param force - optional param :returns: rc - return code :returns: job - job object of the replica creation operation """ @@ -768,7 +804,8 @@ class EMCVMAXProvision(object): 'rc': rc}) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error break clone relationship: " @@ -816,12 +853,13 @@ class EMCVMAXProvision(object): return rc, targetEndpoints def create_consistency_group( - self, conn, replicationService, consistencyGroupName): + self, conn, replicationService, consistencyGroupName, extraSpecs): """Create a new consistency group. :param conn: the connection to the ecom server :param replicationService: the replication Service :param consistencyGroupName: the CG group name + :param extraSpecs - additional info :returns: rc :returns: job """ @@ -833,7 +871,8 @@ class EMCVMAXProvision(object): GroupName=consistencyGroupName) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Failed to create consistency group: " @@ -855,7 +894,7 @@ class EMCVMAXProvision(object): def delete_consistency_group( self, conn, replicationService, cgInstanceName, - consistencyGroupName): + consistencyGroupName, extraSpecs): """Delete a consistency group. @@ -863,6 +902,7 @@ class EMCVMAXProvision(object): :param replicationService: the replication Service :param cgInstanceName: the CG instance name :param consistencyGroupName: the CG group name + :param extraSpecs - additional info :returns: rc :returns: job """ @@ -875,7 +915,8 @@ class EMCVMAXProvision(object): RemoveElements=True) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Failed to delete consistency group: " @@ -897,7 +938,7 @@ class EMCVMAXProvision(object): def add_volume_to_cg( self, conn, replicationService, cgInstanceName, - volumeInstanceName, cgName, volumeName): + volumeInstanceName, cgName, volumeName, extraSpecs): """Add a volume to a consistency group. :param conn: the connection to the ecom server @@ -906,6 +947,7 @@ class EMCVMAXProvision(object): :param cgInstanceName: the CG instance name :param cgName: the CG group name :param volumeName: the volume name + :param extraSpecs - additional info :returns: rc :returns: job """ @@ -918,7 +960,8 @@ class EMCVMAXProvision(object): ReplicationGroup=cgInstanceName) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Failed to add volume %(volumeName)s: " @@ -940,7 +983,7 @@ class EMCVMAXProvision(object): def remove_volume_from_cg( self, conn, replicationService, cgInstanceName, - volumeInstanceName, cgName, volumeName): + volumeInstanceName, cgName, volumeName, extraSpecs): """Remove a volume from a consistency group. :param conn: the connection to the ecom server @@ -949,6 +992,7 @@ class EMCVMAXProvision(object): :param cgInstanceName: the CG instance name :param cgName: the CG group name :param volumeName: the volume name + :param extraSpecs - additional info :returns: rc :returns: job """ @@ -962,7 +1006,8 @@ class EMCVMAXProvision(object): RemoveElements=True) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Failed to remove volume %(volumeName)s: " @@ -984,14 +1029,16 @@ class EMCVMAXProvision(object): def create_group_replica( self, conn, replicationService, - srcGroupInstanceName, tgtGroupInstanceName, relationName): + srcGroupInstanceName, tgtGroupInstanceName, relationName, + extraSpecs): """Make SMI-S call to create replica for source group. :param conn - the connection to the ecom server :param repServiceInstanceName - replication service :param srcGroupInstanceName - source group instance name :param tgtGroupInstanceName - target group instance name - :param cgName - target group name + :param relationName - + :param extraSpecs - additional info :returns: rc - return code :returns: job - job object of the replica creation operation @@ -1016,7 +1063,8 @@ class EMCVMAXProvision(object): SyncType=self.utils.get_num(8, '16')) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMsg = (_("Error CreateGroupReplica: " "source: %(source)s target: %(target)s. " diff --git a/cinder/volume/drivers/emc/emc_vmax_provision_v3.py b/cinder/volume/drivers/emc/emc_vmax_provision_v3.py index e160e83b6..c551a98d9 100644 --- a/cinder/volume/drivers/emc/emc_vmax_provision_v3.py +++ b/cinder/volume/drivers/emc/emc_vmax_provision_v3.py @@ -41,13 +41,15 @@ class EMCVMAXProvisionV3(object): self.utils = emc_vmax_utils.EMCVMAXUtils(prtcl) def delete_volume_from_pool( - self, conn, storageConfigservice, volumeInstanceName, volumeName): + self, conn, storageConfigservice, volumeInstanceName, volumeName, + extraSpecs): """Given the volume instance remove it from the pool. :param conn: connection the the ecom server :param storageConfigservice: volume created from job :param volumeInstanceName: the volume instance name :param volumeName: the volume name (String) + :param extraSpecs: additional info :returns: rc -- return code """ startTime = time.time() @@ -63,7 +65,8 @@ class EMCVMAXProvisionV3(object): TheElements=theElements) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Delete Volume: %(volumeName)s. " @@ -84,7 +87,7 @@ class EMCVMAXProvisionV3(object): def create_volume_from_sg( self, conn, storageConfigService, volumeName, - sgInstanceName, volumeSize): + sgInstanceName, volumeSize, extraSpecs): """Create the volume and associate it with a storage group. We use EMCCollections parameter to supply a Device Masking Group @@ -96,6 +99,7 @@ class EMCVMAXProvisionV3(object): :param sgInstanceName: the storage group instance name associated with an SLO :param volumeSize: volume size (String) + :param extraSpecs: additional info :returns: volumeDict - the volume dict :returns: rc - return code """ @@ -113,7 +117,8 @@ class EMCVMAXProvisionV3(object): 'rc': rc}) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Create Volume: %(volumeName)s. " @@ -173,7 +178,8 @@ class EMCVMAXProvisionV3(object): def create_element_replica( self, conn, repServiceInstanceName, - cloneName, syncType, sourceInstance, targetInstance=None): + cloneName, syncType, sourceInstance, extraSpecs, + targetInstance=None): """Make SMI-S call to create replica for source element. :param conn - the connection to the ecom server @@ -181,6 +187,7 @@ class EMCVMAXProvisionV3(object): :param cloneName - clone volume name :param syncType - 7: snapshot, 8: clone :param sourceInstance - source volume instance + :param extraSpecs: additional info :param targetInstance - target volume instance :returns: rc - return code :returns: job - job object of the replica creation operation @@ -212,7 +219,8 @@ class EMCVMAXProvisionV3(object): TargetElement=targetInstance.path) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error Create Cloned Volume: %(cloneName)s " @@ -232,7 +240,7 @@ class EMCVMAXProvisionV3(object): def break_replication_relationship( self, conn, repServiceInstanceName, syncInstanceName, - operation, force=False): + operation, extraSpecs, force=False): """Deletes the relationship between the clone/snap and source volume. Makes an SMI-S call to break clone relationship between the clone @@ -243,6 +251,7 @@ class EMCVMAXProvisionV3(object): :param syncInstanceName: instance name of the SE_StorageSynchronized_SV_SV object :param operation: operation code + :param extraSpecs: additional info :param force: force to break replication relationship if True :returns: rc - return code :returns: job - job object of the replica creation operation @@ -252,10 +261,11 @@ class EMCVMAXProvisionV3(object): {'sv': syncInstanceName, 'operation': operation}) return self._modify_replica_synchronization( - conn, repServiceInstanceName, syncInstanceName, operation, force) + conn, repServiceInstanceName, syncInstanceName, operation, + extraSpecs, force) def create_storage_group_v3(self, conn, controllerConfigService, - groupName, srp, slo, workload): + groupName, srp, slo, workload, extraSpecs): """Create the volume in the specified pool. :param conn: the connection information to the ecom server @@ -264,6 +274,7 @@ class EMCVMAXProvisionV3(object): :param srp: the SRP (String) :param slo: the SLO (String) :param workload: the workload (String) + :param extraSpecs: additional info :returns: storageGroupInstanceName - storage group instance name """ @@ -279,8 +290,8 @@ class EMCVMAXProvisionV3(object): EMCWorkload=workload) if rc != 0L: - rc, errordesc = rc, errordesc = self.utils.wait_for_job_complete( - conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: LOG.error(_LE( "Error Create Group: %(groupName)s. " @@ -345,7 +356,7 @@ class EMCVMAXProvisionV3(object): def _get_supported_size_range_for_SLO( self, conn, storageConfigService, - srpPoolInstanceName, storagePoolSettingInstanceName): + srpPoolInstanceName, storagePoolSettingInstanceName, extraSpecs): """Gets available performance capacity per SLO. :param conn: the connection information to the ecom server @@ -353,6 +364,7 @@ class EMCVMAXProvisionV3(object): :param srpPoolInstanceName: the SRP storage pool instance :param storagePoolSettingInstanceName: the SLO type e.g Bronze + :param extraSpecs: additional info :returns: supportedSizeDict - the supported size dict """ startTime = time.time() @@ -365,7 +377,7 @@ class EMCVMAXProvisionV3(object): if rc != 0L: rc, errordesc = self.utils.wait_for_job_complete( - conn, supportedSizeDict) + conn, supportedSizeDict, extraSpecs) if rc != 0L: exceptionMessage = (_( "Cannot get supported size range for %(sps)s " @@ -385,7 +397,8 @@ class EMCVMAXProvisionV3(object): return supportedSizeDict def get_volume_range( - self, conn, storageConfigService, poolInstanceName, slo, workload): + self, conn, storageConfigService, poolInstanceName, slo, workload, + extraSpecs): """Get upper and lower range for volume for slo/workload combination. :param conn: the connection information to the ecom server @@ -393,6 +406,7 @@ class EMCVMAXProvisionV3(object): :param poolInstanceName: the pool instance :param slo: slo string e.g Bronze :param workload: workload string e.g DSS + :param extraSpecs: additional info :returns: maximumVolumeSize - the maximum volume size supported :returns: minimumVolumeSize - the minimum volume size supported """ @@ -407,7 +421,7 @@ class EMCVMAXProvisionV3(object): if storagePoolCapabilityInstanceName: supportedSizeDict = self._get_supported_size_range_for_SLO( conn, storageConfigService, poolInstanceName, - storagePoolSettingInstanceName) + storagePoolSettingInstanceName, extraSpecs) maximumVolumeSize = supportedSizeDict['MaximumVolumeSize'] minimumVolumeSize = supportedSizeDict['MinimumVolumeSize'] @@ -415,13 +429,14 @@ class EMCVMAXProvisionV3(object): return maximumVolumeSize, minimumVolumeSize def activate_snap_relationship( - self, conn, repServiceInstanceName, syncInstanceName): + self, conn, repServiceInstanceName, syncInstanceName, extraSpecs): """Activate snap relationship and start copy operation. :param conn: the connection to the ecom server :param repServiceInstanceName: instance name of the replication service :param syncInstanceName: instance name of the SE_StorageSynchronized_SV_SV object + :param extraSpecs: additional info :returns: rc - return code :returns: job - job object of the replica creation operation """ @@ -432,15 +447,17 @@ class EMCVMAXProvisionV3(object): {'sv': syncInstanceName, 'operation': operation}) return self._modify_replica_synchronization( - conn, repServiceInstanceName, syncInstanceName, operation) + conn, repServiceInstanceName, syncInstanceName, operation, + extraSpecs) def return_to_resource_pool(self, conn, repServiceInstanceName, - syncInstanceName): + syncInstanceName, extraSpecs): """Return the snap target resources back to the pool. :param conn: the connection to the ecom server :param repServiceInstanceName: instance name of the replication service :param syncInstanceName: instance name of the + :param extraSpecs: additional info :returns: rc - return code :returns: job - job object of the replica creation operation """ @@ -452,11 +469,12 @@ class EMCVMAXProvisionV3(object): {'sv': syncInstanceName, 'operation': operation}) return self._modify_replica_synchronization( - conn, repServiceInstanceName, syncInstanceName, operation) + conn, repServiceInstanceName, syncInstanceName, operation, + extraSpecs) def _modify_replica_synchronization( self, conn, repServiceInstanceName, syncInstanceName, - operation, force=False): + operation, extraSpecs, force=False): """Modify the relationship between the clone/snap and source volume. Helper function that makes an SMI-S call to break clone relationship @@ -466,7 +484,8 @@ class EMCVMAXProvisionV3(object): :param repServiceInstanceName: instance name of the replication service :param syncInstanceName: instance name of the SE_StorageSynchronized_SV_SV object - :param operatoin: opeation code + :param operation: opeation code + :param extraSpecs: additional info :param force: force to modify replication synchronization if True :returns: rc - return code :returns: job - job object of the replica creation operation @@ -484,7 +503,8 @@ class EMCVMAXProvisionV3(object): {'sv': syncInstanceName, 'operation': operation, 'rc': rc}) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMessage = (_( "Error modify replica synchronization: %(sv)s " @@ -505,7 +525,8 @@ class EMCVMAXProvisionV3(object): def create_group_replica( self, conn, replicationService, - srcGroupInstanceName, tgtGroupInstanceName, relationName): + srcGroupInstanceName, tgtGroupInstanceName, relationName, + extraSpecs): """Make SMI-S call to create replica for source group. :param conn - the connection to the ecom server @@ -513,6 +534,7 @@ class EMCVMAXProvisionV3(object): :param srcGroupInstanceName - source group instance name :param tgtGroupInstanceName - target group instance name :param relationName - replica relationship name + :param extraSpecs: additional info :returns: rc - return code :returns: job - job object of the replica creation operation """ @@ -537,7 +559,8 @@ class EMCVMAXProvisionV3(object): SyncType=self.utils.get_num(syncType, '16')) if rc != 0L: - rc, errordesc = self.utils.wait_for_job_complete(conn, job) + rc, errordesc = self.utils.wait_for_job_complete(conn, job, + extraSpecs) if rc != 0L: exceptionMsg = (_("Error CreateGroupReplica: " "source: %(source)s target: %(target)s. " diff --git a/cinder/volume/drivers/emc/emc_vmax_utils.py b/cinder/volume/drivers/emc/emc_vmax_utils.py index bc81d7ef6..897eb764a 100644 --- a/cinder/volume/drivers/emc/emc_vmax_utils.py +++ b/cinder/volume/drivers/emc/emc_vmax_utils.py @@ -47,6 +47,8 @@ ISCSI = 'iscsi' FC = 'fc' JOB_RETRIES = 60 INTERVAL_10_SEC = 10 +INTERVAL = 'storagetype:interval' +RETRIES = 'storagetype:retries' CIM_ERR_NOT_FOUND = 6 @@ -259,7 +261,7 @@ class EMCVMAXUtils(object): return foundTierPolicyService - def wait_for_job_complete(self, conn, job): + def wait_for_job_complete(self, conn, job, extraSpecs=None): """Given the job wait for it to complete. :param conn: connection to the ecom server @@ -269,33 +271,36 @@ class EMCVMAXUtils(object): """ jobInstanceName = job['Job'] - self._wait_for_job_complete(conn, job) + if extraSpecs and (INTERVAL in extraSpecs or RETRIES in extraSpecs): + self._wait_for_job_complete(conn, job, extraSpecs) + else: + self._wait_for_job_complete(conn, job) jobinstance = conn.GetInstance(jobInstanceName, LocalOnly=False) rc = jobinstance['ErrorCode'] errorDesc = jobinstance['ErrorDescription'] - LOG.debug("Return code is: %(rc)lu " + LOG.debug("Return code is: %(rc)lu. " "Error Description is: %(errorDesc)s.", {'rc': rc, 'errorDesc': errorDesc}) return rc, errorDesc - def _wait_for_job_complete(self, conn, job): + def _wait_for_job_complete(self, conn, job, extraSpecs=None): """Given the job wait for it to complete. - Called at an interval until the job is finished. - :param conn: connection to the ecom server :param job: the job dict """ def _wait_for_job_complete(): + # Called at an interval until the job is finished. + maxJobRetries = self._get_max_job_retries(extraSpecs) retries = kwargs['retries'] wait_for_job_called = kwargs['wait_for_job_called'] if self._is_job_finished(conn, job): raise loopingcall.LoopingCallDone() - if retries > JOB_RETRIES: + if retries > maxJobRetries: LOG.error(_LE("_wait_for_job_complete " "failed after %(retries)d " "tries."), @@ -308,15 +313,44 @@ class EMCVMAXUtils(object): if self._is_job_finished(conn, job): kwargs['wait_for_job_called'] = True except Exception as e: - LOG.error(_LE("Exception: %s") % six.text_type(e)) + LOG.error(_LE("Exception: %s.") % six.text_type(e)) exceptionMessage = (_("Issue encountered waiting for job.")) LOG.error(exceptionMessage) raise exception.VolumeBackendAPIException(exceptionMessage) kwargs = {'retries': 0, 'wait_for_job_called': False} + + intervalInSecs = self._get_interval_in_secs(extraSpecs) + timer = loopingcall.FixedIntervalLoopingCall(_wait_for_job_complete) - timer.start(interval=INTERVAL_10_SEC).wait() + timer.start(interval=intervalInSecs).wait() + + def _get_max_job_retries(self, extraSpecs): + """Get max job retries either default or user defined + + :param extraSpecs: extraSpecs + + :returns: JOB_RETRIES or user defined + """ + if extraSpecs: + jobRetries = extraSpecs[RETRIES] + else: + jobRetries = JOB_RETRIES + return int(jobRetries) + + def _get_interval_in_secs(self, extraSpecs): + """Get interval in secs, either default or user defined + + :param extraSpecs: extraSpecs + + :returns: INTERVAL_10_SEC or user defined + """ + if extraSpecs: + intervalInSecs = extraSpecs[INTERVAL] + else: + intervalInSecs = INTERVAL_10_SEC + return int(intervalInSecs) def _is_job_finished(self, conn, job): """Check if the job is finished. @@ -774,6 +808,36 @@ class EMCVMAXUtils(object): "Defaulting to NONE.") return 'NONE' + def parse_interval_from_file(self, fileName): + """Parse the interval from config file. + + If it is not there then the default will be used. + + :param fileName: the path and name of the file + :returns: interval - the interval in seconds + """ + interval = self._parse_from_file(fileName, 'Interval') + if interval: + return interval + else: + LOG.debug("Interval not found in config file.") + return None + + def parse_retries_from_file(self, fileName): + """Parse the retries from config file. + + If it is not there then the default will be used. + + :param fileName: the path and name of the file + :returns: retries - the max number of retries + """ + retries = self._parse_from_file(fileName, 'Retries') + if retries: + return retries + else: + LOG.debug("Retries not found in config file.") + return None + def parse_pool_instance_id(self, poolInstanceId): """Given the instance Id parse the pool name and system name from it. -- 2.45.2