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:
Xing Yang
2015-08-13 09:15:33 -04:00
parent 5a47eecd0e
commit 93974f845d
5 changed files with 246 additions and 5 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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