]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
EMC VMAX - SnapVX and other snapshot improvements
authorHelen Walsh <helen.walsh@emc.com>
Sat, 13 Feb 2016 12:12:47 +0000 (12:12 +0000)
committerHelen Walsh <helen.walsh@emc.com>
Thu, 10 Mar 2016 20:20:04 +0000 (20:20 +0000)
Using snapVX VMAX3 for creating snapshot and volume from
snapshot. Improvements in wait_for_sync to determine when
the max number of retries has been reached.

Change-Id: Ifc5f80637ef7b86a6d3719b3a1b152556c6919a6
Closes-Bug: #1522821

cinder/tests/unit/test_emc_vmax.py
cinder/volume/drivers/emc/emc_vmax_common.py
cinder/volume/drivers/emc/emc_vmax_fc.py
cinder/volume/drivers/emc/emc_vmax_iscsi.py
cinder/volume/drivers/emc/emc_vmax_provision.py
cinder/volume/drivers/emc/emc_vmax_provision_v3.py
cinder/volume/drivers/emc/emc_vmax_utils.py

index a297145ac004174b3430f07cbefa1352daba7dae..7d7a8980a67337c2df5f317a5f8c7f8dd5597071 100644 (file)
@@ -533,7 +533,7 @@ class FakeEcomConnection(object):
                      EMCCollections=None, InitiatorMaskingGroup=None,
                      DeviceMaskingGroup=None, TargetMaskingGroup=None,
                      ProtocolController=None, StorageID=None, IDType=None,
-                     WaitForCopyState=None):
+                     WaitForCopyState=None, Collections=None):
 
         rc = 0
         myjob = SE_ConcreteJob()
@@ -2331,7 +2331,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         self.driver.utils._is_sync_complete = mock.Mock(
             return_value=True)
         rc = self.driver.utils.wait_for_sync(conn, mysync)
-        self.assertIsNone(rc)
+        self.assertIsNotNone(rc)
         self.driver.utils._is_sync_complete.assert_called_once_with(
             conn, mysync)
         self.assertTrue(self.driver.utils._is_sync_complete.return_value)
@@ -2341,7 +2341,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         loopingcall_orig = loopingcall.FixedIntervalLoopingCall
         loopingcall.FixedIntervalLoopingCall = mock.Mock()
         rc = self.driver.utils.wait_for_sync(conn, mysync)
-        self.assertIsNone(rc)
+        self.assertIsNotNone(rc)
         loopingcall.FixedIntervalLoopingCall.assert_called_once_with(
             mock.ANY)
         loopingcall.FixedIntervalLoopingCall.reset_mock()
@@ -2363,7 +2363,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         self.driver.utils._is_sync_complete = mock.Mock(
             return_value=True)
         rc = self.driver.utils.wait_for_sync(conn, mysync, extraSpecs)
-        self.assertIsNone(rc)
+        self.assertIsNotNone(rc)
         self.driver.utils._is_sync_complete.assert_called_once_with(
             conn, mysync)
         self.assertTrue(self.driver.utils._is_sync_complete.return_value)
@@ -2377,7 +2377,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         loopingcall_orig = loopingcall.FixedIntervalLoopingCall
         loopingcall.FixedIntervalLoopingCall = mock.Mock()
         rc = self.driver.utils.wait_for_sync(conn, mysync)
-        self.assertIsNone(rc)
+        self.assertIsNotNone(rc)
         loopingcall.FixedIntervalLoopingCall.assert_called_once_with(
             mock.ANY)
         loopingcall.FixedIntervalLoopingCall.reset_mock()
@@ -7690,8 +7690,8 @@ class EMCVMAXFCTest(test.TestCase):
 
 class EMCVMAXUtilsTest(test.TestCase):
     def setUp(self):
-
         self.data = EMCVMAXCommonData()
+
         super(EMCVMAXUtilsTest, self).setUp()
 
         configuration = mock.Mock()
@@ -7726,3 +7726,86 @@ class EMCVMAXUtilsTest(test.TestCase):
             exception.VolumeBackendAPIException,
             self.driver.utils.get_protocol_controller,
             conn, hardwareid)
+
+    def test_set_target_element_supplier_in_rsd(self):
+        conn = FakeEcomConnection()
+        extraSpecs = self.data.extra_specs
+        repServiceInstanceName = (
+            self.driver.utils.find_replication_service(
+                conn, self.data.storage_system))
+        rsdInstance = self.driver.utils.set_target_element_supplier_in_rsd(
+            conn, repServiceInstanceName,
+            emc_vmax_common.SNAPVX_REPLICATION_TYPE,
+            emc_vmax_common.CREATE_NEW_TARGET, extraSpecs)
+        self.assertIsNotNone(rsdInstance)
+
+    def test_set_copy_methodology_in_rsd(self):
+        conn = FakeEcomConnection()
+        extraSpecs = self.data.extra_specs
+        repServiceInstanceName = (
+            self.driver.utils.find_replication_service(
+                conn, self.data.storage_system))
+        rsdInstance = self.driver.utils.set_copy_methodology_in_rsd(
+            conn, repServiceInstanceName,
+            emc_vmax_provision.SYNC_CLONE_LOCAL,
+            emc_vmax_provision.COPY_ON_WRITE, extraSpecs)
+        self.assertIsNotNone(rsdInstance)
+
+
+class EMCVMAXCommonTest(test.TestCase):
+    def setUp(self):
+        self.data = EMCVMAXCommonData()
+
+        super(EMCVMAXCommonTest, self).setUp()
+
+        configuration = mock.Mock()
+        configuration.safe_get.return_value = 'CommonTests'
+        configuration.config_group = 'CommonTests'
+        emc_vmax_common.EMCVMAXCommon._gather_info = mock.Mock()
+        driver = emc_vmax_iscsi.EMCVMAXISCSIDriver(configuration=configuration)
+        driver.db = FakeDB()
+        self.driver = driver
+        self.driver.utils = emc_vmax_utils.EMCVMAXUtils(object)
+
+    @mock.patch.object(
+        emc_vmax_common.EMCVMAXCommon,
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system))
+    def test_create_duplicate_volume(self, mock_pool):
+        common = self.driver.common
+        common.conn = FakeEcomConnection()
+        volumeInstanceName = (
+            common.conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
+        sourceInstance = common.conn.GetInstance(volumeInstanceName)
+        cloneName = "SS-V3-Vol"
+        extraSpecs = {'volume_backend_name': 'V3_BE',
+                      'isV3': True,
+                      'storagetype:pool': 'SRP_1',
+                      'storagetype:workload': 'DSS',
+                      'storagetype:slo': 'Bronze'}
+        targetInstance = common.conn.GetInstance(volumeInstanceName)
+        common.utils.find_volume_instance = mock.Mock(
+            return_value=targetInstance)
+        duplicateVolumeInstance = self.driver.common._create_duplicate_volume(
+            sourceInstance, cloneName, extraSpecs)
+        self.assertIsNotNone(duplicateVolumeInstance)
+
+    def test_cleanup_target(self):
+        common = self.driver.common
+        common.conn = FakeEcomConnection()
+        volumeInstanceName = (
+            common.conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
+        extraSpecs = {'volume_backend_name': 'V3_BE',
+                      'isV3': True,
+                      'storagetype:pool': 'SRP_1',
+                      'storagetype:workload': 'DSS',
+                      'storagetype:slo': 'Bronze'}
+        targetInstance = common.conn.GetInstance(volumeInstanceName)
+        repServiceInstanceName = (
+            self.driver.utils.find_replication_service(
+                common.conn, self.data.storage_system))
+        common.utils.find_sync_sv_by_target = mock.Mock(
+            return_value=(None, None))
+
+        self.driver.common._cleanup_target(
+            repServiceInstanceName, targetInstance, extraSpecs)
index 164b3fa747b29565ecc927a7fd602b32667c30d7..1664215c18d8c47106f18c1701dd4aa4b5a77cfb 100644 (file)
@@ -69,6 +69,10 @@ RETRIES = 'storagetype:retries'
 ISV3 = 'isV3'
 TRUNCATE_5 = 5
 TRUNCATE_8 = 8
+SNAPVX = 7
+DISSOLVE_SNAPVX = 9
+CREATE_NEW_TARGET = 2
+SNAPVX_REPLICATION_TYPE = 6
 
 emc_opts = [
     cfg.StrOpt('cinder_emc_config_file',
@@ -2372,6 +2376,12 @@ class EMCVMAXCommon(object):
 
         if not extraSpecs[ISV3]:
             snapshotInstance = self._find_lun(snapshot)
+            if snapshotInstance is None:
+                LOG.error(_LE(
+                    "Snapshot %(snapshotname)s not found on the array. "
+                    "No volume to delete."),
+                    {'snapshotname': snapshotname})
+                return (-1, snapshotname)
             storageSystem = snapshotInstance['SystemName']
 
             # Wait for it to fully sync in case there is an ongoing
@@ -2937,13 +2947,9 @@ class EMCVMAXCommon(object):
         :param extraSpecs: extra specifications
         :returns: sgInstanceName
         """
-        storageGroupName = self.utils.get_v3_storage_group_name(
-            poolName, slo, workload)
-        controllerConfigService = (
-            self.utils.find_controller_configuration_service(
-                self.conn, storageSystemName))
-        sgInstanceName = self.utils.find_storage_masking_group(
-            self.conn, controllerConfigService, storageGroupName)
+        storageGroupName, controllerConfigService, sgInstanceName = (
+            self.utils.get_v3_default_sg_instance_name(
+                self.conn, poolName, slo, workload, storageSystemName))
         if sgInstanceName is None:
             sgInstanceName = self.provisionv3.create_storage_group_v3(
                 self.conn, controllerConfigService, storageGroupName,
@@ -3784,65 +3790,43 @@ class EMCVMAXCommon(object):
         :returns: dict -- cloneDict
         """
         cloneName = cloneVolume['name']
-        # Default syncType 8: clone.
-        syncType = self.utils.get_num(8, '16')
-        # Default operation 8: Detach for clone.
-        operation = self.utils.get_num(8, '16')
-
-        numOfBlocks = sourceInstance['NumberOfBlocks']
-        blockSize = sourceInstance['BlockSize']
-        volumeSizeInbits = numOfBlocks * blockSize
-
-        volume = {'size':
-                  int(self.utils.convert_bits_to_gbs(volumeSizeInbits))}
-        _rc, volumeDict, _storageSystemName = (
-            self._create_v3_volume(
-                volume, cloneName, volumeSizeInbits, extraSpecs))
-        targetInstance = self.utils.find_volume_instance(
-            self.conn, volumeDict, cloneName)
-        LOG.debug("Create replica target volume "
-                  "source volume: %(sourceVol)s, "
-                  "target volume: %(targetVol)s.",
-                  {'sourceVol': sourceInstance.path,
-                   'targetVol': targetInstance.path})
+        # SyncType 7: snap, VG3R default snapshot is snapVx.
+        syncType = self.utils.get_num(SNAPVX, '16')
+        # Operation 9: Dissolve for snapVx.
+        operation = self.utils.get_num(DISSOLVE_SNAPVX, '16')
+        rsdInstance = None
+        targetInstance = None
         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')
+            rsdInstance = self.utils.set_target_element_supplier_in_rsd(
+                self.conn, repServiceInstanceName, SNAPVX_REPLICATION_TYPE,
+                CREATE_NEW_TARGET, extraSpecs)
+        else:
+            targetInstance = self._create_duplicate_volume(
+                sourceInstance, cloneName, extraSpecs)
 
         try:
             _rc, job = (
                 self.provisionv3.create_element_replica(
                     self.conn, repServiceInstanceName, cloneName, syncType,
-                    sourceInstance, extraSpecs, targetInstance))
+                    sourceInstance, extraSpecs, targetInstance, rsdInstance))
         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']
-            rc = self._delete_from_pool_v3(
-                storageConfigService, targetInstance, volumeName,
-                deviceId, extraSpecs)
-            # Re-throw the exception.
-            raise
+            if targetInstance:
+                self._cleanup_target(
+                    repServiceInstanceName, targetInstance, extraSpecs)
+                # Re-throw the exception.
+                raise
 
         cloneDict = self.provisionv3.get_volume_dict_from_job(
             self.conn, job['Job'])
+        targetVolumeInstance = (
+            self.provisionv3.get_volume_from_job(self.conn, job['Job']))
+        LOG.info(_LI("The target instance device id is: %(deviceid)s."),
+                 {'deviceid': targetVolumeInstance['DeviceID']})
 
         cloneVolume['provider_location'] = six.text_type(cloneDict)
 
@@ -3850,21 +3834,36 @@ class EMCVMAXCommon(object):
             self._find_storage_sync_sv_sv(cloneVolume, sourceVolume,
                                           extraSpecs, True))
 
-        # Detach/dissolve the clone/snap relationship.
-        # 8 - Detach operation.
-        # 9 - Dissolve operation.
-        if isSnapshot:
-            # Operation 9: dissolve for snapVx.
-            operation = self.utils.get_num(9, '16')
-        else:
-            # Operation 8: detach for clone.
-            operation = self.utils.get_num(8, '16')
-
         rc, job = self.provisionv3.break_replication_relationship(
             self.conn, repServiceInstanceName, syncInstanceName,
             operation, extraSpecs)
         return rc, cloneDict
 
+    def _cleanup_target(
+            self, repServiceInstanceName, targetInstance, extraSpecs):
+        """cleanup target after exception
+
+        :param repServiceInstanceName: the replication service
+        :param targetInstance: the target instance
+        :param extraSpecs: extra specifications
+        """
+        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.
+            self.provisionv3.break_replication_relationship(
+                self.conn, repServiceInstanceName, syncInstanceName,
+                DISSOLVE_SNAPVX, extraSpecs, True)
+        storageConfigService = (
+            self.utils.find_storage_configuration_service(
+                self.conn, storageSystem))
+        deviceId = targetInstance['DeviceID']
+        volumeName = targetInstance['Name']
+        self._delete_from_pool_v3(
+            storageConfigService, targetInstance, volumeName,
+            deviceId, extraSpecs)
+
     def _delete_cg_and_members(
             self, storageSystem, cgName, modelUpdate, volumes, extraSpecs):
         """Helper function to delete a consistencygroup and its member volumes.
@@ -4434,3 +4433,30 @@ class EMCVMAXCommon(object):
             volumeName, new_size_in_bits, extraSpecs)
 
         return rc, volumeDict
+
+    def _create_duplicate_volume(
+            self, sourceInstance, cloneName, extraSpecs):
+        """Create a volume in the same dimensions of the source volume.
+
+        :param sourceInstance: the source volume instance
+        :param cloneName: the user supplied snap name
+        :param extraSpecs: additional info
+        :returns: targetInstance
+        """
+        numOfBlocks = sourceInstance['NumberOfBlocks']
+        blockSize = sourceInstance['BlockSize']
+        volumeSizeInbits = numOfBlocks * blockSize
+
+        volume = {'size':
+                  int(self.utils.convert_bits_to_gbs(volumeSizeInbits))}
+        _rc, volumeDict, _storageSystemName = (
+            self._create_v3_volume(
+                volume, cloneName, volumeSizeInbits, extraSpecs))
+        targetInstance = self.utils.find_volume_instance(
+            self.conn, volumeDict, cloneName)
+        LOG.debug("Create replica target volume "
+                  "Source Volume: %(sourceVol)s, "
+                  "Target Volume: %(targetVol)s.",
+                  {'sourceVol': sourceInstance.path,
+                   'targetVol': targetInstance.path})
+        return targetInstance
index f5f1de30b83367e17fac8803f09459682148113d..960b09abf0aae1dad79f2b8252d83c319b1d957a 100644 (file)
@@ -60,6 +60,7 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
               - Changing PercentSynced to CopyState (bug #1517103)
               - Getting iscsi ip from port in existing masking view
               - Replacement of EMCGetTargetEndpoints api (bug #1512791)
+              - VMAX3 snapvx improvements (bug #1522821)
     """
 
     VERSION = "2.3.0"
index 66d3fc0e75cba72e177c15cd95f4b8a8262b9d28..ec7ebf456b009b376ce547060e590ae87f12a64c 100644 (file)
@@ -66,6 +66,7 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
               - Changing PercentSynced to CopyState (bug #1517103)
               - Getting iscsi ip from port in existing masking view
               - Replacement of EMCGetTargetEndpoints api (bug #1512791)
+              - VMAX3 snapvx improvements (bug #1522821)
     """
 
     VERSION = "2.3.0"
index 2a769a826905fbdf7458741f839174cd3052612e..c38174a06ea5ba4a0a07fc39dfd478fb1cee6dfa 100644 (file)
@@ -30,6 +30,9 @@ POSTGROUPTYPE = 3
 EMC_ROOT = 'root/emc'
 THINPROVISIONINGCOMPOSITE = 32768
 THINPROVISIONING = 5
+SYNC_CLONE_LOCAL = 10
+COPY_ON_WRITE = 6
+TF_CLONE = 8
 
 
 class EMCVMAXProvision(object):
@@ -693,52 +696,18 @@ class EMCVMAXProvision(object):
         """
         if copyOnWrite:
             startTime = time.time()
-            repServiceCapabilityInstanceNames = conn.AssociatorNames(
-                repServiceInstanceName,
-                ResultClass='CIM_ReplicationServiceCapabilities',
-                AssocClass='CIM_ElementCapabilities')
-            repServiceCapabilityInstanceName = (
-                repServiceCapabilityInstanceNames[0])
-
             # ReplicationType 10 - Synchronous Clone Local.
-            rc, rsd = conn.InvokeMethod(
-                'GetDefaultReplicationSettingData',
-                repServiceCapabilityInstanceName,
-                ReplicationType=self.utils.get_num(10, '16'))
-
-            if rc != 0:
-                rc, errordesc = self.utils.wait_for_job_complete(conn, rsd,
-                                                                 extraSpecs)
-                if rc != 0:
-                    exceptionMessage = (_(
-                        "Error creating cloned volume using "
-                        "Volume: %(cloneName)s, Source Volume: "
-                        "%(sourceName)s. Return code: %(rc)lu. "
-                        "Error: %(error)s.")
-                        % {'cloneName': cloneName,
-                           'sourceName': sourceName,
-                           'rc': rc,
-                           'error': errordesc})
-                    LOG.error(exceptionMessage)
-                    raise exception.VolumeBackendAPIException(
-                        data=exceptionMessage)
-
-            LOG.debug("InvokeMethod GetDefaultReplicationSettingData "
-                      "took: %(delta)s H:MM:SS.",
-                      {'delta': self.utils.get_time_delta(startTime,
-                                                          time.time())})
-
             # Set DesiredCopyMethodology to Copy-On-Write (6).
-            rsdInstance = rsd['DefaultInstance']
-            rsdInstance['DesiredCopyMethodology'] = self.utils.get_num(6, '16')
-
-            startTime = time.time()
+            rsdInstance = self.utils.set_copy_methodology_in_rsd(
+                conn, repServiceInstanceName, SYNC_CLONE_LOCAL,
+                COPY_ON_WRITE, extraSpecs)
 
             # SyncType 8 - Clone.
             # ReplicationSettingData.DesiredCopyMethodology Copy-On-Write (6).
             rc, job = conn.InvokeMethod(
                 'CreateElementReplica', repServiceInstanceName,
-                ElementName=cloneName, SyncType=self.utils.get_num(8, '16'),
+                ElementName=cloneName,
+                SyncType=self.utils.get_num(TF_CLONE, '16'),
                 ReplicationSettingData=rsdInstance,
                 SourceElement=sourceInstance.path)
         else:
@@ -747,13 +716,13 @@ class EMCVMAXProvision(object):
                 rc, job = conn.InvokeMethod(
                     'CreateElementReplica', repServiceInstanceName,
                     ElementName=cloneName,
-                    SyncType=self.utils.get_num(8, '16'),
+                    SyncType=self.utils.get_num(TF_CLONE, '16'),
                     SourceElement=sourceInstance.path)
             else:
                 rc, job = conn.InvokeMethod(
                     'CreateElementReplica', repServiceInstanceName,
                     ElementName=cloneName,
-                    SyncType=self.utils.get_num(8, '16'),
+                    SyncType=self.utils.get_num(TF_CLONE, '16'),
                     SourceElement=sourceInstance.path,
                     TargetElement=targetInstance.path)
 
index 3c301930a6e20a84271cf937b32534b0c7e88f41..1cf058c5ea639bedc848aef44c43e35d41451732 100644 (file)
@@ -31,6 +31,9 @@ EMC_ROOT = 'root/emc'
 THINPROVISIONINGCOMPOSITE = 32768
 THINPROVISIONING = 5
 INFO_SRC_V3 = 3
+ACTIVATESNAPVX = 4
+DEACTIVATESNAPVX = 19
+SNAPSYNCTYPE = 7
 
 
 class EMCVMAXProvisionV3(object):
@@ -169,7 +172,39 @@ class EMCVMAXProvisionV3(object):
         associators = conn.Associators(
             jobInstance,
             ResultClass='EMC_StorageVolume')
-        volpath = associators[0].path
+        if len(associators) > 0:
+            return self.create_volume_dict(associators[0].path)
+        else:
+            exceptionMessage = (_(
+                "Unable to get storage volume from job."))
+            LOG.error(exceptionMessage)
+            raise exception.VolumeBackendAPIException(data=exceptionMessage)
+
+    def get_volume_from_job(self, conn, jobInstance):
+        """Given the jobInstance determine the volume Instance.
+
+        :param conn: the ecom connection
+        :param jobInstance: the instance of a job
+        :returns: dict -- volumeDict - an instance of a volume
+        """
+        associators = conn.Associators(
+            jobInstance,
+            ResultClass='EMC_StorageVolume')
+        if len(associators) > 0:
+            return associators[0]
+        else:
+            exceptionMessage = (_(
+                "Unable to get storage volume from job."))
+            LOG.error(exceptionMessage)
+            raise exception.VolumeBackendAPIException(data=exceptionMessage)
+
+    def create_volume_dict(self, volumeInstanceName):
+        """Create volume dictionary
+
+        :param volumeInstanceName: the instance of a job
+        :returns: dict -- volumeDict - an instance of a volume
+        """
+        volpath = volumeInstanceName
         volumeDict = {}
         volumeDict['classname'] = volpath.classname
         keys = {}
@@ -184,7 +219,7 @@ class EMCVMAXProvisionV3(object):
     def create_element_replica(
             self, conn, repServiceInstanceName,
             cloneName, syncType, sourceInstance, extraSpecs,
-            targetInstance=None):
+            targetInstance=None, rsdInstance=None):
         """Make SMI-S call to create replica for source element.
 
         :param conn: the connection to the ecom server
@@ -193,36 +228,36 @@ class EMCVMAXProvisionV3(object):
         :param syncType: 7=snapshot, 8=clone
         :param sourceInstance: source volume instance
         :param extraSpecs: additional info
-        :param targetInstance: target volume instance. Defaults to None
+        :param targetInstance: Target volume instance. Default None
+        :param rsdInstance: replication settingdata instance. Default None
         :returns: int -- rc - return code
         :returns: job - job object of the replica creation operation
         :raises: VolumeBackendAPIException
         """
         startTime = time.time()
-
-        if targetInstance is None:
-            LOG.debug("Create targetless replica: %(clone)s "
-                      "syncType: %(syncType)s  Source: %(source)s.",
-                      {'clone': cloneName,
-                       'syncType': syncType,
-                       'source': sourceInstance.path})
-            rc, job = conn.InvokeMethod(
-                'CreateElementReplica', repServiceInstanceName,
-                ElementName=cloneName, SyncType=syncType,
-                SourceElement=sourceInstance.path)
-        else:
-            LOG.debug(
-                "Create replica: %(clone)s syncType: %(syncType)s "
-                "Source: %(source)s target: %(target)s.",
-                {'clone': cloneName,
-                 'syncType': syncType,
-                 'source': sourceInstance.path,
-                 'target': targetInstance.path})
+        LOG.debug("Create replica: %(clone)s "
+                  "syncType: %(syncType)s  Source: %(source)s.",
+                  {'clone': cloneName,
+                   'syncType': syncType,
+                   'source': sourceInstance.path})
+        storageSystemName = sourceInstance['SystemName']
+        __, __, sgInstanceName = (
+            self.utils.get_v3_default_sg_instance_name(
+                conn, extraSpecs[self.utils.POOL],
+                extraSpecs[self.utils.SLO],
+                extraSpecs[self.utils.WORKLOAD], storageSystemName))
+        if targetInstance is None and rsdInstance is None:
             rc, job = conn.InvokeMethod(
                 'CreateElementReplica', repServiceInstanceName,
-                ElementName=cloneName, SyncType=syncType,
+                ElementName=cloneName,
+                SyncType=self.utils.get_num(syncType, '16'),
                 SourceElement=sourceInstance.path,
-                TargetElement=targetInstance.path)
+                Collections=[sgInstanceName])
+        else:
+            rc, job = self._create_element_replica_extra_params(
+                conn, repServiceInstanceName, cloneName, syncType,
+                sourceInstance, targetInstance, rsdInstance,
+                sgInstanceName)
 
         if rc != 0:
             rc, errordesc = self.utils.wait_for_job_complete(conn, job,
@@ -244,6 +279,49 @@ class EMCVMAXProvisionV3(object):
                                                       time.time())})
         return rc, job
 
+    def _create_element_replica_extra_params(
+            self, conn, repServiceInstanceName, cloneName, syncType,
+            sourceInstance, targetInstance, rsdInstance, sgInstanceName):
+        """CreateElementReplica using extra parameters.
+
+        :param conn: the connection to the ecom server
+        :param repServiceInstanceName: replication service
+        :param cloneName: clone volume name
+        :param syncType: 7=snapshot, 8=clone
+        :param sourceInstance: source volume instance
+        :param targetInstance: Target volume instance. Default None
+        :param rsdInstance: replication settingdata instance. Default None
+        :param sgInstanceName: pool instance name
+        :returns: int -- rc - return code
+        :returns: job - job object of the replica creation operation
+        """
+        syncType = self.utils.get_num(syncType, '16')
+        if targetInstance and rsdInstance:
+            rc, job = conn.InvokeMethod(
+                'CreateElementReplica', repServiceInstanceName,
+                ElementName=cloneName,
+                SyncType=syncType,
+                SourceElement=sourceInstance.path,
+                TargetElement=targetInstance.path,
+                ReplicationSettingData=rsdInstance)
+        elif targetInstance:
+            rc, job = conn.InvokeMethod(
+                'CreateElementReplica', repServiceInstanceName,
+                ElementName=cloneName,
+                SyncType=syncType,
+                SourceElement=sourceInstance.path,
+                TargetElement=targetInstance.path)
+        elif rsdInstance:
+            rc, job = conn.InvokeMethod(
+                'CreateElementReplica', repServiceInstanceName,
+                ElementName=cloneName,
+                SyncType=syncType,
+                SourceElement=sourceInstance.path,
+                ReplicationSettingData=rsdInstance,
+                Collections=[sgInstanceName])
+
+        return rc, job
+
     def break_replication_relationship(
             self, conn, repServiceInstanceName, syncInstanceName,
             operation, extraSpecs, force=False):
@@ -449,7 +527,7 @@ class EMCVMAXProvisionV3(object):
         :returns: job object of the replica creation operation
         """
         # Operation 4: activate the snapVx.
-        operation = self.utils.get_num(4, '16')
+        operation = ACTIVATESNAPVX
 
         LOG.debug("Activate snap: %(sv)s  operation: %(operation)s.",
                   {'sv': syncInstanceName, 'operation': operation})
@@ -470,7 +548,7 @@ class EMCVMAXProvisionV3(object):
         :returns: job object of the replica creation operation
         """
         # Operation 4: activate the snapVx.
-        operation = self.utils.get_num(19, '16')
+        operation = DEACTIVATESNAPVX
 
         LOG.debug("Return snap resource back to pool: "
                   "%(sv)s  operation: %(operation)s.",
@@ -503,7 +581,7 @@ class EMCVMAXProvisionV3(object):
 
         rc, job = conn.InvokeMethod(
             'ModifyReplicaSynchronization', repServiceInstanceName,
-            Operation=operation,
+            Operation=self.utils.get_num(operation, '16'),
             Synchronization=syncInstanceName,
             Force=force)
 
@@ -558,15 +636,13 @@ class EMCVMAXProvisionV3(object):
              'relationName': relationName,
              'srcGroup': srcGroupInstanceName,
              'tgtGroup': tgtGroupInstanceName})
-        # 7 for snap.
-        syncType = 7
         rc, job = conn.InvokeMethod(
             'CreateGroupReplica',
             replicationService,
             RelationshipName=relationName,
             SourceGroup=srcGroupInstanceName,
             TargetGroup=tgtGroupInstanceName,
-            SyncType=self.utils.get_num(syncType, '16'))
+            SyncType=self.utils.get_num(SNAPSYNCTYPE, '16'))
 
         if rc != 0:
             rc, errordesc = self.utils.wait_for_job_complete(conn, job,
index 986609e50bea8b7a752bfe8788678f83e6a5427a..2b185cbe845f29c300647b899ba3d98acfeec26e 100644 (file)
@@ -410,18 +410,9 @@ class EMCVMAXUtils(object):
             :raises: VolumeBackendAPIException
             """
             retries = kwargs['retries']
-            maxJobRetries = self._get_max_job_retries(extraSpecs)
-            wait_for_sync_called = kwargs['wait_for_sync_called']
-            if self._is_sync_complete(conn, syncName):
-                raise loopingcall.LoopingCallDone()
-            if retries > maxJobRetries:
-                LOG.error(_LE("_wait_for_sync failed after %(retries)d "
-                              "tries."),
-                          {'retries': retries})
-                raise loopingcall.LoopingCallDone()
             try:
                 kwargs['retries'] = retries + 1
-                if not wait_for_sync_called:
+                if not kwargs['wait_for_sync_called']:
                     if self._is_sync_complete(conn, syncName):
                         kwargs['wait_for_sync_called'] = True
             except Exception:
@@ -430,11 +421,21 @@ class EMCVMAXUtils(object):
                 LOG.exception(exceptionMessage)
                 raise exception.VolumeBackendAPIException(exceptionMessage)
 
+            if kwargs['retries'] > maxJobRetries:
+                LOG.error(_LE("_wait_for_sync failed after %(retries)d "
+                              "tries."),
+                          {'retries': retries})
+                raise loopingcall.LoopingCallDone(retvalue=maxJobRetries)
+            if kwargs['wait_for_sync_called']:
+                raise loopingcall.LoopingCallDone()
+
+        maxJobRetries = self._get_max_job_retries(extraSpecs)
         kwargs = {'retries': 0,
                   'wait_for_sync_called': False}
         intervalInSecs = self._get_interval_in_secs(extraSpecs)
         timer = loopingcall.FixedIntervalLoopingCall(_wait_for_sync)
-        timer.start(interval=intervalInSecs).wait()
+        rc = timer.start(interval=intervalInSecs).wait()
+        return rc
 
     def _is_sync_complete(self, conn, syncName):
         """Check if the job is finished.
@@ -2471,3 +2472,97 @@ class EMCVMAXUtils(object):
             LOG.error(exceptionMessage)
             raise exception.VolumeBackendAPIException(data=exceptionMessage)
         return protocolControllerInstanceName
+
+    def get_replication_setting_data(self, conn, repServiceInstanceName,
+                                     replication_type, extraSpecs):
+        """Get the replication setting data
+
+        :param conn: connection the ecom server
+        :param repServiceInstanceName: the storage group instance name
+        :param replication_type: the replication type
+        :param copy_methodology: the copy methodology
+        :returns: instance rsdInstance
+        """
+        repServiceCapabilityInstanceNames = conn.AssociatorNames(
+            repServiceInstanceName,
+            ResultClass='CIM_ReplicationServiceCapabilities',
+            AssocClass='CIM_ElementCapabilities')
+        repServiceCapabilityInstanceName = (
+            repServiceCapabilityInstanceNames[0])
+
+        rc, rsd = conn.InvokeMethod(
+            'GetDefaultReplicationSettingData',
+            repServiceCapabilityInstanceName,
+            ReplicationType=self.get_num(replication_type, '16'))
+
+        if rc != 0:
+            rc, errordesc = self.wait_for_job_complete(conn, rsd,
+                                                       extraSpecs)
+            if rc != 0:
+                exceptionMessage = (_(
+                    "Error getting ReplicationSettingData. "
+                    "Return code: %(rc)lu. "
+                    "Error: %(error)s.")
+                    % {'rc': rc,
+                       'error': errordesc})
+                LOG.error(exceptionMessage)
+                raise exception.VolumeBackendAPIException(
+                    data=exceptionMessage)
+        return rsd
+
+    def set_copy_methodology_in_rsd(self, conn, repServiceInstanceName,
+                                    replication_type, copy_methodology,
+                                    extraSpecs):
+        """Get the replication setting data
+
+        :param conn: connection the ecom server
+        :param repServiceInstanceName: the storage group instance name
+        :param replication_type: the replication type
+        :param copy_methodology: the copy methodology
+        :returns: instance rsdInstance
+        """
+        rsd = self.get_replication_setting_data(
+            conn, repServiceInstanceName, replication_type, extraSpecs)
+        rsdInstance = rsd['DefaultInstance']
+        rsdInstance['DesiredCopyMethodology'] = (
+            self.get_num(copy_methodology, '16'))
+        return rsdInstance
+
+    def set_target_element_supplier_in_rsd(
+            self, conn, repServiceInstanceName, replication_type,
+            target_type, extraSpecs):
+        """Get the replication setting data
+
+        :param conn: connection the ecom server
+        :param repServiceInstanceName: the storage group instance name
+        :param replication_type: the replication type
+        :param target_type: Use existing, Create new, Use and create
+        :returns: instance rsdInstance
+        """
+        rsd = self.get_replication_setting_data(
+            conn, repServiceInstanceName, replication_type, extraSpecs)
+        rsdInstance = rsd['DefaultInstance']
+        rsdInstance['TargetElementSupplier'] = (
+            self.get_num(target_type, '16'))
+
+        return rsdInstance
+
+    def get_v3_default_sg_instance_name(
+            self, conn, poolName, slo, workload, storageSystemName):
+        """Get the V3 default instance name
+
+        :param conn: the connection to the ecom server
+        :param poolName: the pool name
+        :param slo: the SLO
+        :param workload: the workload
+        :param storageSystemName: the storage system name
+        :returns: the storage group instance name
+        """
+        storageGroupName = self.get_v3_storage_group_name(
+            poolName, slo, workload)
+        controllerConfigService = (
+            self.find_controller_configuration_service(
+                conn, storageSystemName))
+        sgInstanceName = self.find_storage_masking_group(
+            conn, controllerConfigService, storageGroupName)
+        return storageGroupName, controllerConfigService, sgInstanceName