from cinder.volume.drivers.emc import emc_vmax_fc
from cinder.volume.drivers.emc import emc_vmax_iscsi
from cinder.volume.drivers.emc import emc_vmax_masking
+from cinder.volume.drivers.emc import emc_vmax_provision
from cinder.volume.drivers.emc import emc_vmax_provision_v3
from cinder.volume.drivers.emc import emc_vmax_utils
from cinder.volume import volume_types
vols = []
vol = EMC_StorageVolume()
- vol['name'] = self.data.test_volume['name']
+ vol['Name'] = self.data.test_volume['name']
vol['CreationClassName'] = 'Symm_StorageVolume'
vol['ElementName'] = self.data.test_volume['id']
vol['DeviceID'] = self.data.test_volume['device_id']
self.driver.create_cloned_volume(self.data.test_volume,
EMCVMAXCommonData.test_source_volume)
- def test_create_clone_no_fast_failed(self):
+ # Bug https://bugs.launchpad.net/cinder/+bug/1440154
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'get_volume_meta_head',
+ return_value=[EMCVMAXCommonData.test_volume])
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_get_pool_and_storage_system',
+ return_value=(None, EMCVMAXCommonData.storage_system))
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'get_meta_members_capacity_in_bit',
+ return_value=[1234567, 7654321])
+ @mock.patch.object(
+ FakeDB,
+ 'volume_get',
+ return_value=EMCVMAXCommonData.test_source_volume)
+ @mock.patch.object(
+ volume_types,
+ 'get_volume_type_extra_specs',
+ return_value={'volume_backend_name': 'ISCSINoFAST'})
+ @mock.patch.object(
+ emc_vmax_provision.EMCVMAXProvision,
+ 'create_element_replica')
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'find_sync_sv_by_target',
+ return_value=(None, None))
+ def test_create_clone_assert_clean_up_target_volume(
+ self, mock_sync, mock_create_replica, mock_volume_type,
+ mock_volume, mock_capacities, mock_pool, mock_meta_volume):
self.data.test_volume['volume_name'] = "vmax-1234567"
+ e = exception.VolumeBackendAPIException('CreateElementReplica Ex')
+ common = self.driver.common
+ common._delete_from_pool = mock.Mock(return_value=0L)
+ conn = self.fake_ecom_connection()
+ storageConfigService = (
+ common.utils.find_storage_configuration_service(
+ conn, self.data.storage_system))
+ mock_create_replica.side_effect = e
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_cloned_volume,
self.data.test_volume,
EMCVMAXCommonData.test_source_volume)
+ extraSpecs = common._initial_setup(self.data.test_volume)
+ fastPolicy = extraSpecs['storagetype:fastpolicy']
+ targetInstance = (
+ conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
+ common._delete_from_pool.assert_called_with(storageConfigService,
+ targetInstance,
+ targetInstance['Name'],
+ targetInstance['DeviceID'],
+ fastPolicy,
+ extraSpecs)
@mock.patch.object(
volume_types,
def test_create_cloned_volume_v3_success(
self, mock_volume_db, mock_type, moke_pool, mock_size):
self.data.test_volume['volume_name'] = "vmax-1234567"
-
cloneVol = {}
cloneVol['name'] = 'vol1'
cloneVol['id'] = '10'
cloneVol['provider_location'] = None
cloneVol['NumberOfBlocks'] = 100
cloneVol['BlockSize'] = 512
- name = {}
- name['classname'] = 'Symm_StorageVolume'
- keys = {}
- keys['CreationClassName'] = cloneVol['CreationClassName']
- keys['SystemName'] = cloneVol['SystemName']
- keys['DeviceID'] = cloneVol['DeviceID']
- keys['NumberOfBlocks'] = cloneVol['NumberOfBlocks']
- keys['BlockSize'] = cloneVol['BlockSize']
- keys['SystemCreationClassName'] = \
- cloneVol['SystemCreationClassName']
- name['keybindings'] = keys
-
self.driver.create_cloned_volume(cloneVol, self.data.test_volume)
@mock.patch.object(
numTargetWwns = len(EMCVMAXCommonData.target_wwns)
self.assertEqual(numTargetWwns, len(data['data']))
+ # Bug https://bugs.launchpad.net/cinder/+bug/1440154
+ @mock.patch.object(
+ emc_vmax_provision_v3.EMCVMAXProvisionV3,
+ '_get_supported_size_range_for_SLO',
+ return_value={'MaximumVolumeSize': '30000000000',
+ 'MinimumVolumeSize': '100000'})
+ @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': 'V3_BE'})
+ @mock.patch.object(
+ FakeDB,
+ 'volume_get',
+ return_value=EMCVMAXCommonData.test_source_volume)
+ @mock.patch.object(
+ emc_vmax_provision_v3.EMCVMAXProvisionV3,
+ 'create_element_replica')
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'find_sync_sv_by_target',
+ return_value=(None, None))
+ def test_create_clone_v3_assert_clean_up_target_volume(
+ self, mock_sync, mock_create_replica, mock_volume_db,
+ mock_type, moke_pool, mock_size):
+ self.data.test_volume['volume_name'] = "vmax-1234567"
+ e = exception.VolumeBackendAPIException('CreateElementReplica Ex')
+ common = self.driver.common
+ volumeDict = {'classname': u'Symm_StorageVolume',
+ 'keybindings': EMCVMAXCommonData.keybindings}
+ common._create_v3_volume = (
+ mock.Mock(return_value=(0L, volumeDict, self.data.storage_system)))
+ conn = self.fake_ecom_connection()
+ storageConfigService = []
+ storageConfigService = {}
+ storageConfigService['SystemName'] = EMCVMAXCommonData.storage_system
+ storageConfigService['CreationClassName'] = \
+ self.data.stconf_service_creationclass
+ common._delete_from_pool_v3 = mock.Mock(return_value=0L)
+ mock_create_replica.side_effect = e
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_cloned_volume,
+ self.data.test_volume,
+ EMCVMAXCommonData.test_source_volume)
+ extraSpecs = common._initial_setup(self.data.test_volume)
+ targetInstance = (
+ conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
+ storageGroupName = common.utils.get_v3_storage_group_name('SRP_1',
+ 'Bronze',
+ 'DSS')
+ deviceID = targetInstance['DeviceID']
+ common._delete_from_pool_v3.assert_called_with(storageConfigService,
+ targetInstance,
+ targetInstance['Name'],
+ deviceID,
+ storageGroupName,
+ extraSpecs)
+
def _cleanup(self):
bExists = os.path.exists(self.config_file_path)
if bExists:
exceptionMessage = (_(
"Error Creating unbound volume."))
LOG.error(exceptionMessage)
+ # Remove target volume
+ self._delete_target_volume_v2(storageConfigService,
+ baseTargetVolumeInstance,
+ extraSpecs)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
# base target composite volume.
baseTargetVolumeInstance = self.utils.find_volume_instance(
self.conn, baseVolumeDict, baseVolumeName)
- elementCompositionService = (
- self.utils.find_element_composition_service(
- self.conn, storageSystemName))
- compositeType = self.utils.get_composite_type(
- extraSpecs[COMPOSITETYPE])
- _rc, modifiedVolumeDict = (
- self._modify_and_get_composite_volume_instance(
- self.conn,
- elementCompositionService,
- baseTargetVolumeInstance,
- unboundVolumeInstance.path,
- targetVolumeName,
- compositeType,
- extraSpecs))
- if modifiedVolumeDict is None:
+ try:
+ elementCompositionService = (
+ self.utils.find_element_composition_service(
+ self.conn, storageSystemName))
+ compositeType = self.utils.get_composite_type(
+ extraSpecs[COMPOSITETYPE])
+ _rc, modifiedVolumeDict = (
+ self._modify_and_get_composite_volume_instance(
+ self.conn,
+ elementCompositionService,
+ baseTargetVolumeInstance,
+ unboundVolumeInstance.path,
+ targetVolumeName,
+ compositeType,
+ extraSpecs))
+ if modifiedVolumeDict is None:
+ exceptionMessage = (_(
+ "Error appending volume %(volumename)s to "
+ "target base volume.")
+ % {'volumename': targetVolumeName})
+ LOG.error(exceptionMessage)
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+ except Exception:
exceptionMessage = (_(
- "Error appending volume %(volumename)s to "
- "target base volume.")
- % {'volumename': targetVolumeName})
+ "Exception appending meta volume to target volume "
+ "%(volumename)s.")
+ % {'volumename': baseVolumeName})
LOG.error(exceptionMessage)
+ # Remove append volume and target base volume
+ self._delete_target_volume_v2(
+ storageConfigService, unboundVolumeInstance,
+ extraSpecs)
+ self._delete_target_volume_v2(
+ storageConfigService, baseTargetVolumeInstance,
+ extraSpecs)
+
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
"""
sourceName = sourceVolume['name']
cloneName = cloneVolume['name']
- rc, job = self.provision.create_element_replica(
- self.conn, repServiceInstanceName, cloneName, sourceName,
- sourceInstance, targetInstance, extraSpecs)
+
+ try:
+ rc, job = self.provision.create_element_replica(
+ self.conn, repServiceInstanceName, cloneName, sourceName,
+ sourceInstance, targetInstance, extraSpecs)
+ except Exception:
+ exceptionMessage = (_(
+ "Exception during create element replica. "
+ "Clone name: %(cloneName)s "
+ "Source name: %(sourceName)s "
+ "Extra specs: %(extraSpecs)s ")
+ % {'cloneName': cloneName,
+ 'sourceName': sourceName,
+ 'extraSpecs': extraSpecs})
+ LOG.error(exceptionMessage)
+
+ if targetInstance is not None:
+ # Check if the copy session exists.
+ storageSystem = targetInstance['SystemName']
+ syncInstanceName = self.utils.find_sync_sv_by_target(
+ self.conn, storageSystem, targetInstance, False)
+ if syncInstanceName is not None:
+ # Remove the Clone relationship.
+ rc, job = self.provision.delete_clone_relationship(
+ self.conn, repServiceInstanceName, syncInstanceName,
+ extraSpecs, True)
+ storageConfigService = (
+ self.utils.find_storage_configuration_service(
+ self.conn, storageSystem))
+ self._delete_target_volume_v2(
+ storageConfigService, targetInstance, extraSpecs)
+
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
cloneDict = self.provision.get_volume_dict_from_job(
self.conn, job['Job'])
+
fastPolicyName = extraSpecs[FASTPOLICY]
if isSnapshot:
if fastPolicyName is not None:
cloneVolume['provider_location'] = six.text_type(cloneDict)
syncInstanceName, storageSystemName = (
- self._find_storage_sync_sv_sv(cloneVolume, sourceVolume))
+ self._find_storage_sync_sv_sv(cloneVolume, sourceVolume,
+ extraSpecs))
# Remove the Clone relationship so it can be used as a regular lun.
# 8 - Detach operation.
:returns: dict -- cloneDict
"""
cloneName = cloneVolume['name']
- syncType = self.utils.get_num(8, '16') # Default syncType 8: clone.
+ # Default syncType 8: clone.
+ syncType = self.utils.get_num(8, '16')
+ # Default operation 8: Detach for clone.
+ operation = self.utils.get_num(8, '16')
# Create target volume
extraSpecs = self._initial_setup(cloneVolume)
if isSnapshot:
# SyncType 7: snap, VG3R default snapshot is snapVx.
syncType = self.utils.get_num(7, '16')
+ # Operation 9: Dissolve for snapVx.
+ operation = self.utils.get_num(9, '16')
+
+ try:
+ _rc, job = (
+ self.provisionv3.create_element_replica(
+ self.conn, repServiceInstanceName, cloneName, syncType,
+ sourceInstance, extraSpecs, targetInstance))
+ except Exception:
+ LOG.warning(_LW(
+ "Clone failed on V3. Cleaning up the target volume. "
+ "Clone name: %(cloneName)s "),
+ {'cloneName': cloneName})
+ # Check if the copy session exists.
+ storageSystem = targetInstance['SystemName']
+ syncInstanceName = self.utils.find_sync_sv_by_target(
+ self.conn, storageSystem, targetInstance, False)
+ if syncInstanceName is not None:
+ # Break the clone relationship.
+ rc, job = self.provisionv3.break_replication_relationship(
+ self.conn, repServiceInstanceName, syncInstanceName,
+ operation, extraSpecs, True)
+ storageConfigService = (
+ self.utils.find_storage_configuration_service(
+ self.conn, storageSystem))
+ deviceId = targetInstance['DeviceID']
+ volumeName = targetInstance['Name']
+ storageGroupName = self.utils.get_v3_storage_group_name(
+ extraSpecs[POOL], extraSpecs[SLO],
+ extraSpecs[WORKLOAD])
+ rc = self._delete_from_pool_v3(
+ storageConfigService, targetInstance, volumeName,
+ deviceId, storageGroupName, extraSpecs)
+ # Re-throw the exception.
+ raise
- _rc, job = (
- self.provisionv3.create_element_replica(
- self.conn, repServiceInstanceName, cloneName, syncType,
- sourceInstance, extraSpecs, targetInstance))
cloneDict = self.provisionv3.get_volume_dict_from_job(
self.conn, job['Job'])
volumeRef['status'] = 'error_deleting'
modelUpdate['status'] = 'error_deleting'
return modelUpdate, volumes
+
+ def _delete_target_volume_v2(
+ self, storageConfigService, targetVolumeInstance, extraSpecs):
+ """Helper function to delete the clone target volume instance.
+
+ :param storageConfigService: storage configuration service instance
+ :param targetVolumeInstance: clone target volume instance
+ :param extraSpecs: extra specifications
+ """
+ deviceId = targetVolumeInstance['DeviceID']
+ volumeName = targetVolumeInstance['Name']
+ rc = self._delete_from_pool(storageConfigService,
+ targetVolumeInstance,
+ volumeName, deviceId,
+ extraSpecs[FASTPOLICY],
+ extraSpecs)
+ return rc