EMC VMAX Create CG from CG Snapshot
This patch adds support for create CG from CG snapshot in the VMAX driver. implements blueprint emc-vmax-create-cg-from-cgsnapshot Change-Id: Iab13167382ceebbc6eaa165c431c5a09796ce3e9
This commit is contained in:
@@ -3151,6 +3151,45 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
|
||||
conn, volumeInstance, originalName)
|
||||
self.assertEqual(originalName, volumeInstance['ElementName'])
|
||||
|
||||
def test_get_volume_model_updates(self):
|
||||
utils = self.driver.common.utils
|
||||
status = 'status-string'
|
||||
volumes = utils.get_volume_model_updates(
|
||||
None, self.driver.db, self.data.test_CG['id'],
|
||||
status)
|
||||
self.assertEqual(status, volumes[0]['status'])
|
||||
|
||||
@mock.patch.object(
|
||||
emc_vmax_utils.EMCVMAXUtils,
|
||||
'find_group_sync_rg_by_target',
|
||||
return_value="")
|
||||
@mock.patch.object(
|
||||
emc_vmax_common.EMCVMAXCommon,
|
||||
'_find_consistency_group',
|
||||
return_value=(None, EMCVMAXCommonData.test_CG))
|
||||
@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_create_consistencygroup_from_src(
|
||||
self, _mock_volume_type, _mock_storage, _mock_cg, _mock_rg):
|
||||
volumes = []
|
||||
volumes.append(self.data.test_source_volume)
|
||||
snapshots = []
|
||||
self.data.test_snapshot['volume_size'] = "10"
|
||||
snapshots.append(self.data.test_snapshot)
|
||||
model_update, volumes_model_update = (
|
||||
self.driver.create_consistencygroup_from_src(
|
||||
self.data.test_ctxt, self.data.test_CG, volumes,
|
||||
self.data.test_CG_snapshot, snapshots))
|
||||
self.assertEqual({'status': 'available'}, model_update)
|
||||
self.assertEqual([{'status': 'available', 'id': '2'}],
|
||||
volumes_model_update)
|
||||
|
||||
def _cleanup(self):
|
||||
if self.config_file_path:
|
||||
bExists = os.path.exists(self.config_file_path)
|
||||
|
||||
@@ -64,6 +64,8 @@ WORKLOAD = 'storagetype:workload'
|
||||
INTERVAL = 'storagetype:interval'
|
||||
RETRIES = 'storagetype:retries'
|
||||
ISV3 = 'isV3'
|
||||
TRUNCATE_5 = 5
|
||||
TRUNCATE_8 = 8
|
||||
|
||||
emc_opts = [
|
||||
cfg.StrOpt('cinder_emc_config_file',
|
||||
@@ -2523,8 +2525,8 @@ class EMCVMAXCommon(object):
|
||||
targetVolumeInstance = self.utils.find_volume_instance(
|
||||
self.conn, volumeDict, targetVolumeName)
|
||||
LOG.debug("Create target volume for member volume "
|
||||
"source volume: %(memberVol)s "
|
||||
"target volume %(targetVol)s.",
|
||||
"Source volume: %(memberVol)s "
|
||||
"Target volume %(targetVol)s.",
|
||||
{'memberVol': memberInstanceName,
|
||||
'targetVol': targetVolumeInstance.path})
|
||||
self.provision.add_volume_to_cg(self.conn,
|
||||
@@ -3742,7 +3744,7 @@ class EMCVMAXCommon(object):
|
||||
# 8 - Detach operation.
|
||||
# 9 - Dissolve operation.
|
||||
if isSnapshot:
|
||||
# Operation 7: dissolve for snapVx.
|
||||
# Operation 9: dissolve for snapVx.
|
||||
operation = self.utils.get_num(9, '16')
|
||||
else:
|
||||
# Operation 8: detach for clone.
|
||||
@@ -4132,3 +4134,141 @@ class EMCVMAXCommon(object):
|
||||
else:
|
||||
volumeInstanceNames.append(volumeInstance.path)
|
||||
return volumeInstanceNames
|
||||
|
||||
def create_consistencygroup_from_src(self, context, group, volumes,
|
||||
cgsnapshot, snapshots, db):
|
||||
"""Creates the consistency group from source.
|
||||
|
||||
Currently the source can only be a cgsnapshot.
|
||||
|
||||
:param context: the context
|
||||
:param group: the consistency group object to be created
|
||||
:param volumes: volumes in the consistency group
|
||||
:param cgsnapshot: the source consistency group snapshot
|
||||
:param snapshots: snapshots of the source volumes
|
||||
:param db: database
|
||||
:returns: model_update, volumes_model_update
|
||||
model_update is a dictionary of cg status
|
||||
volumes_model_update is a list of dictionaries of volume
|
||||
update
|
||||
"""
|
||||
LOG.debug("Enter EMCVMAXCommon::create_consistencygroup_from_src. "
|
||||
"Group to be created: %(cgId)s, "
|
||||
"Source snapshot: %(cgSnapshot)s.",
|
||||
{'cgId': group['id'],
|
||||
'cgSnapshot': cgsnapshot['consistencygroup_id']})
|
||||
|
||||
volumeTypeId = group['volume_type_id'].replace(",", "")
|
||||
extraSpecs = self._initial_setup(None, volumeTypeId)
|
||||
|
||||
self.create_consistencygroup(context, group)
|
||||
targetCgName = self.utils.truncate_string(group['id'], TRUNCATE_8)
|
||||
|
||||
if not snapshots:
|
||||
exceptionMessage = (_("No source snapshots provided to create "
|
||||
"consistency group %s.") % targetCgName)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exceptionMessage)
|
||||
|
||||
modelUpdate = {'status': 'available'}
|
||||
|
||||
_poolInstanceName, storageSystem = (
|
||||
self._get_pool_and_storage_system(extraSpecs))
|
||||
try:
|
||||
replicationService = self.utils.find_replication_service(
|
||||
self.conn, storageSystem)
|
||||
if replicationService is None:
|
||||
exceptionMessage = (_(
|
||||
"Cannot find replication service on system %s.") %
|
||||
storageSystem)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exceptionMessage)
|
||||
targetCgInstanceName = self._find_consistency_group(
|
||||
replicationService, targetCgName)
|
||||
LOG.debug("Create CG %(targetCg)s from snapshot.",
|
||||
{'targetCg': targetCgInstanceName})
|
||||
|
||||
for volume, snapshot in zip(volumes, snapshots):
|
||||
volumeSizeInbits = int(self.utils.convert_gb_to_bits(
|
||||
snapshot['volume_size']))
|
||||
targetVolumeName = 'targetVol'
|
||||
volume = {'size': int(self.utils.convert_bits_to_gbs(
|
||||
volumeSizeInbits))}
|
||||
if extraSpecs[ISV3]:
|
||||
_rc, volumeDict, _storageSystemName = (
|
||||
self._create_v3_volume(
|
||||
volume, targetVolumeName, volumeSizeInbits,
|
||||
extraSpecs))
|
||||
else:
|
||||
_rc, volumeDict, _storageSystemName = (
|
||||
self._create_composite_volume(
|
||||
volume, targetVolumeName, volumeSizeInbits,
|
||||
extraSpecs))
|
||||
targetVolumeInstance = self.utils.find_volume_instance(
|
||||
self.conn, volumeDict, targetVolumeName)
|
||||
LOG.debug("Create target volume for member snapshot. "
|
||||
"Source snapshot: %(snapshot)s, "
|
||||
"Target volume: %(targetVol)s.",
|
||||
{'snapshot': snapshot['id'],
|
||||
'targetVol': targetVolumeInstance.path})
|
||||
|
||||
self.provision.add_volume_to_cg(self.conn,
|
||||
replicationService,
|
||||
targetCgInstanceName,
|
||||
targetVolumeInstance.path,
|
||||
targetCgName,
|
||||
targetVolumeName,
|
||||
extraSpecs)
|
||||
|
||||
sourceCgName = self.utils.truncate_string(cgsnapshot['id'],
|
||||
TRUNCATE_8)
|
||||
sourceCgInstanceName = self._find_consistency_group(
|
||||
replicationService, sourceCgName)
|
||||
if sourceCgInstanceName is None:
|
||||
exceptionMessage = (_("Cannot find source CG instance. "
|
||||
"consistencygroup_id: %s.") %
|
||||
cgsnapshot['consistencygroup_id'])
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exceptionMessage)
|
||||
relationName = self.utils.truncate_string(group['id'], TRUNCATE_5)
|
||||
if extraSpecs[ISV3]:
|
||||
self.provisionv3.create_group_replica(
|
||||
self.conn, replicationService, sourceCgInstanceName,
|
||||
targetCgInstanceName, relationName, extraSpecs)
|
||||
else:
|
||||
self.provision.create_group_replica(
|
||||
self.conn, replicationService, sourceCgInstanceName,
|
||||
targetCgInstanceName, relationName, extraSpecs)
|
||||
# Break the replica group relationship.
|
||||
rgSyncInstanceName = self.utils.find_group_sync_rg_by_target(
|
||||
self.conn, storageSystem, targetCgInstanceName, extraSpecs,
|
||||
True)
|
||||
|
||||
if rgSyncInstanceName is not None:
|
||||
if extraSpecs[ISV3]:
|
||||
# Operation 9: dissolve for snapVx
|
||||
operation = self.utils.get_num(9, '16')
|
||||
self.provisionv3.break_replication_relationship(
|
||||
self.conn, replicationService, rgSyncInstanceName,
|
||||
operation, extraSpecs)
|
||||
else:
|
||||
self.provision.delete_clone_relationship(
|
||||
self.conn, replicationService,
|
||||
rgSyncInstanceName, extraSpecs)
|
||||
except Exception as ex:
|
||||
modelUpdate['status'] = 'error'
|
||||
cgSnapshotId = cgsnapshot['consistencygroup_id']
|
||||
volumes_model_update = self.utils.get_volume_model_updates(
|
||||
context, db, group['id'], modelUpdate['status'])
|
||||
LOG.error(_LE("Exception: %(ex)s."), {'ex': ex})
|
||||
exceptionMessage = (_("Failed to create CG %(cgName)s "
|
||||
"from snapshot %(cgSnapshot)s.")
|
||||
% {'cgName': targetCgName,
|
||||
'cgSnapshot': cgSnapshotId})
|
||||
LOG.error(exceptionMessage)
|
||||
return modelUpdate, volumes_model_update
|
||||
|
||||
volumes_model_update = self.utils.get_volume_model_updates(
|
||||
context, db, group['id'], modelUpdate['status'])
|
||||
|
||||
return modelUpdate, volumes_model_update
|
||||
|
||||
@@ -41,9 +41,10 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
|
||||
2.2.1 - Support for SE 8.0.3
|
||||
2.2.2 - Update Consistency Group
|
||||
2.2.3 - Pool aware scheduler(multi-pool) support
|
||||
2.2.4 - Create CG from CG snapshot
|
||||
"""
|
||||
|
||||
VERSION = "2.2.3"
|
||||
VERSION = "2.2.4"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -356,3 +357,21 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
|
||||
"""Updates LUNs in consistency group."""
|
||||
return self.common.update_consistencygroup(group, add_volumes,
|
||||
remove_volumes)
|
||||
|
||||
def create_consistencygroup_from_src(self, context, group, volumes,
|
||||
cgsnapshot=None, snapshots=None,
|
||||
source_cg=None, source_vols=None):
|
||||
"""Creates the consistency group from source.
|
||||
|
||||
Currently the source can only be a cgsnapshot.
|
||||
|
||||
:param context: the context
|
||||
:param group: the consistency group object to be created
|
||||
:param volumes: volumes in the consistency group
|
||||
:param cgsnapshot: the source consistency group snapshot
|
||||
:param snapshots: snapshots of the source volumes
|
||||
:param source_cg: the dictionary of a consistency group as source.
|
||||
:param source_vols: a list of volume dictionaries in the source_cg.
|
||||
"""
|
||||
return self.common.create_consistencygroup_from_src(
|
||||
context, group, volumes, cgsnapshot, snapshots, self.db)
|
||||
|
||||
@@ -49,9 +49,10 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
|
||||
2.2.1 - Support for SE 8.0.3
|
||||
2.2.2 - Update Consistency Group
|
||||
2.2.3 - Pool aware scheduler(multi-pool) support
|
||||
2.2.4 - Create CG from CG snapshot
|
||||
"""
|
||||
|
||||
VERSION = "2.2.3"
|
||||
VERSION = "2.2.4"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -359,3 +360,21 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
|
||||
"""Updates LUNs in consistency group."""
|
||||
return self.common.update_consistencygroup(group, add_volumes,
|
||||
remove_volumes)
|
||||
|
||||
def create_consistencygroup_from_src(self, context, group, volumes,
|
||||
cgsnapshot=None, snapshots=None,
|
||||
source_cg=None, source_vols=None):
|
||||
"""Creates the consistency group from source.
|
||||
|
||||
Currently the source can only be a cgsnapshot.
|
||||
|
||||
:param context: the context
|
||||
:param group: the consistency group object to be created
|
||||
:param volumes: volumes in the consistency group
|
||||
:param cgsnapshot: the source consistency group snapshot
|
||||
:param snapshots: snapshots of the source volumes
|
||||
:param source_cg: the dictionary of a consistency group as source.
|
||||
:param source_vols: a list of volume dictionaries in the source_cg.
|
||||
"""
|
||||
return self.common.create_consistencygroup_from_src(
|
||||
context, group, volumes, cgsnapshot, snapshots, self.db)
|
||||
|
||||
@@ -2259,3 +2259,27 @@ class EMCVMAXUtils(object):
|
||||
{'source': sourceDeviceId, 'storageSystem': storageSystem})
|
||||
|
||||
return foundSyncInstanceName
|
||||
|
||||
def get_volume_model_updates(
|
||||
self, context, db, cgId, status='available'):
|
||||
"""Update the volume model's status and return it.
|
||||
|
||||
:param context: the context
|
||||
:param db: cinder database
|
||||
:param cgId: cg id
|
||||
:param status: string value reflects the status of the member volume
|
||||
:returns: volume_model_updates - updated volumes
|
||||
"""
|
||||
volume_model_updates = []
|
||||
volumes = db.volume_get_all_by_group(context, cgId)
|
||||
LOG.info(_LI(
|
||||
"Updating status for CG: %(id)s."),
|
||||
{'id': cgId})
|
||||
if volumes:
|
||||
for volume in volumes:
|
||||
volume_model_updates.append({'id': volume['id'],
|
||||
'status': status})
|
||||
else:
|
||||
LOG.info(_LI("No volume found for CG: %(cg)s."),
|
||||
{'cg': cgId})
|
||||
return volume_model_updates
|
||||
|
||||
Reference in New Issue
Block a user