]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Fix a clone volume problem in VMAX driver
authorXing Yang <xing.yang@emc.com>
Fri, 5 Dec 2014 22:55:28 +0000 (17:55 -0500)
committerXing Yang <xing.yang@emc.com>
Sat, 6 Dec 2014 22:31:35 +0000 (17:31 -0500)
If the source volume contains meta members of different sizes,
create cloned volume will fail with error "ERROR_FAMILY_NOT_SUPPORTED".
This patched fixed this problem.

Change-Id: I92e7e8b56e3a21644aa2bff3288c1bdc80d45cc6
Closes-Bug: #1391205

cinder/tests/test_emc_vmax.py
cinder/volume/drivers/emc/emc_vmax_common.py
cinder/volume/drivers/emc/emc_vmax_fast.py
cinder/volume/drivers/emc/emc_vmax_provision.py
cinder/volume/drivers/emc/emc_vmax_utils.py

index 5bbd40e24d56bed9f46ceedf64b31c47adabd4f1..5646ff1e9261927a4b2d6757b780716267bb84c8 100644 (file)
@@ -275,7 +275,7 @@ class FakeEcomConnection():
 
     def InvokeMethod(self, MethodName, Service, ElementName=None, InPool=None,
                      ElementType=None, Size=None,
-                     SyncType=None, SourceElement=None,
+                     SyncType=None, SourceElement=None, TargetElement=None,
                      Operation=None, Synchronization=None,
                      TheElements=None, TheElement=None,
                      LUNames=None, InitiatorPortIDs=None, DeviceAccesses=None,
@@ -664,6 +664,13 @@ class FakeEcomConnection():
         fakeinstance = instance.fake_getpolicyinstance()
         return fakeinstance
 
+    def _getinstance_syncsvsv(self, objectpath):
+        svInstance = {}
+        svInstance['SyncedElement'] = 'SyncedElement'
+        svInstance['SystemElement'] = 'SystemElement'
+        svInstance['PercentSynced'] = 100
+        return svInstance
+
     def _default_getinstance(self, objectpath):
         return objectpath
 
@@ -1300,14 +1307,29 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         FakeDB,
         'volume_get',
         return_value=EMCVMAXCommonData.test_source_volume)
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_volume_meta_head',
+        return_value=[EMCVMAXCommonData.test_volume])
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_meta_members_capacity_in_bit',
+        return_value=[1234567, 7654321])
     @mock.patch.object(
         EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    def test_create_snapshot_no_fast_success(
-            self, mock_volume_type,
-            mock_volume, mock_sync_sv):
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system))
+    def test_create_snapshot_different_sizes_meta_no_fast_success(
+            self, mock_volume_type, mock_volume,
+            mock_meta, mock_size, mock_pool):
         self.data.test_volume['volume_name'] = "vmax-1234567"
+        common = self.driver.common
+        volumeDict = {'classname': u'Symm_StorageVolume',
+                      'keybindings': EMCVMAXCommonData.keybindings}
+        common.provision.create_volume_from_pool = (
+            mock.Mock(return_value=(volumeDict, 0L)))
+        common.provision.get_volume_dict_from_job = (
+            mock.Mock(return_value=volumeDict))
         self.driver.create_snapshot(self.data.test_volume)
 
     def test_create_snapshot_no_fast_failed(self):
@@ -1320,20 +1342,23 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         volume_types,
         'get_volume_type_extra_specs',
         return_value={'volume_backend_name': 'ISCSINoFAST'})
-    @mock.patch.object(
-        FakeDB,
-        'volume_get',
-        return_value=EMCVMAXCommonData.test_source_volume)
     @mock.patch.object(
         EMCVMAXCommon,
         '_find_storage_sync_sv_sv',
         return_value=(None, None))
-    def test_create_volume_from_snapshot_no_fast_success(
-            self, mock_volume_type,
-            mock_volume, mock_sync_sv):
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_volume_meta_head',
+        return_value=[EMCVMAXCommonData.test_volume])
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_meta_members_capacity_in_bit',
+        return_value=[1234567])
+    def test_create_volume_from_same_size_meta_snapshot(
+            self, mock_volume_type, mock_sync_sv, mock_meta, mock_size):
         self.data.test_volume['volume_name'] = "vmax-1234567"
         self.driver.create_volume_from_snapshot(
-            self.data.test_volume, EMCVMAXCommonData.test_source_volume)
+            self.data.test_volume, self.data.test_volume)
 
     def test_create_volume_from_snapshot_no_fast_failed(self):
         self.data.test_volume['volume_name'] = "vmax-1234567"
@@ -1354,8 +1379,13 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         EMCVMAXCommon,
         '_find_storage_sync_sv_sv',
         return_value=(None, None))
-    def test_create_clone_no_fast_success(self, mock_volume_type,
-                                          mock_volume, mock_sync_sv):
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_volume_meta_head',
+        return_value=None)
+    def test_create_clone_simple_volume_no_fast_success(
+            self, mock_volume_type, mock_volume, mock_sync_sv,
+            mock_simple_volume):
         self.data.test_volume['volume_name'] = "vmax-1234567"
         self.driver.create_cloned_volume(self.data.test_volume,
                                          EMCVMAXCommonData.test_source_volume)
@@ -1737,37 +1767,41 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'ISCSIFAST',
-                      'FASTPOLICY': 'FC_GOLD1'})
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_storage_masking_group',
-        return_value=EMCVMAXCommonData.storagegroupname)
+        return_value={'volume_backend_name': 'ISCSIFAST'})
     @mock.patch.object(
         FakeDB,
         'volume_get',
         return_value=EMCVMAXCommonData.test_source_volume)
     @mock.patch.object(
-        EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
+        EMCVMAXFast,
+        'get_pool_associated_to_policy',
+        return_value=1)
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_storage_configuration_service',
-        return_value=1)
+        'get_volume_meta_head',
+        return_value=[EMCVMAXCommonData.test_volume])
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_controller_configuration_service',
-        return_value=1)
+        'get_meta_members_capacity_in_bit',
+        return_value=[1234567, 7654321])
     @mock.patch.object(
         EMCVMAXCommon,
-        '_get_or_create_default_storage_group',
-        return_value=1)
-    def test_create_snapshot_fast_success(
-            self, mock_volume_type, mock_storage_group, mock_volume,
-            mock_sync_sv, mock_storage_config_service, mock_controller_service,
-            mock_default_sg):
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system))
+    def test_create_snapshot_different_sizes_meta_fast_success(
+            self, mock_volume_type, mock_volume, mock_meta,
+            mock_size, mock_pool, mock_policy):
         self.data.test_volume['volume_name'] = "vmax-1234567"
+        common = self.driver.common
+
+        volumeDict = {'classname': u'Symm_StorageVolume',
+                      'keybindings': EMCVMAXCommonData.keybindings}
+        common.provision.create_volume_from_pool = (
+            mock.Mock(return_value=(volumeDict, 0L)))
+        common.provision.get_volume_dict_from_job = (
+            mock.Mock(return_value=volumeDict))
+        common.fast.is_volume_in_default_SG = (
+            mock.Mock(return_value=True))
         self.driver.create_snapshot(self.data.test_volume)
 
     def test_create_snapshot_fast_failed(self):
@@ -1779,39 +1813,30 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'ISCSIFAST',
-                      'FASTPOLICY': 'FC_GOLD1'})
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_storage_masking_group',
-        return_value=EMCVMAXCommonData.storagegroupname)
-    @mock.patch.object(
-        FakeDB,
-        'volume_get',
-        return_value=EMCVMAXCommonData.test_source_volume)
+        return_value={'volume_backend_name': 'ISCSIFAST'})
     @mock.patch.object(
         EMCVMAXCommon,
         '_find_storage_sync_sv_sv',
         return_value=(None, None))
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_storage_configuration_service',
-        return_value=1)
+        'get_volume_meta_head',
+        return_value=[EMCVMAXCommonData.test_volume])
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_controller_configuration_service',
-        return_value=1)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_get_or_create_default_storage_group',
-        return_value=1)
-    def test_create_volume_from_snapshot_fast_success(
-            self, mock_volume_type, mock_storage_group, mock_volume,
-            mock_sync_sv, mock_storage_config_service, mock_controller_service,
-            mock_default_sg):
+        'get_meta_members_capacity_in_bit',
+        return_value=[1234567])
+    def test_create_volume_from_same_size_meta_snapshot(
+            self, mock_volume_type, mock_sync_sv, mock_meta, mock_size):
         self.data.test_volume['volume_name'] = "vmax-1234567"
+        self.driver.common.utils.find_storage_configuration_service = (
+            mock.Mock(return_value=EMCVMAXCommonData.storage_system))
+        self.driver.common._get_or_create_default_storage_group = (
+            mock.Mock(return_value=EMCVMAXCommonData.default_storage_group))
+        self.driver.common.fast.is_volume_in_default_SG = (
+            mock.Mock(return_value=True))
         self.driver.create_volume_from_snapshot(
-            self.data.test_volume, EMCVMAXCommonData.test_source_volume)
+            self.data.test_volume, self.data.test_volume)
 
     @mock.patch.object(
         volume_types,
@@ -1826,9 +1851,13 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
         EMCVMAXCommon,
         '_find_storage_sync_sv_sv',
         return_value=(None, None))
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_volume_meta_head',
+        return_value=None)
     def test_create_volume_from_snapshot_fast_failed(
-            self, mock_volume_type,
-            mock_rep_service, mock_sync_sv):
+            self, mock_type, mock_rep_service, mock_sync_sv, mock_meta):
+
         self.data.test_volume['volume_name'] = "vmax-1234567"
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_volume_from_snapshot,
@@ -1838,12 +1867,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'ISCSIFAST',
-                      'FASTPOLICY': 'FC_GOLD1'})
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_storage_masking_group',
-        return_value=EMCVMAXCommonData.storagegroupname)
+        return_value={'volume_backend_name': 'ISCSIFAST'})
     @mock.patch.object(
         FakeDB,
         'volume_get',
@@ -1854,38 +1878,51 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
         return_value=(None, None))
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_storage_configuration_service',
-        return_value=1)
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_controller_configuration_service',
-        return_value=1)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_get_or_create_default_storage_group',
-        return_value=1)
-    def test_create_clone_fast_success(self, mock_volume_type,
-                                       mock_storage_group, mock_volume,
-                                       mock_sync_sv,
-                                       mock_storage_config_service,
-                                       mock_controller_service,
-                                       mock_default_sg):
+        'get_volume_meta_head',
+        return_value=None)
+    def test_create_clone_simple_volume_fast_success(
+            self, mock_volume_type, mock_volume, mock_sync_sv,
+            mock_simple_volume):
         self.data.test_volume['volume_name'] = "vmax-1234567"
+        self.driver.common.utils.find_storage_configuration_service = (
+            mock.Mock(return_value=EMCVMAXCommonData.storage_system))
+        self.driver.common._get_or_create_default_storage_group = (
+            mock.Mock(return_value=EMCVMAXCommonData.default_storage_group))
+        self.driver.common.fast.is_volume_in_default_SG = (
+            mock.Mock(return_value=True))
         self.driver.create_cloned_volume(self.data.test_volume,
                                          EMCVMAXCommonData.test_source_volume)
 
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'ISCSIFAST',
-                      'FASTPOLICY': 'FC_GOLD1'})
+        return_value={'volume_backend_name': 'ISCSIFAST'})
+    @mock.patch.object(
+        FakeDB,
+        'volume_get',
+        return_value=EMCVMAXCommonData.test_source_volume)
+    @mock.patch.object(
+        EMCVMAXFast,
+        'get_pool_associated_to_policy',
+        return_value=1)
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_volume_meta_head',
+        return_value=[EMCVMAXCommonData.test_volume])
+    @mock.patch.object(
+        EMCVMAXUtils,
+        'get_meta_members_capacity_in_bit',
+        return_value=[1234567, 7654321])
     @mock.patch.object(
         EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    def test_create_clone_fast_failed(self, mock_volume_type,
-                                      mock_sync_sv):
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system))
+    def test_create_clone_fast_failed(
+            self, mock_volume_type, mock_vol, mock_policy, mock_meta,
+            mock_size, mock_pool):
         self.data.test_volume['volume_name'] = "vmax-1234567"
+        self.driver.common._modify_and_get_composite_volume_instance = (
+            mock.Mock(return_value=(1L, None)))
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_cloned_volume,
                           self.data.test_volume,
@@ -2229,81 +2266,6 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
                           self.data.test_volume,
                           newSize)
 
-    @mock.patch.object(
-        volume_types,
-        'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCNoFAST'})
-    @mock.patch.object(
-        FakeDB,
-        'volume_get',
-        return_value=EMCVMAXCommonData.test_source_volume)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    def test_create_snapshot_no_fast_success(
-            self, mock_volume_type,
-            mock_volume, mock_sync_sv):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.driver.create_snapshot(self.data.test_volume)
-
-    def test_create_snapshot_no_fast_failed(self):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.assertRaises(exception.VolumeBackendAPIException,
-                          self.driver.create_snapshot,
-                          self.data.test_volume)
-
-    @mock.patch.object(
-        volume_types,
-        'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCNoFAST'})
-    @mock.patch.object(
-        FakeDB,
-        'volume_get',
-        return_value=EMCVMAXCommonData.test_source_volume)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    def test_create_volume_from_snapshot_no_fast_success(
-            self, mock_volume_type,
-            mock_volume, mock_sync_sv):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.driver.create_volume_from_snapshot(
-            self.data.test_volume, EMCVMAXCommonData.test_source_volume)
-
-    def test_create_volume_from_snapshot_no_fast_failed(self):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.assertRaises(exception.VolumeBackendAPIException,
-                          self.driver.create_volume_from_snapshot,
-                          self.data.test_volume,
-                          EMCVMAXCommonData.test_source_volume)
-
-    @mock.patch.object(
-        volume_types,
-        'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCNoFAST'})
-    @mock.patch.object(
-        FakeDB,
-        'volume_get',
-        return_value=EMCVMAXCommonData.test_source_volume)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    def test_create_clone_no_fast_success(self, mock_volume_type,
-                                          mock_volume, mock_sync_sv):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.driver.create_cloned_volume(self.data.test_volume,
-                                         EMCVMAXCommonData.test_source_volume)
-
-    def test_create_clone_no_fast_failed(self):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.assertRaises(exception.VolumeBackendAPIException,
-                          self.driver.create_cloned_volume,
-                          self.data.test_volume,
-                          EMCVMAXCommonData.test_source_volume)
-
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
@@ -2652,39 +2614,41 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCFAST',
-                      'FASTPOLICY': 'FC_GOLD1'})
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_storage_masking_group',
-        return_value=EMCVMAXCommonData.storagegroupname)
+        return_value={'volume_backend_name': 'FCFAST'})
     @mock.patch.object(
         FakeDB,
         'volume_get',
         return_value=EMCVMAXCommonData.test_source_volume)
     @mock.patch.object(
-        EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
+        EMCVMAXFast,
+        'get_pool_associated_to_policy',
+        return_value=1)
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_storage_configuration_service',
-        return_value=1)
+        'get_volume_meta_head',
+        return_value=[EMCVMAXCommonData.test_volume])
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_controller_configuration_service',
-        return_value=1)
+        'get_meta_members_capacity_in_bit',
+        return_value=[1234567, 7654321])
     @mock.patch.object(
         EMCVMAXCommon,
-        '_get_or_create_default_storage_group',
-        return_value=1)
-    def test_create_snapshot_fast_success(self, mock_volume_type,
-                                          mock_storage_group, mock_volume,
-                                          mock_sync_sv,
-                                          mock_storage_config_service,
-                                          mock_controller_config_service,
-                                          mock_default_sg):
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system))
+    def test_create_snapshot_different_sizes_meta_fast_success(
+            self, mock_volume_type, mock_volume, mock_meta,
+            mock_size, mock_pool, mock_policy):
         self.data.test_volume['volume_name'] = "vmax-1234567"
+        common = self.driver.common
+
+        volumeDict = {'classname': u'Symm_StorageVolume',
+                      'keybindings': EMCVMAXCommonData.keybindings}
+        common.provision.create_volume_from_pool = (
+            mock.Mock(return_value=(volumeDict, 0L)))
+        common.provision.get_volume_dict_from_job = (
+            mock.Mock(return_value=volumeDict))
+        common.fast.is_volume_in_default_SG = (
+            mock.Mock(return_value=True))
         self.driver.create_snapshot(self.data.test_volume)
 
     def test_create_snapshot_fast_failed(self):
@@ -2696,12 +2660,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCFAST',
-                      'FASTPOLICY': 'FC_GOLD1'})
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_storage_masking_group',
-        return_value=EMCVMAXCommonData.storagegroupname)
+        return_value={'volume_backend_name': 'FCFAST'})
     @mock.patch.object(
         FakeDB,
         'volume_get',
@@ -2712,100 +2671,52 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
         return_value=(None, None))
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_storage_configuration_service',
-        return_value=1)
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_controller_configuration_service',
-        return_value=1)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_get_or_create_default_storage_group',
-        return_value=1)
-    def test_create_volume_from_snapshot_fast_success(
-            self, mock_volume_type, mock_storage_group, mock_volume,
-            mock_sync_sv, mock_storage_config_service,
-            mock_controller_config_service, mock_default_sg):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.driver.create_volume_from_snapshot(
-            self.data.test_volume, EMCVMAXCommonData.test_source_volume)
-
-    @mock.patch.object(
-        volume_types,
-        'get_volume_type_extra_specs',
-        return_value={'storagetype: pool': 'gold',
-                      'volume_backend_name': 'FCFAST'})
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_replication_service',
+        'get_volume_meta_head',
         return_value=None)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    def test_create_volume_from_snapshot_fast_failed(self, mock_volume_type,
-                                                     mock_rep_service,
-                                                     mock_sync_sv):
+    def test_create_clone_simple_volume_fast_success(
+            self, mock_volume_type,
+            mock_volume, mock_sync_sv, mock_meta):
         self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.assertRaises(exception.VolumeBackendAPIException,
-                          self.driver.create_volume_from_snapshot,
-                          self.data.test_volume,
-                          EMCVMAXCommonData.test_source_volume)
+        self.driver.common.utils.find_storage_configuration_service = (
+            mock.Mock(return_value=EMCVMAXCommonData.storage_system))
+        self.driver.common._get_or_create_default_storage_group = (
+            mock.Mock(return_value=EMCVMAXCommonData.default_storage_group))
+        self.driver.common.fast.is_volume_in_default_SG = (
+            mock.Mock(return_value=True))
+        self.driver.create_cloned_volume(
+            self.data.test_volume,
+            EMCVMAXCommonData.test_source_volume)
 
     @mock.patch.object(
         volume_types,
         'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCFAST',
-                      'FASTPOLICY': 'FC_GOLD1'})
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_storage_masking_group',
-        return_value=EMCVMAXCommonData.storagegroupname)
+        return_value={'volume_backend_name': 'FCFAST'})
     @mock.patch.object(
         FakeDB,
         'volume_get',
         return_value=EMCVMAXCommonData.test_source_volume)
     @mock.patch.object(
-        EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    @mock.patch.object(
-        EMCVMAXUtils,
-        'find_storage_configuration_service',
+        EMCVMAXFast,
+        'get_pool_associated_to_policy',
         return_value=1)
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_controller_configuration_service',
-        return_value=1)
-    @mock.patch.object(
-        EMCVMAXCommon,
-        '_get_or_create_default_storage_group',
-        return_value=1)
-    def test_create_clone_fast_success(self, mock_volume_type,
-                                       mock_storage_group, mock_volume,
-                                       mock_sync_sv,
-                                       mock_storage_config_service,
-                                       mock_controller_config_service,
-                                       mock_default_sg):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.driver.create_cloned_volume(self.data.test_volume,
-                                         EMCVMAXCommonData.test_source_volume)
-
-    @mock.patch.object(
-        volume_types,
-        'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCFAST'})
+        'get_volume_meta_head',
+        return_value=[EMCVMAXCommonData.test_volume])
     @mock.patch.object(
         EMCVMAXUtils,
-        'find_replication_service',
-        return_value=None)
+        'get_meta_members_capacity_in_bit',
+        return_value=[1234567, 7654321])
     @mock.patch.object(
         EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    def test_create_clone_fast_failed(self, mock_volume_type,
-                                      mock_rep_service, mock_sync_sv):
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system))
+    def test_create_clone_fast_failed(
+            self, mock_volume_type, mock_vol,
+            mock_policy, mock_meta, mock_size, mock_pool):
         self.data.test_volume['volume_name'] = "vmax-1234567"
+        self.driver.common._modify_and_get_composite_volume_instance = (
+            mock.Mock(return_value=(1L, None)))
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_cloned_volume,
                           self.data.test_volume,
index 2d88a4e2ac8349f0779aad2aec79aa2c0ef17aed..101894ee517c808774d52554e9a963f426ea8e07 100644 (file)
@@ -116,87 +116,10 @@ class EMCVMAXCommon(object):
         volumeName = volume['name']
 
         extraSpecs = self._initial_setup(volume)
-        memberCount, errorDesc = self.utils.determine_member_count(
-            volume['size'], extraSpecs[MEMBERCOUNT], extraSpecs[COMPOSITETYPE])
-        if errorDesc is not None:
-            exceptionMessage = (_("The striped meta count of %(memberCount)s "
-                                  "is too small for volume: %(volumeName)s. "
-                                  "with size %(volumeSize)s ")
-                                % {'memberCount': memberCount,
-                                   'volumeName': volumeName,
-                                   'volumeSize': volume['size']})
-            LOG.error(exceptionMessage)
-            raise exception.VolumeBackendAPIException(data=exceptionMessage)
-
         self.conn = self._get_ecom_connection()
 
-        poolInstanceName, storageSystemName = (
-            self._get_pool_and_storage_system(extraSpecs))
-
-        LOG.debug("Create Volume: %(volume)s  Pool: %(pool)s  "
-                  "Storage System: %(storageSystem)s "
-                  "Size: %(size)lu "
-                  % {'volume': volumeName,
-                     'pool': poolInstanceName,
-                     'storageSystem': storageSystemName,
-                     'size': volumeSize})
-
-        elementCompositionService = (
-            self.utils.find_element_composition_service(self.conn,
-                                                        storageSystemName))
-
-        storageConfigService = self.utils.find_storage_configuration_service(
-            self.conn, storageSystemName)
-
-        # If FAST is intended to be used we must first check that the pool
-        # is associated with the correct storage tier
-        if extraSpecs[FASTPOLICY] is not None:
-            foundPoolInstanceName = self.fast.get_pool_associated_to_policy(
-                self.conn, extraSpecs[FASTPOLICY], extraSpecs[ARRAY],
-                storageConfigService, poolInstanceName)
-            if foundPoolInstanceName is None:
-                exceptionMessage = (_("Pool: %(poolName)s. "
-                                      "is not associated to storage tier for "
-                                      "fast policy %(fastPolicy)s.")
-                                    % {'poolName': extraSpecs[POOL],
-                                       'fastPolicy': extraSpecs[FASTPOLICY]})
-                LOG.error(exceptionMessage)
-                raise exception.VolumeBackendAPIException(
-                    data=exceptionMessage)
-
-        compositeType = self.utils.get_composite_type(
-            extraSpecs[COMPOSITETYPE])
-
-        volumeDict, rc = self.provision.create_composite_volume(
-            self.conn, elementCompositionService, volumeSize, volumeName,
-            poolInstanceName, compositeType, memberCount)
-
-        # Now that we have already checked that the pool is associated with
-        # the correct storage tier and the volume was successfully created
-        # add the volume to the default storage group created for
-        # volumes in pools associated with this fast policy
-        if extraSpecs[FASTPOLICY]:
-            LOG.info(_LI("Adding volume: %(volumeName)s to "
-                         "default storage group "
-                         "for FAST policy: %(fastPolicyName)s "),
-                     {'volumeName': volumeName,
-                      'fastPolicyName': extraSpecs[FASTPOLICY]})
-            defaultStorageGroupInstanceName = (
-                self._get_or_create_default_storage_group(
-                    self.conn, storageSystemName, volumeDict,
-                    volumeName, extraSpecs[FASTPOLICY]))
-            if not defaultStorageGroupInstanceName:
-                exceptionMessage = (_(
-                    "Unable to create or get default storage group for "
-                    "FAST policy: %(fastPolicyName)s. ")
-                    % {'fastPolicyName': extraSpecs[FASTPOLICY]})
-                LOG.error(exceptionMessage)
-                raise exception.VolumeBackendAPIException(
-                    data=exceptionMessage)
-
-            self._add_volume_to_default_storage_group_on_create(
-                volumeDict, volumeName, storageConfigService,
-                storageSystemName, extraSpecs[FASTPOLICY])
+        rc, volumeDict, storageSystemName = self._create_composite_volume(
+            volume, extraSpecs, volumeName, volumeSize)
 
         LOG.info(_LI("Leaving create_volume: %(volumeName)s  "
                      "Return code: %(rc)lu "
@@ -359,7 +282,7 @@ class EMCVMAXCommon(object):
             # Device is already mapped so we will leave the state as is
             deviceNumber = deviceInfoDict['hostlunid']
             LOG.info(_LI("Volume %(volume)s is already mapped. "
-                         "The device number is  %(deviceNumber)s ")
+                         "The device number is %(deviceNumber)s ")
                      % {'volume': volumeName,
                         'deviceNumber': deviceNumber})
         else:
@@ -1072,7 +995,7 @@ class EMCVMAXCommon(object):
                 conn, controllerConfigurationService, volumeInstance,
                 volumeName, targetFastPolicyName))
         if assocDefaultStorageGroupName is None:
-            errorMsg = (_(
+            errorMsg = (_LE(
                 "Failed to add %(volumeName)s "
                 "to default storage group for fast policy "
                 "%(fastPolicyName)s ")
@@ -1113,7 +1036,7 @@ class EMCVMAXCommon(object):
                           "and fast policy"))
 
         if targetArraySerialNumber not in sourceArraySerialNumber:
-            errorMessage = (_(
+            errorMessage = (_LE(
                 "The source array : %(sourceArraySerialNumber)s does not "
                 "match the target array: %(targetArraySerialNumber)s"
                 "skipping storage-assisted migration")
@@ -1129,8 +1052,8 @@ class EMCVMAXCommon(object):
         assocPoolInstance = self.conn.GetInstance(
             assocPoolInstanceName)
         if assocPoolInstance['ElementName'] == targetPoolName:
-            errorMessage = (_("No action required. Volume : %(volumeName)s is "
-                              "already part of pool : %(pool)s")
+            errorMessage = (_LE("No action required. Volume : %(volumeName)s "
+                                "is already part of pool : %(pool)s")
                             % {'volumeName': volumeName,
                                'pool': targetPoolName})
             LOG.error(errorMessage)
@@ -1139,7 +1062,7 @@ class EMCVMAXCommon(object):
         LOG.info(_LI("Volume status is: %s"), volumeStatus)
         if (host['capabilities']['storage_protocol'] != self.protocol and
                 (volumeStatus != 'available' and volumeStatus != 'retyping')):
-            errorMessage = (_(
+            errorMessage = (_LE(
                 "Only available volumes can be migrated between "
                 "different protocols"))
             LOG.error(errorMessage)
@@ -1884,7 +1807,7 @@ class EMCVMAXCommon(object):
         cloneName = cloneVolume['name']
 
         LOG.info(_LI("Create a Clone from Volume: Clone "
-                     "Volume: %(cloneName)s  "
+                     "Volume: %(cloneName)s "
                      "Source Volume: %(sourceName)s")
                  % {'cloneName': cloneName,
                     'sourceName': sourceName})
@@ -1893,15 +1816,6 @@ class EMCVMAXCommon(object):
 
         sourceInstance = self._find_lun(sourceVolume)
         storageSystem = sourceInstance['SystemName']
-
-        LOG.debug("Create Cloned Volume: Volume: %(cloneName)s  "
-                  "Source Volume: %(sourceName)s  Source Instance: "
-                  "%(sourceInstance)s  Storage System: %(storageSystem)s."
-                  % {'cloneName': cloneName,
-                     'sourceName': sourceName,
-                     'sourceInstance': sourceInstance.path,
-                     'storageSystem': storageSystem})
-
         repServiceInstanceName = self.utils.find_replication_service(
             self.conn, storageSystem)
 
@@ -1917,10 +1831,133 @@ class EMCVMAXCommon(object):
                      'elementname': cloneName,
                      'sourceelement': sourceInstance.path})
 
+        return self._examine_source_and_create_clone(
+            repServiceInstanceName, cloneVolume, sourceVolume,
+            sourceInstance, extraSpecs)
+
+    def _examine_source_and_create_clone(self, repServiceInstanceName,
+                                         cloneVolume, sourceVolume,
+                                         sourceInstance, extraSpecs):
+        """Create a clone (v2).
+
+        :param repServiceInstanceName: the replication service
+        :param cloneVolume: the clone volume object
+        :param sourceVolume: the source volume object
+        :param sourceInstance: the device ID of the volume
+        :param fastPolicyName: the FAST policy name(if it exists)
+        :returns: rc
+        """
+        # check if the source volume contains any meta devices
+        metaHeadInstanceName = self.utils.get_volume_meta_head(
+            self.conn, sourceInstance.path)
+
+        if metaHeadInstanceName is None:  # simple volume
+            return self._create_replica_and_delete_clone_relationship(
+                repServiceInstanceName, cloneVolume, sourceVolume,
+                sourceInstance, None, extraSpecs)
+        else:  # composite volume with meta device members
+            # check if the meta members' capacity
+            metaMemberInstanceNames = (
+                self.utils.get_meta_members_of_composite_volume(
+                    self.conn, metaHeadInstanceName))
+            volumeCapacities = self.utils.get_meta_members_capacity_in_bit(
+                self.conn, metaMemberInstanceNames)
+            LOG.debug("Volume capacities:  %(metasizes)s "
+                      % {'metasizes': volumeCapacities})
+            if len(set(volumeCapacities)) == 1:
+                LOG.debug("Meta volume all of the same size")
+                return self._create_replica_and_delete_clone_relationship(
+                    repServiceInstanceName, cloneVolume, sourceVolume,
+                    sourceInstance, None, extraSpecs)
+
+            LOG.debug("Meta volumes are of different sizes: "
+                      "%d different sizes." % len(set(volumeCapacities)))
+
+            baseTargetVolumeInstance = None
+            for volumeSizeInbits in volumeCapacities:
+                if baseTargetVolumeInstance is None:  # Create base volume
+                    baseVolumeName = "TargetBaseVol"
+                    volume = {'size': int(self.utils.convert_bits_to_gbs(
+                        volumeSizeInbits))}
+                    rc, baseVolumeDict, storageSystemName = (
+                        self._create_composite_volume(
+                            volume, extraSpecs,
+                            baseVolumeName, volumeSizeInbits))
+                    baseTargetVolumeInstance = self.utils.find_volume_instance(
+                        self.conn, baseVolumeDict, baseVolumeName)
+                    LOG.info(_LI("Base target volume %(targetVol)s created. "
+                                 "Capacity in bits: %(capInBits)lu ")
+                             % {'capInBits': volumeSizeInbits,
+                                'targetVol': baseTargetVolumeInstance.path})
+                else:  # create append volume
+                    targetVolumeName = "MetaVol"
+                    volume = {'size': int(self.utils.convert_bits_to_gbs(
+                        volumeSizeInbits))}
+                    storageConfigService = (
+                        self.utils.find_storage_configuration_service(
+                            self.conn, storageSystemName))
+                    unboundVolumeInstance = (
+                        self._create_and_get_unbound_volume(
+                            self.conn, storageConfigService,
+                            baseTargetVolumeInstance.path, volumeSizeInbits))
+                    if unboundVolumeInstance is None:
+                        exceptionMessage = (_(
+                            "Error Creating unbound volume."))
+                        LOG.error(exceptionMessage)
+                        raise exception.VolumeBackendAPIException(
+                            data=exceptionMessage)
+
+                    # append the new unbound volume to the
+                    # 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))
+                    if modifiedVolumeDict is None:
+                        exceptionMessage = (_(
+                            "Error appending volume %(volumename)s to "
+                            "target base volume")
+                            % {'volumename': targetVolumeName})
+                        LOG.error(exceptionMessage)
+                        raise exception.VolumeBackendAPIException(
+                            data=exceptionMessage)
+
+            LOG.debug("Create replica for meta members of different sizes.")
+            return self._create_replica_and_delete_clone_relationship(
+                repServiceInstanceName, cloneVolume, sourceVolume,
+                sourceInstance, baseTargetVolumeInstance, extraSpecs)
+
+    def _create_replica_and_delete_clone_relationship(
+            self, repServiceInstanceName, cloneVolume, sourceVolume,
+            sourceInstance, targetInstance, extraSpecs):
+        """Helper function to create a clone and delete the relationship.
+
+        This function creates clone of the source volume and then
+        delete the clone replatinship once the copy completes.
+
+        :param repServiceInstanceName: replication service instance name
+        :param cloneVolume: target volume of the clone operation
+        :param sourceVolume: source volume of the clone operation
+        :param sourceInstance: instance of ECOM StorageVolume object
+        :param targetInstance: instance of ECOM StorageVolume object
+        :param extraSpecs: extraSpecs info
+        :returns: rc the return code, cloneDict the cloned volume dictionary
+        """
+        sourceName = sourceVolume['name']
+        cloneName = cloneVolume['name']
         # Create a Clone from source volume
         rc, job = self.provision.create_element_replica(
             self.conn, repServiceInstanceName, cloneName, sourceName,
-            sourceInstance)
+            sourceInstance, targetInstance)
 
         cloneDict = self.provision.get_volume_dict_from_job(
             self.conn, job['Job'])
@@ -1960,9 +1997,15 @@ class EMCVMAXCommon(object):
                 raise exception.VolumeBackendAPIException(
                     data=exceptionMessage)
 
-            self._add_volume_to_default_storage_group_on_create(
-                cloneDict, cloneName, storageConfigService, storageSystemName,
-                extraSpecs[FASTPOLICY])
+            # check if the clone/snapshot volume already part of the default sg
+            cloneInstance = self.utils.find_volume_instance(
+                self.conn, cloneDict, cloneName)
+            inDefaultSG = self.fast.is_volume_in_default_SG(
+                self.conn, cloneInstance.path)
+            if inDefaultSG is False:
+                self._add_volume_to_default_storage_group_on_create(
+                    cloneDict, cloneName, storageConfigService,
+                    storageSystemName, extraSpecs[FASTPOLICY])
 
         LOG.debug("Leaving _create_cloned_volume: Volume: "
                   "%(cloneName)s Source Volume: %(sourceName)s  "
@@ -2051,7 +2094,7 @@ class EMCVMAXCommon(object):
                         self.conn, controllerConfigurationService,
                         volumeInstance, volumeName, fastPolicyName))
                 if assocDefaultStorageGroupName is None:
-                    errorMsg = (_(
+                    errorMsg = (_LE(
                         "Failed to Roll back to re-add volume %(volumeName)s "
                         "to default storage group for fast policy "
                         "%(fastPolicyName)s: Please contact your sysadmin to "
@@ -2218,7 +2261,7 @@ class EMCVMAXCommon(object):
             self.conn, maskingViewInstanceName)
 
     def get_masking_view_by_volume(self, volume):
-        """Given volume, retrieve the masking view instance name
+        """Given volume, retrieve the masking view instance name.
 
         :param volume: the volume
         :param mvInstanceName: masking view instance name
@@ -2231,7 +2274,7 @@ class EMCVMAXCommon(object):
             self.conn, volumeInstance)
 
     def get_masking_views_by_port_group(self, portGroupInstanceName):
-        """Given port group, retrieve the masking view instance name
+        """Given port group, retrieve the masking view instance name.
 
         :param : the volume
         :param mvInstanceName: masking view instance name
@@ -2241,3 +2284,93 @@ class EMCVMAXCommon(object):
                   % {'pg': portGroupInstanceName})
         return self.masking.get_masking_views_by_port_group(
             self.conn, portGroupInstanceName)
+
+    def _create_composite_volume(
+            self, volume, extraSpecs, volumeName, volumeSize):
+        """Create a composite volume.
+
+        :param volume: the volume object
+        :param extraSpecs:
+        :param volumeName:
+        :param volumeSize:
+        :returns:
+        """
+        memberCount, errorDesc = self.utils.determine_member_count(
+            volume['size'], extraSpecs[MEMBERCOUNT], extraSpecs[COMPOSITETYPE])
+        if errorDesc is not None:
+            exceptionMessage = (_("The striped meta count of %(memberCount)s "
+                                  "is too small for volume: %(volumeName)s. "
+                                  "with size %(volumeSize)s ")
+                                % {'memberCount': memberCount,
+                                   'volumeName': volumeName,
+                                   'volumeSize': volume['size']})
+            LOG.error(exceptionMessage)
+            raise exception.VolumeBackendAPIException(data=exceptionMessage)
+
+        poolInstanceName, storageSystemName = (
+            self._get_pool_and_storage_system(extraSpecs))
+
+        LOG.debug("Create Volume: %(volume)s  Pool: %(pool)s  "
+                  "Storage System: %(storageSystem)s "
+                  "Size: %(size)lu "
+                  % {'volume': volumeName,
+                     'pool': poolInstanceName,
+                     'storageSystem': storageSystemName,
+                     'size': volumeSize})
+
+        elementCompositionService = (
+            self.utils.find_element_composition_service(self.conn,
+                                                        storageSystemName))
+
+        storageConfigService = self.utils.find_storage_configuration_service(
+            self.conn, storageSystemName)
+
+        # If FAST is intended to be used we must first check that the pool
+        # is associated with the correct storage tier
+        if extraSpecs[FASTPOLICY] is not None:
+            foundPoolInstanceName = self.fast.get_pool_associated_to_policy(
+                self.conn, extraSpecs[FASTPOLICY], extraSpecs[ARRAY],
+                storageConfigService, poolInstanceName)
+            if foundPoolInstanceName is None:
+                exceptionMessage = (_("Pool: %(poolName)s. "
+                                      "is not associated to storage tier for "
+                                      "fast policy %(fastPolicy)s.")
+                                    % {'poolName': extraSpecs[POOL],
+                                       'fastPolicy': extraSpecs[FASTPOLICY]})
+                LOG.error(exceptionMessage)
+                raise exception.VolumeBackendAPIException(
+                    data=exceptionMessage)
+
+        compositeType = self.utils.get_composite_type(
+            extraSpecs[COMPOSITETYPE])
+
+        volumeDict, rc = self.provision.create_composite_volume(
+            self.conn, elementCompositionService, volumeSize, volumeName,
+            poolInstanceName, compositeType, memberCount)
+
+        # Now that we have already checked that the pool is associated with
+        # the correct storage tier and the volume was successfully created
+        # add the volume to the default storage group created for
+        # volumes in pools associated with this fast policy
+        if extraSpecs[FASTPOLICY]:
+            LOG.info(_LI("Adding volume: %(volumeName)s to default storage "
+                         "group for FAST policy: %(fastPolicyName)s ")
+                     % {'volumeName': volumeName,
+                        'fastPolicyName': extraSpecs[FASTPOLICY]})
+            defaultStorageGroupInstanceName = (
+                self._get_or_create_default_storage_group(
+                    self.conn, storageSystemName, volumeDict,
+                    volumeName, extraSpecs[FASTPOLICY]))
+            if not defaultStorageGroupInstanceName:
+                exceptionMessage = (_(
+                    "Unable to create or get default storage group for "
+                    "FAST policy: %(fastPolicyName)s. ")
+                    % {'fastPolicyName': extraSpecs[FASTPOLICY]})
+                LOG.error(exceptionMessage)
+                raise exception.VolumeBackendAPIException(
+                    data=exceptionMessage)
+
+            self._add_volume_to_default_storage_group_on_create(
+                volumeDict, volumeName, storageConfigService,
+                storageSystemName, extraSpecs[FASTPOLICY])
+        return rc, volumeDict, storageSystemName
index 677a2c7a2479806ab63467bd6fe2eb07d0f0ace3..d3512f7f021ec8429121923c1cd1ddc57ecfd28e 100644 (file)
@@ -140,12 +140,12 @@ class EMCVMAXFast(object):
             foundDefaultStorageGroupInstanceName = (
                 assocStorageGroupInstanceName)
         else:
-            exceptionMessage = (_(
+            errorMessage = (_LW(
                 "Volume: %(volumeName)s Does not belong "
                 "to storage storage group %(defaultSgGroupName)s. ")
                 % {'volumeName': volumeName,
                    'defaultSgGroupName': defaultSgGroupName})
-            LOG.warn(exceptionMessage)
+            LOG.warn(errorMessage)
         return foundDefaultStorageGroupInstanceName
 
     def add_volume_to_default_storage_group_for_fast_policy(
@@ -402,7 +402,7 @@ class EMCVMAXFast(object):
         if len(storageTierInstanceNames) == 0:
             storageTierInstanceNames = None
             LOG.warn(_LW("Unable to get storage tiers "
-                         "from tier policy rule  "))
+                         "from tier policy rule."))
 
         return storageTierInstanceNames
 
@@ -490,7 +490,7 @@ class EMCVMAXFast(object):
         tierPolicyRuleInstanceName = self._get_service_level_tier_policy(
             conn, tierPolicyServiceInstanceName, fastPolicyName)
         if tierPolicyRuleInstanceName is None:
-            errorMessage = (_(
+            errorMessage = (_LE(
                 "Cannot find the fast policy %(fastPolicyName)s")
                 % {'fastPolicyName': fastPolicyName})
 
@@ -511,7 +511,7 @@ class EMCVMAXFast(object):
                     storageGroupName, fastPolicyName)
             except Exception as ex:
                 LOG.error(_LE("Exception: %s") % six.text_type(ex))
-                errorMessage = (_(
+                errorMessage = (_LE(
                     "Failed to add storage group %(storageGroupInstanceName)s "
                     " to tier policy rule %(tierPolicyRuleInstanceName)s")
                     % {'storageGroupInstanceName': storageGroupInstanceName,
@@ -578,7 +578,7 @@ class EMCVMAXFast(object):
                 rc, errordesc = self.utils.wait_for_job_complete(conn, job)
                 if rc != 0L:
                     LOG.error(_LE("Error disassociating storage group from "
-                              "policy: %s") % errordesc)
+                                  "policy: %s") % errordesc)
                 else:
                     LOG.debug("Disassociated storage group from policy %s")
             else:
@@ -766,3 +766,25 @@ class EMCVMAXFast(object):
                 fastPolicyName = tierPolicyInstanceName['PolicyRuleName']
 
         return fastPolicyName
+
+    def is_volume_in_default_SG(self, conn, volumeInstanceName):
+        """Check if the volume is already part of the default storage group.
+
+        :param volumeInstanceName: the volume instance
+        :returns: True if the volume is already in default storage group
+                  False otherwise
+        """
+        sgInstanceNames = conn.AssociatorNames(
+            volumeInstanceName,
+            ResultClass='CIM_DeviceMaskingGroup')
+        if len(sgInstanceNames) == 0:
+            LOG.debug("volume  %(vol)s is not in default sg."
+                      % {'vol': volumeInstanceName})
+            return False
+        else:
+            for sgInstance in sgInstanceNames:
+                if DEFAULT_SG_PREFIX in sgInstance['InstanceID']:
+                    LOG.debug("volume  %(vol)s already in default sg."
+                              % {'vol': volumeInstanceName})
+                    return True
+        return False
index 4a58a19677f77d7141fa5ff0a9948c0dd77c370c..f305669f87cd69619faa90d09e83334ee2550662 100644 (file)
@@ -543,7 +543,7 @@ class EMCVMAXProvision(object):
 
     def create_element_replica(
             self, conn, repServiceInstanceName, cloneName,
-            sourceName, sourceInstance):
+            sourceName, sourceInstance, targetInstance):
         """Make SMI-S call to create replica for source element.
 
         :param conn: the connection to the ecom server
@@ -551,14 +551,23 @@ class EMCVMAXProvision(object):
         :param cloneName: replica name
         :param sourceName: source volume name
         :param sourceInstance: source volume instance
+        :param targetInstance: target volume instance
         :returns: rc - return code
         :returns: job - job object of the replica creation operation
         """
-        rc, job = conn.InvokeMethod(
-            'CreateElementReplica', repServiceInstanceName,
-            ElementName=cloneName,
-            SyncType=self.utils.get_num(8, '16'),
-            SourceElement=sourceInstance.path)
+        if targetInstance is None:
+            rc, job = conn.InvokeMethod(
+                'CreateElementReplica', repServiceInstanceName,
+                ElementName=cloneName,
+                SyncType=self.utils.get_num(8, '16'),
+                SourceElement=sourceInstance.path)
+        else:
+            rc, job = conn.InvokeMethod(
+                'CreateElementReplica', repServiceInstanceName,
+                ElementName=cloneName,
+                SyncType=self.utils.get_num(8, '16'),
+                SourceElement=sourceInstance.path,
+                TargetElement=targetInstance.path)
 
         if rc != 0L:
             rc, errordesc = self.utils.wait_for_job_complete(conn, job)
index 59ba6db4ec71c5c5dd62ee171c65f7626863ace4..a8d9022c34454b9a611bae173efd24f74f8aff86 100644 (file)
@@ -346,11 +346,11 @@ class EMCVMAXUtils(object):
         """
 
         def _wait_for_sync():
-            """Called at an interval until the synchronization is finished"""
+            """Called at an interval until the synchronization is finished."""
             if self._is_sync_complete(conn, syncName):
                 raise loopingcall.LoopingCallDone()
             if self.retries > JOB_RETRIES:
-                LOG.error(_LE("_wait_for_sync failed after %(retries)d tries")
+                LOG.error(_LE("_wait_for_sync failed after %(retries)d tries.")
                           % {'retries': self.retries})
                 raise loopingcall.LoopingCallDone()
             try:
@@ -1170,3 +1170,52 @@ class EMCVMAXUtils(object):
                 break
 
         return foundIpAddress
+
+    def get_volume_meta_head(self, conn, volumeInstanceName):
+        """Get the head of a meta volume.
+
+        :param volumeInstanceName: the composite volume instance name
+        :returns: the instance name of the meta volume head
+        """
+        metaHeadInstanceName = None
+        metaHeads = conn.AssociatorNames(
+            volumeInstanceName,
+            ResultClass='EMC_Meta')
+
+        if len(metaHeads) > 0:
+            metaHeadInstanceName = metaHeads[0]
+        if metaHeadInstanceName is None:
+            LOG.info(_LI("Volume  %(volume)s does not have meta device "
+                         "members."),
+                     {'volume': volumeInstanceName})
+
+        return metaHeadInstanceName
+
+    def get_meta_members_of_composite_volume(
+            self, conn, metaHeadInstanceName):
+        """Get the member volumes of a composite volume.
+
+        :param metaHeadInstanceName: head of the composite volume
+        :returns: an array containing instance names of member volumes
+        """
+        metaMembers = conn.AssociatorNames(
+            metaHeadInstanceName,
+            AssocClass='CIM_BasedOn',
+            ResultClass='EMC_PartialAllocOfConcreteExtent')
+        LOG.debug("metaMembers:  %(members)s " % {'members': metaMembers})
+        return metaMembers
+
+    def get_meta_members_capacity_in_bit(self, conn, volumeInstanceNames):
+        """Get the capacity in bits of all meta device member volumes.
+
+        :param volumeInstanceNames: array contains meta device member volumes
+        :returns: array contains capacities of each member device in bits
+        """
+        capacitiesInBit = []
+        for volumeInstanceName in volumeInstanceNames:
+            volumeInstance = conn.GetInstance(volumeInstanceName)
+            numOfBlocks = volumeInstance['ConsumableBlocks']
+            blockSize = volumeInstance['BlockSize']
+            volumeSizeInbits = numOfBlocks * blockSize
+            capacitiesInBit.append(volumeSizeInbits)
+        return capacitiesInBit