Merge "Fix a clone volume problem in VMAX driver"
This commit is contained in:
commit
83bc931790
|
@ -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))
|
||||
@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)
|
||||
'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,
|
||||
'_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))
|
||||
@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)
|
||||
'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,
|
||||
'_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)
|
||||
|
||||
@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)
|
||||
@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',
|
||||
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)
|
||||
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'})
|
||||
@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,
|
||||
'find_replication_service',
|
||||
return_value=None)
|
||||
'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_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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue