Merge "Fix a clone volume problem in VMAX driver"

This commit is contained in:
Jenkins 2014-12-08 05:32:21 +00:00 committed by Gerrit Code Review
commit 83bc931790
5 changed files with 514 additions and 390 deletions

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

View 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

View 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

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

View 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