VMAX driver - allow for multi volume types in Consistency Group
Allow for multiple volume types in the creation of a Consistency Group and ensure that targets have the correct volume types. Change-Id: I14e647d7c6edb35b420a4578c2e0900f3908c6a1 Closes-Bug: #1662359
This commit is contained in:
@@ -19,6 +19,7 @@ import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import uuid
|
||||
from xml.dom import minidom
|
||||
|
||||
import ddt
|
||||
@@ -6534,6 +6535,16 @@ class EMCV3DriverTestCase(test.TestCase):
|
||||
self.data.test_ctxt, self.data.test_volume_v3, self.data.new_type,
|
||||
self.data.diff, self.data.test_host_v3))
|
||||
|
||||
@mock.patch.object(
|
||||
utils.VMAXUtils,
|
||||
'find_volume_instance',
|
||||
return_value=(
|
||||
FakeEcomConnection().EnumerateInstanceNames(
|
||||
"EMC_StorageVolume")[0]))
|
||||
@mock.patch.object(
|
||||
common.VMAXCommon,
|
||||
'_create_v3_volume',
|
||||
return_value=(0, {}, VMAXCommonData.storage_system))
|
||||
@mock.patch.object(
|
||||
utils.VMAXUtils,
|
||||
'find_group_sync_rg_by_target',
|
||||
@@ -6554,22 +6565,33 @@ class EMCV3DriverTestCase(test.TestCase):
|
||||
'_get_pool_and_storage_system',
|
||||
return_value=(None, VMAXCommonData.storage_system))
|
||||
@mock.patch.object(
|
||||
volume_types,
|
||||
'get_volume_type_extra_specs',
|
||||
return_value={'volume_backend_name': 'V3_BE'})
|
||||
utils.VMAXUtils,
|
||||
'get_volumetype_extraspecs',
|
||||
return_value={'pool_name': u'Bronze+DSS+SRP_1+1234567891011'})
|
||||
def test_create_cgsnapshot_v3_success(
|
||||
self, _mock_volume_type, _mock_storage, _mock_cg,
|
||||
_mock_members, mock_rg):
|
||||
_mock_members, mock_rg, mock_create_vol, mock_find):
|
||||
volume = {}
|
||||
snapshot = {}
|
||||
snapshots = []
|
||||
volume['volume_type_id'] = 'abc'
|
||||
volume['size'] = '123'
|
||||
volume['id'] = '123'
|
||||
snapshot['volume'] = volume
|
||||
snapshot['id'] = '456'
|
||||
snapshots.append(snapshot)
|
||||
provisionv3 = self.driver.common.provisionv3
|
||||
provisionv3.create_group_replica = mock.Mock(return_value=(0, None))
|
||||
self.driver.create_cgsnapshot(
|
||||
self.data.test_ctxt, self.data.test_CG_snapshot, [])
|
||||
self.data.test_ctxt, self.data.test_CG_snapshot, snapshots)
|
||||
repServ = self.conn.EnumerateInstanceNames("EMC_ReplicationService")[0]
|
||||
intervals_retries_dict = (
|
||||
{'storagetype:interval': 0, 'storagetype:retries': 0})
|
||||
provisionv3.create_group_replica.assert_called_once_with(
|
||||
self.conn, repServ,
|
||||
VMAXCommonData.test_CG,
|
||||
VMAXCommonData.test_CG, '12de',
|
||||
VMAXCommonData.extra_specs)
|
||||
intervals_retries_dict)
|
||||
|
||||
@mock.patch.object(
|
||||
common.VMAXCommon,
|
||||
@@ -8898,13 +8920,78 @@ class VMAXCommonTest(test.TestCase):
|
||||
def test_get_consistency_group_utils(self, mock_init, mock_pool):
|
||||
common = self.driver.common
|
||||
common.conn = FakeEcomConnection()
|
||||
replicationService, storageSystem, extraSpecs = (
|
||||
replicationService, storageSystem, extraSpecsList, isV3 = (
|
||||
common._get_consistency_group_utils(
|
||||
common.conn, VMAXCommonData.test_CG))
|
||||
self.assertEqual(self.data.extra_specs, extraSpecs)
|
||||
self.assertEqual(
|
||||
self.data.extra_specs, extraSpecsList[0]['extraSpecs'])
|
||||
|
||||
self.assertEqual(common.conn.EnumerateInstanceNames(
|
||||
'EMC_ReplicationService')[0], replicationService)
|
||||
|
||||
@mock.patch.object(
|
||||
common.VMAXCommon,
|
||||
'_get_pool_and_storage_system',
|
||||
return_value=(None, VMAXCommonData.storage_system))
|
||||
@mock.patch.object(
|
||||
utils.VMAXUtils,
|
||||
'get_volumetype_extraspecs',
|
||||
return_value=(VMAXCommonData.multi_pool_extra_specs))
|
||||
def test_get_consistency_group_utils_multi_pool_enabled(
|
||||
self, mock_init, mock_pool):
|
||||
common = self.driver.common
|
||||
common.conn = FakeEcomConnection()
|
||||
replicationService, storageSystem, extraSpecsList, isV3 = (
|
||||
common._get_consistency_group_utils(
|
||||
common.conn, VMAXCommonData.test_CG))
|
||||
self.assertEqual(
|
||||
self.data.multi_pool_extra_specs, extraSpecsList[0]['extraSpecs'])
|
||||
self.assertEqual(1, len(extraSpecsList))
|
||||
self.assertEqual(common.conn.EnumerateInstanceNames(
|
||||
'EMC_ReplicationService')[0], replicationService)
|
||||
|
||||
@mock.patch.object(
|
||||
common.VMAXCommon,
|
||||
'_get_pool_and_storage_system',
|
||||
return_value=(None, VMAXCommonData.storage_system))
|
||||
@mock.patch.object(
|
||||
utils.VMAXUtils,
|
||||
'get_volumetype_extraspecs',
|
||||
return_value=(VMAXCommonData.multi_pool_extra_specs))
|
||||
def test_get_consistency_group_utils_multi_pool_multi_vp(
|
||||
self, mock_init, mock_pool):
|
||||
common = self.driver.common
|
||||
common.conn = FakeEcomConnection()
|
||||
test_CG_multi_vp = consistencygroup.ConsistencyGroup(
|
||||
context=None, name='myCG1', id=uuid.uuid1(),
|
||||
volume_type_id='abc,def',
|
||||
status=fields.ConsistencyGroupStatus.AVAILABLE)
|
||||
replicationService, storageSystem, extraSpecsList, isV3 = (
|
||||
common._get_consistency_group_utils(
|
||||
common.conn, test_CG_multi_vp))
|
||||
self.assertEqual(2, len(extraSpecsList))
|
||||
|
||||
@mock.patch.object(
|
||||
common.VMAXCommon,
|
||||
'_get_pool_and_storage_system',
|
||||
return_value=(None, VMAXCommonData.storage_system))
|
||||
@mock.patch.object(
|
||||
common.VMAXCommon,
|
||||
'_initial_setup',
|
||||
return_value=(VMAXCommonData.extra_specs))
|
||||
def test_get_consistency_group_utils_single_pool_multi_vp(
|
||||
self, mock_init, mock_pool):
|
||||
common = self.driver.common
|
||||
common.conn = FakeEcomConnection()
|
||||
test_CG_multi_vp = consistencygroup.ConsistencyGroup(
|
||||
context=None, name='myCG1', id=uuid.uuid1(),
|
||||
volume_type_id='abc,def',
|
||||
status=fields.ConsistencyGroupStatus.AVAILABLE)
|
||||
self.assertRaises(
|
||||
exception.VolumeBackendAPIException,
|
||||
common._get_consistency_group_utils, common.conn,
|
||||
test_CG_multi_vp)
|
||||
|
||||
def test_update_consistency_group_name(self):
|
||||
common = self.driver.common
|
||||
cg_name = common._update_consistency_group_name(
|
||||
|
||||
@@ -2814,10 +2814,11 @@ class VMAXCommon(object):
|
||||
|
||||
# Find storage system.
|
||||
try:
|
||||
replicationService, storageSystem, extraSpecs = (
|
||||
replicationService, storageSystem, __, __ = (
|
||||
self._get_consistency_group_utils(self.conn, group))
|
||||
interval_retries_dict = self.utils.get_default_intervals_retries()
|
||||
self.provision.create_consistency_group(
|
||||
self.conn, replicationService, cgName, extraSpecs)
|
||||
self.conn, replicationService, cgName, interval_retries_dict)
|
||||
except Exception:
|
||||
exceptionMessage = (_("Failed to create consistency group:"
|
||||
" %(cgName)s.")
|
||||
@@ -2846,7 +2847,7 @@ class VMAXCommon(object):
|
||||
self.conn = self._get_ecom_connection()
|
||||
|
||||
try:
|
||||
replicationService, storageSystem, extraSpecs = (
|
||||
replicationService, storageSystem, __, isV3 = (
|
||||
self._get_consistency_group_utils(self.conn, group))
|
||||
|
||||
storageConfigservice = (
|
||||
@@ -2865,17 +2866,17 @@ class VMAXCommon(object):
|
||||
|
||||
memberInstanceNames = self._get_members_of_replication_group(
|
||||
cgInstanceName)
|
||||
|
||||
interval_retries_dict = self.utils.get_default_intervals_retries()
|
||||
self.provision.delete_consistency_group(self.conn,
|
||||
replicationService,
|
||||
cgInstanceName, cgName,
|
||||
extraSpecs)
|
||||
interval_retries_dict)
|
||||
|
||||
# Do a bulk delete, a lot faster than single deletes.
|
||||
if memberInstanceNames:
|
||||
volumes_model_update, modelUpdate = self._do_bulk_delete(
|
||||
storageSystem, memberInstanceNames, storageConfigservice,
|
||||
volumes, group, extraSpecs[ISV3], extraSpecs)
|
||||
volumes, group, isV3, interval_retries_dict)
|
||||
|
||||
except Exception:
|
||||
exceptionMessage = (_(
|
||||
@@ -2950,7 +2951,7 @@ class VMAXCommon(object):
|
||||
self.conn = self._get_ecom_connection()
|
||||
|
||||
try:
|
||||
replicationService, storageSystem, extraSpecs = (
|
||||
replicationService, storageSystem, extraSpecsDictList, isV3 = (
|
||||
self._get_consistency_group_utils(self.conn, consistencyGroup))
|
||||
|
||||
cgInstanceName, cgName = (
|
||||
@@ -2964,30 +2965,34 @@ class VMAXCommon(object):
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exception_message)
|
||||
|
||||
memberInstanceNames = self._get_members_of_replication_group(
|
||||
cgInstanceName)
|
||||
|
||||
# Create the target consistency group.
|
||||
targetCgName = self._update_consistency_group_name(cgsnapshot)
|
||||
interval_retries_dict = self.utils.get_default_intervals_retries()
|
||||
self.provision.create_consistency_group(
|
||||
self.conn, replicationService, targetCgName, extraSpecs)
|
||||
self.conn, replicationService, targetCgName,
|
||||
interval_retries_dict)
|
||||
targetCgInstanceName, targetCgName = self._find_consistency_group(
|
||||
replicationService, cgsnapshot['id'])
|
||||
LOG.info(_LI("Create target consistency group %(targetCg)s."),
|
||||
{'targetCg': targetCgInstanceName})
|
||||
|
||||
for memberInstanceName in memberInstanceNames:
|
||||
volInstance = self.conn.GetInstance(
|
||||
memberInstanceName, LocalOnly=False)
|
||||
numOfBlocks = volInstance['NumberOfBlocks']
|
||||
blockSize = volInstance['BlockSize']
|
||||
volumeSizeInbits = numOfBlocks * blockSize
|
||||
|
||||
for snapshot in snapshots:
|
||||
volume = snapshot['volume']
|
||||
for extraSpecsDict in extraSpecsDictList:
|
||||
if volume['volume_type_id'] in extraSpecsDict.values():
|
||||
extraSpecs = extraSpecsDict.get('extraSpecs')
|
||||
if 'pool_name' in extraSpecs:
|
||||
extraSpecs = self.utils.update_extra_specs(
|
||||
extraSpecs)
|
||||
if 'size' in volume:
|
||||
volumeSizeInbits = int(self.utils.convert_gb_to_bits(
|
||||
volume['size']))
|
||||
else:
|
||||
volumeSizeInbits = int(self.utils.convert_gb_to_bits(
|
||||
volume['volume_size']))
|
||||
targetVolumeName = 'targetVol'
|
||||
volume = {'size': int(self.utils.convert_bits_to_gbs(
|
||||
volumeSizeInbits))}
|
||||
|
||||
if extraSpecs[ISV3]:
|
||||
if isV3:
|
||||
_rc, volumeDict, _storageSystemName = (
|
||||
self._create_v3_volume(
|
||||
volume, targetVolumeName, volumeSizeInbits,
|
||||
@@ -3002,7 +3007,7 @@ class VMAXCommon(object):
|
||||
LOG.debug("Create target volume for member volume "
|
||||
"Source volume: %(memberVol)s "
|
||||
"Target volume %(targetVol)s.",
|
||||
{'memberVol': memberInstanceName,
|
||||
{'memberVol': volume['id'],
|
||||
'targetVol': targetVolumeInstance.path})
|
||||
self.provision.add_volume_to_cg(self.conn,
|
||||
replicationService,
|
||||
@@ -3012,39 +3017,9 @@ class VMAXCommon(object):
|
||||
targetVolumeName,
|
||||
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, extraSpecs)
|
||||
else:
|
||||
self.provision.create_group_replica(
|
||||
self.conn, replicationService, cgInstanceName,
|
||||
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:
|
||||
repservice = self.utils.find_replication_service(
|
||||
self.conn, storageSystem)
|
||||
if repservice is None:
|
||||
exception_message = (_(
|
||||
"Cannot find Replication service on system %s.") %
|
||||
storageSystem)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exception_message)
|
||||
if extraSpecs[ISV3]:
|
||||
# Operation 7: dissolve for snapVx.
|
||||
operation = self.utils.get_num(9, '16')
|
||||
self.provisionv3.break_replication_relationship(
|
||||
self.conn, repservice, rgSyncInstanceName, operation,
|
||||
extraSpecs)
|
||||
else:
|
||||
self.provision.delete_clone_relationship(self.conn, repservice,
|
||||
rgSyncInstanceName,
|
||||
extraSpecs)
|
||||
self._create_group_and_break_relationship(
|
||||
isV3, cgsnapshot['id'], replicationService, cgInstanceName,
|
||||
targetCgInstanceName, storageSystem, interval_retries_dict)
|
||||
|
||||
except Exception:
|
||||
exceptionMessage = (_("Failed to create snapshot for cg:"
|
||||
@@ -3062,6 +3037,53 @@ class VMAXCommon(object):
|
||||
|
||||
return modelUpdate, snapshots_model_update
|
||||
|
||||
def _create_group_and_break_relationship(
|
||||
self, isV3, cgsnapshotId, replicationService, cgInstanceName,
|
||||
targetCgInstanceName, storageSystem, interval_retries_dict):
|
||||
"""Creates a cg group and deletes the relationship.
|
||||
|
||||
:param isV3: the context
|
||||
:param cgsnapshotId: the consistency group snapshot id
|
||||
:param replicationService: replication service
|
||||
:param cgInstanceName: cg instance name
|
||||
:param targetCgInstanceName: target cg instance name
|
||||
:param storageSystem: storage system
|
||||
:param interval_retries_dict:
|
||||
"""
|
||||
# Less than 5 characters relationship name.
|
||||
relationName = self.utils.truncate_string(cgsnapshotId, 5)
|
||||
if isV3:
|
||||
self.provisionv3.create_group_replica(
|
||||
self.conn, replicationService, cgInstanceName,
|
||||
targetCgInstanceName, relationName, interval_retries_dict)
|
||||
else:
|
||||
self.provision.create_group_replica(
|
||||
self.conn, replicationService, cgInstanceName,
|
||||
targetCgInstanceName, relationName, interval_retries_dict)
|
||||
# Break the replica group relationship.
|
||||
rgSyncInstanceName = self.utils.find_group_sync_rg_by_target(
|
||||
self.conn, storageSystem, targetCgInstanceName,
|
||||
interval_retries_dict, True)
|
||||
if rgSyncInstanceName is not None:
|
||||
repservice = self.utils.find_replication_service(
|
||||
self.conn, storageSystem)
|
||||
if repservice is None:
|
||||
exception_message = (_(
|
||||
"Cannot find Replication service on system %s.") %
|
||||
storageSystem)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exception_message)
|
||||
if isV3:
|
||||
# Operation 7: dissolve for snapVx.
|
||||
operation = self.utils.get_num(9, '16')
|
||||
self.provisionv3.break_replication_relationship(
|
||||
self.conn, repservice, rgSyncInstanceName, operation,
|
||||
interval_retries_dict)
|
||||
else:
|
||||
self.provision.delete_clone_relationship(self.conn, repservice,
|
||||
rgSyncInstanceName,
|
||||
interval_retries_dict)
|
||||
|
||||
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
|
||||
"""Delete a cgsnapshot.
|
||||
|
||||
@@ -3086,11 +3108,12 @@ class VMAXCommon(object):
|
||||
self.conn = self._get_ecom_connection()
|
||||
|
||||
try:
|
||||
replicationService, storageSystem, extraSpecs = (
|
||||
replicationService, storageSystem, __, isV3 = (
|
||||
self._get_consistency_group_utils(self.conn, consistencyGroup))
|
||||
interval_retries_dict = self.utils.get_default_intervals_retries()
|
||||
model_update, snapshots = self._delete_cg_and_members(
|
||||
storageSystem, cgsnapshot, model_update,
|
||||
snapshots, extraSpecs)
|
||||
snapshots, isV3, interval_retries_dict)
|
||||
for snapshot in snapshots:
|
||||
snapshots_model_update.append(
|
||||
{'id': snapshot['id'],
|
||||
@@ -4339,13 +4362,15 @@ class VMAXCommon(object):
|
||||
deviceId, extraSpecs)
|
||||
|
||||
def _delete_cg_and_members(
|
||||
self, storageSystem, cgsnapshot, modelUpdate, volumes, extraSpecs):
|
||||
self, storageSystem, cgsnapshot, modelUpdate, volumes, isV3,
|
||||
extraSpecs):
|
||||
"""Helper function to delete a consistencygroup and its member volumes.
|
||||
|
||||
:param storageSystem: storage system
|
||||
:param cgsnapshot: consistency group snapshot
|
||||
:param modelUpdate: dict -- the model update dict
|
||||
:param volumes: the list of member volumes
|
||||
:param isV3: boolean
|
||||
:param extraSpecs: extra specifications
|
||||
:returns: dict -- modelUpdate
|
||||
:returns: list -- the updated list of member volumes
|
||||
@@ -4387,7 +4412,7 @@ class VMAXCommon(object):
|
||||
{'cg': cgInstanceName,
|
||||
'numVols': len(memberInstanceNames),
|
||||
'memVols': memberInstanceNames})
|
||||
if extraSpecs[ISV3]:
|
||||
if isV3:
|
||||
self.provisionv3.delete_volume_from_pool(
|
||||
self.conn, storageConfigservice,
|
||||
memberInstanceNames, None, extraSpecs)
|
||||
@@ -4691,7 +4716,7 @@ class VMAXCommon(object):
|
||||
self.conn = self._get_ecom_connection()
|
||||
|
||||
try:
|
||||
replicationService, storageSystem, extraSpecs = (
|
||||
replicationService, storageSystem, __, __ = (
|
||||
self._get_consistency_group_utils(self.conn, group))
|
||||
cgInstanceName = (
|
||||
self._find_consistency_group(
|
||||
@@ -4700,17 +4725,18 @@ class VMAXCommon(object):
|
||||
raise exception.ConsistencyGroupNotFound(
|
||||
consistencygroup_id=cg_name)
|
||||
# Add volume(s) to a consistency group
|
||||
interval_retries_dict = self.utils.get_default_intervals_retries()
|
||||
if add_instance_names:
|
||||
self.provision.add_volume_to_cg(
|
||||
self.conn, replicationService, cgInstanceName,
|
||||
add_instance_names, cg_name, None,
|
||||
extraSpecs)
|
||||
interval_retries_dict)
|
||||
# Remove volume(s) from a consistency group
|
||||
if remove_instance_names:
|
||||
self.provision.remove_volume_from_cg(
|
||||
self.conn, replicationService, cgInstanceName,
|
||||
remove_instance_names, cg_name, None,
|
||||
extraSpecs)
|
||||
interval_retries_dict)
|
||||
except exception.ConsistencyGroupNotFound:
|
||||
raise
|
||||
except Exception as ex:
|
||||
@@ -4744,8 +4770,6 @@ class VMAXCommon(object):
|
||||
source_vols):
|
||||
"""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
|
||||
@@ -4781,7 +4805,7 @@ class VMAXCommon(object):
|
||||
modelUpdate = {'status': fields.ConsistencyGroupStatus.AVAILABLE}
|
||||
|
||||
try:
|
||||
replicationService, storageSystem, extraSpecs = (
|
||||
replicationService, storageSystem, extraSpecsDictList, isV3 = (
|
||||
self._get_consistency_group_utils(self.conn, group))
|
||||
if replicationService is None:
|
||||
exceptionMessage = (_(
|
||||
@@ -4802,67 +4826,22 @@ class VMAXCommon(object):
|
||||
else:
|
||||
volumeSizeInbits = int(self.utils.convert_gb_to_bits(
|
||||
source_vol_or_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)s, "
|
||||
"Target volume: %(targetVol)s.",
|
||||
{'snapshot': source_vol_or_snapshot['id'],
|
||||
'targetVol': targetVolumeInstance.path})
|
||||
for extraSpecsDict in extraSpecsDictList:
|
||||
if volume['volume_type_id'] in extraSpecsDict.values():
|
||||
extraSpecs = extraSpecsDict.get('extraSpecs')
|
||||
if 'pool_name' in extraSpecs:
|
||||
extraSpecs = self.utils.update_extra_specs(
|
||||
extraSpecs)
|
||||
self._create_vol_and_add_to_cg(
|
||||
volumeSizeInbits, replicationService,
|
||||
targetCgInstanceName, targetCgName,
|
||||
source_vol_or_snapshot['id'], extraSpecs)
|
||||
|
||||
self.provision.add_volume_to_cg(self.conn,
|
||||
replicationService,
|
||||
targetCgInstanceName,
|
||||
targetVolumeInstance.path,
|
||||
targetCgName,
|
||||
targetVolumeName,
|
||||
extraSpecs)
|
||||
sourceCgInstanceName, sourceCgName = self._find_consistency_group(
|
||||
replicationService, source_id)
|
||||
if sourceCgInstanceName is None:
|
||||
exceptionMessage = (_("Cannot find source CG instance. "
|
||||
"consistencygroup_id: %s.") %
|
||||
source_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)
|
||||
interval_retries_dict = self.utils.get_default_intervals_retries()
|
||||
self._break_replica_group_relationship(
|
||||
replicationService, source_id, group['id'],
|
||||
targetCgInstanceName, storageSystem, interval_retries_dict,
|
||||
isV3)
|
||||
except Exception:
|
||||
exceptionMessage = (_("Failed to create CG %(cgName)s "
|
||||
"from source %(cgSnapshot)s.")
|
||||
@@ -4875,6 +4854,94 @@ class VMAXCommon(object):
|
||||
|
||||
return modelUpdate, volumes_model_update
|
||||
|
||||
def _break_replica_group_relationship(
|
||||
self, replicationService, source_id, group_id,
|
||||
targetCgInstanceName, storageSystem, extraSpecs, isV3):
|
||||
"""Breaks the replica group relationship.
|
||||
|
||||
:param replicationService: replication service
|
||||
:param source_id: source identifier
|
||||
:param group_id: group identifier
|
||||
:param targetCgInstanceName: target CG instance
|
||||
:param storageSystem: storage system
|
||||
:param extraSpecs: additional info
|
||||
"""
|
||||
sourceCgInstanceName, sourceCgName = self._find_consistency_group(
|
||||
replicationService, source_id)
|
||||
if sourceCgInstanceName is None:
|
||||
exceptionMessage = (_("Cannot find source CG instance. "
|
||||
"consistencygroup_id: %s.") %
|
||||
source_id)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exceptionMessage)
|
||||
relationName = self.utils.truncate_string(group_id, TRUNCATE_5)
|
||||
if 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 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)
|
||||
|
||||
def _create_vol_and_add_to_cg(
|
||||
self, volumeSizeInbits, replicationService,
|
||||
targetCgInstanceName, targetCgName, source_id, extraSpecs):
|
||||
"""Creates volume and adds to CG.
|
||||
|
||||
:param context: the context
|
||||
:param volumeSizeInbits: volume size in bits
|
||||
:param replicationService: replication service
|
||||
:param targetCgInstanceName: target cg instance
|
||||
:param targetCgName: target cg name
|
||||
:param source_id: source identifier
|
||||
:param extraSpecs: additional info
|
||||
"""
|
||||
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)s, "
|
||||
"Target volume: %(targetVol)s.",
|
||||
{'snapshot': source_id,
|
||||
'targetVol': targetVolumeInstance.path})
|
||||
|
||||
self.provision.add_volume_to_cg(self.conn,
|
||||
replicationService,
|
||||
targetCgInstanceName,
|
||||
targetVolumeInstance.path,
|
||||
targetCgName,
|
||||
targetVolumeName,
|
||||
extraSpecs)
|
||||
|
||||
def _find_ip_protocol_endpoints(self, conn, storageSystemName,
|
||||
portgroupname):
|
||||
"""Find the IP protocol endpoint for ISCSI.
|
||||
@@ -4986,20 +5053,43 @@ class VMAXCommon(object):
|
||||
|
||||
:param conn: ecom connection
|
||||
:param group: the consistency group object to be created
|
||||
:return: replicationService, storageSystem, extraSpecs
|
||||
:return: replicationService, storageSystem, extraSpecs, isV3
|
||||
"""
|
||||
storageSystems = set()
|
||||
extraSpecsDictList = []
|
||||
isV3 = False
|
||||
|
||||
volumeTypeIds = group.volume_type_id.split(",")
|
||||
volumeTypeIds = group.get('volume_type_ids')
|
||||
if not volumeTypeIds:
|
||||
volumeTypeIds = group.volume_type_id.split(",")
|
||||
|
||||
for volumeTypeId in volumeTypeIds:
|
||||
if volumeTypeId:
|
||||
extraSpecs = self._initial_setup(None, volumeTypeId)
|
||||
extraSpecsDict = {}
|
||||
extraSpecs = self.utils.get_volumetype_extraspecs(
|
||||
None, volumeTypeId)
|
||||
if 'pool_name' in extraSpecs:
|
||||
isV3 = True
|
||||
extraSpecs = self.utils.update_extra_specs(
|
||||
extraSpecs)
|
||||
extraSpecs[ISV3] = True
|
||||
else:
|
||||
# Without multipool we cannot support multiple volumetypes.
|
||||
if len(volumeTypeIds) == 1:
|
||||
extraSpecs = self._initial_setup(None, volumeTypeId)
|
||||
else:
|
||||
msg = (_("We cannot support multiple volume types if "
|
||||
"multi pool functionality is not enabled."))
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
__, storageSystem = (
|
||||
self._get_pool_and_storage_system(extraSpecs))
|
||||
if storageSystem:
|
||||
storageSystems.add(storageSystem)
|
||||
extraSpecsDict["volumeTypeId"] = volumeTypeId
|
||||
extraSpecsDict["extraSpecs"] = extraSpecs
|
||||
extraSpecsDictList.append(extraSpecsDict)
|
||||
|
||||
if len(storageSystems) != 1:
|
||||
if not storageSystems:
|
||||
@@ -5015,8 +5105,7 @@ class VMAXCommon(object):
|
||||
storageSystem = storageSystems.pop()
|
||||
replicationService = self.utils.find_replication_service(
|
||||
conn, storageSystem)
|
||||
|
||||
return replicationService, storageSystem, extraSpecs
|
||||
return replicationService, storageSystem, extraSpecsDictList, isV3
|
||||
|
||||
def _update_consistency_group_name(self, group):
|
||||
"""Format id and name consistency group
|
||||
|
||||
@@ -80,6 +80,7 @@ class VMAXUtils(object):
|
||||
SLO = 'storagetype:slo'
|
||||
WORKLOAD = 'storagetype:workload'
|
||||
POOL = 'storagetype:pool'
|
||||
ARRAY = 'storagetype:array'
|
||||
DISABLECOMPRESSION = 'storagetype:disablecompression'
|
||||
|
||||
def __init__(self, prtcl):
|
||||
@@ -2996,3 +2997,30 @@ class VMAXUtils(object):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def update_extra_specs(self, extraSpecs):
|
||||
"""Update extra specs.
|
||||
|
||||
:param extraSpecs: the additional info
|
||||
:return: extraSpecs
|
||||
"""
|
||||
try:
|
||||
poolDetails = extraSpecs['pool_name'].split('+')
|
||||
extraSpecs[self.SLO] = poolDetails[0]
|
||||
extraSpecs[self.WORKLOAD] = poolDetails[1]
|
||||
extraSpecs[self.POOL] = poolDetails[2]
|
||||
extraSpecs[self.ARRAY] = poolDetails[3]
|
||||
except KeyError:
|
||||
LOG.error(_LE("Error parsing SLO, workload from "
|
||||
"the provided extra_specs."))
|
||||
return extraSpecs
|
||||
|
||||
def get_default_intervals_retries(self):
|
||||
"""Get the default intervals and retries.
|
||||
|
||||
:return: default_dict
|
||||
"""
|
||||
default_dict = {}
|
||||
default_dict[INTERVAL] = INTERVAL_10_SEC
|
||||
default_dict[RETRIES] = JOB_RETRIES
|
||||
return default_dict
|
||||
|
||||
Reference in New Issue
Block a user