VMAX driver - Manage/Unmanage performance fix

Previously managing an existing volume made a call to the ECOM
server which retrieved every volume across all connected arrays.
When this call was made if there were heavily populated arrays
the call would hang and eventually crash. To get around this,
instead of seeking the volume at the start of the manage operation,
the relevant details are filled in to a CIMInstanceName object and
this object is used for a more directed search to the ECOM server,
returning the volume successfully if it exists.

Change-Id: Ib8a628016caf88f67ec54ce10bc735053b70c8c2
Closes-Bug: #1681888
(cherry picked from commit dd7ea30547)
This commit is contained in:
Helen Walsh 2017-04-12 13:49:37 +01:00
parent 01bede894b
commit a051532fdc
3 changed files with 649 additions and 195 deletions

View File

@ -384,6 +384,22 @@ class VMAXCommonData(object):
provider_location_multi_pool = {'classname': 'Symm_StorageVolume',
'keybindings': keybindings,
'version': '2.2.0'}
keybindings_manage = {'CreationClassName': 'Symm_StorageVolume',
'SystemName': 'SYMMETRIX+000195900551',
'DeviceID': '10',
'SystemCreationClassName': 'Symm_StorageSystem'}
provider_location_manage = {'classname': 'Symm_StorageVolume',
'keybindings': keybindings_manage}
manage_vol = EMC_StorageVolume()
manage_vol['CreationClassName'] = 'Symm_StorageVolume'
manage_vol['ElementName'] = 'OS-Test_Manage_vol'
manage_vol['DeviceID'] = '10'
manage_vol['SystemName'] = 'SYMMETRIX+000195900551'
manage_vol['SystemCreationClassName'] = 'Symm_StorageSystem'
manage_vol.path = manage_vol
replication_driver_data = re_keybindings
block_size = 512
majorVersion = 1
@ -400,6 +416,9 @@ class VMAXCommonData(object):
properties = {'ConsumableBlocks': '12345',
'BlockSize': '512'}
array = '000197800123'
array_v3 = '1234567891011'
test_volume = {'name': 'vol1',
'size': 1,
'volume_name': 'vol1',
@ -3942,17 +3961,6 @@ class VMAXISCSIDriverNoFastTestCase(test.TestCase):
common._create_composite_volume.assert_called_with(
volume, "TargetBaseVol", 1234567, extraSpecs, 1)
def test_find_volume_by_device_id_on_array(self):
conn = self.fake_ecom_connection()
utils = self.driver.common.utils
volumeInstanceName = utils.find_volume_by_device_id_on_array(
conn, self.data.storage_system, self.data.test_volume['device_id'])
expectVolume = {}
expectVolume['CreationClassName'] = 'Symm_StorageVolume'
expectVolume['DeviceID'] = self.data.test_volume['device_id']
expect = conn.GetInstance(expectVolume)
self.assertEqual(expect, volumeInstanceName)
def test_get_volume_element_name(self):
volumeId = 'ea95aa39-080b-4f11-9856-a03acf9112ad'
util = self.driver.common.utils
@ -3974,10 +3982,11 @@ class VMAXISCSIDriverNoFastTestCase(test.TestCase):
def test_get_array_and_device_id_success(self):
deviceId = '0123'
arrayId = u'array1234'
arrayId = '1234567891011'
external_ref = {u'source-name': deviceId}
volume = {'volume_metadata': [{'key': 'array', 'value': arrayId}]
}
volume['host'] = 'HostX@Backend#Bronze+SRP_1+1234567891011'
utils = self.driver.common.utils
(arrId, devId) = utils.get_array_and_device_id(volume, external_ref)
self.assertEqual(arrayId, arrId)
@ -3985,10 +3994,11 @@ class VMAXISCSIDriverNoFastTestCase(test.TestCase):
def test_get_array_and_device_id_failed(self):
deviceId = '0123'
arrayId = u'array1234'
arrayId = '1234567891011'
external_ref = {u'no-source-name': deviceId}
volume = {'volume_metadata': [{'key': 'array', 'value': arrayId}]
}
volume['host'] = 'HostX@Backend#Bronze+SRP_1+1234567891011'
utils = self.driver.common.utils
self.assertRaises(exception.VolumeBackendAPIException,
utils.get_array_and_device_id,
@ -5242,73 +5252,6 @@ class VMAXFCDriverNoFastTestCase(test.TestCase):
self.driver.delete_cgsnapshot(
self.data.test_ctxt, self.data.test_CG_snapshot, [])
@ddt.data((2, 2), (1.75, 2))
@ddt.unpack
def test_manage_existing_get_size(self, gb_size, exp_size):
volume = {}
metadata = {'key': 'array',
'value': '12345'}
volume['volume_metadata'] = [metadata]
external_ref = {'source-name': '0123'}
utils = self.driver.common.utils
utils.get_volume_size = mock.Mock(
return_value=int(gb_size * units.Gi))
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
utils.find_volume_by_device_id_on_array = mock.Mock(
return_value=volumeInstanceName)
size = self.driver.manage_existing_get_size(volume, external_ref)
self.assertEqual(exp_size, size)
def test_manage_existing_no_fast_success(self):
volume = {}
metadata = {'key': 'array',
'value': '12345'}
poolInstanceName = {}
storageSystem = {}
poolInstanceName['InstanceID'] = "SATA_GOLD1"
storageSystem['InstanceID'] = "SYMMETRIX+00019870000"
volume['volume_metadata'] = [metadata]
volume['name'] = "test-volume"
volume['id'] = "test-volume"
external_ref = {'source-name': '0123'}
utils = self.driver.common.utils
gbSize = 2
utils.get_volume_size = mock.Mock(
return_value=gbSize * units.Gi)
utils.get_associated_replication_from_source_volume = mock.Mock(
return_value=None)
utils.get_assoc_pool_from_volume = mock.Mock(
return_value=(poolInstanceName))
vol = EMC_StorageVolume()
vol['CreationClassName'] = 'Symm_StorageVolume'
vol['ElementName'] = 'OS-' + volume['id']
vol['DeviceID'] = external_ref['source-name']
vol['SystemName'] = storageSystem['InstanceID']
vol['SystemCreationClassName'] = 'Symm_StorageSystem'
vol.path = vol
utils.rename_volume = mock.Mock(
return_value=vol)
common = self.driver.common
common._initial_setup = mock.Mock(
return_value={'volume_backend_name': 'FCNoFAST',
'storagetype:fastpolicy': None})
common._get_pool_and_storage_system = mock.Mock(
return_value=(poolInstanceName, storageSystem))
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
utils.find_volume_by_device_id_on_array = mock.Mock(
return_value=volumeInstanceName)
masking = self.driver.common.masking
masking.get_masking_view_from_storage_group = mock.Mock(
return_value={})
self.driver.manage_existing(volume, external_ref)
utils.rename_volume.assert_called_once_with(
common.conn, volumeInstanceName, volume['name'])
def test_unmanage_no_fast_success(self):
keybindings = {'CreationClassName': u'Symm_StorageVolume',
'SystemName': u'SYMMETRIX+000195900000',
@ -6030,6 +5973,7 @@ class VMAXFCDriverFastTestCase(test.TestCase):
storageSystem['InstanceID'] = "SYMMETRIX+00019870000"
volume['volume_metadata'] = [metadata]
volume['name'] = "test-volume"
volume['host'] = 'HostX@Backend#Bronze+SRP_1+1234567891011'
external_ref = {'source-name': '0123'}
common = self.driver.common
common._initial_setup = mock.Mock(
@ -8781,6 +8725,215 @@ class VMAXUtilsTest(test.TestCase):
conn, storageSystem, volumeInstance, extraSpecs)
self.assertEqual(syncInstance, foundSyncInstance)
def test_get_assoc_v2_pool_from_vol(self):
conn = FakeEcomConnection()
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
pool = conn.AssociatorNames(
volumeInstanceName, ResultClass='EMC_VirtualProvisioningPool')
poolName = self.driver.utils.get_assoc_v2_pool_from_volume(
conn, volumeInstanceName)
self.assertEqual(pool[0]['ElementName'], poolName['ElementName'])
def test_get_assoc_v2_pool_from_vol_fail(self):
conn = FakeEcomConnection()
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
conn.AssociatorNames = mock.Mock(return_value={})
poolName = self.driver.utils.get_assoc_v2_pool_from_volume(
conn, volumeInstanceName)
self.assertIsNone(poolName)
def test_get_assoc_v3_pool_from_vol(self):
conn = FakeEcomConnection()
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
pool = conn.AssociatorNames(
volumeInstanceName, ResultClass='Symm_SRPStoragePool')
poolName = self.driver.utils.get_assoc_v3_pool_from_volume(
conn, volumeInstanceName)
self.assertEqual(pool[0]['ElementName'], poolName['ElementName'])
def test_get_assoc_v3_pool_from_vol_fail(self):
conn = FakeEcomConnection()
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
conn.AssociatorNames = mock.Mock(return_value={})
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.utils.get_assoc_v3_pool_from_volume,
conn, volumeInstanceName)
def test_check_volume_no_fast_fail(self):
utils = self.driver.common.utils
initial_setup = {'volume_backend_name': 'FCFAST',
'storagetype:fastpolicy': 'GOLD'}
self.assertRaises(exception.VolumeBackendAPIException,
utils.check_volume_no_fast,
initial_setup)
def test_check_volume_no_fast_pass(self):
utils = self.driver.common.utils
initial_setup = {'volume_backend_name': 'FCnoFAST',
'storagetype:fastpolicy': None}
self.assertTrue(utils.check_volume_no_fast(
initial_setup))
def test_check_volume_not_in_masking_view_pass(self):
conn = FakeEcomConnection()
utils = self.driver.common.utils
bindings = {'CreationClassName': 'Symm_StorageVolume',
'SystemName': self.data.storage_system,
'DeviceID': self.data.test_volume['device_id'],
'SystemCreationClassName': 'Symm_StorageSystem'}
inst = FakeCIMInstanceName()
fake_inst = inst.fake_getinstancename('Symm_StorageVolume', bindings)
sgInstanceNames = conn.AssociatorNames(fake_inst,
ResultClass=
'CIM_DeviceMaskingGroup')
conn.AssociatorNames = mock.Mock(return_value={})
mock.patch.object(self.driver.utils, 'get_storage_groups_from_volume',
return_value=sgInstanceNames)
self.assertTrue(
utils.check_volume_not_in_masking_view(
conn, fake_inst, self.data.test_volume['device_id']))
def test_check_volume_not_in_masking_view_fail(self):
conn = FakeEcomConnection()
utils = self.driver.common.utils
bindings = {'CreationClassName': 'Symm_StorageVolume',
'SystemName': self.data.storage_system,
'DeviceID': self.data.test_volume['device_id'],
'SystemCreationClassName': 'Symm_StorageSystem'}
inst = FakeCIMInstanceName()
fake_inst = inst.fake_getinstancename('Symm_StorageVolume', bindings)
self.assertRaises(exception.VolumeBackendAPIException,
utils.check_volume_not_in_masking_view,
conn, fake_inst, self.data.test_volume['device_id'])
def test_check_volume_not_replication_source_pass(self):
conn = FakeEcomConnection()
utils = self.driver.common.utils
self.assertTrue(
utils.check_volume_not_replication_source(
conn, self.data.storage_system_v3,
self.data.test_volume['device_id']))
def test_check_volume_not_replication_source_fail(self):
conn = FakeEcomConnection()
utils = self.driver.common.utils
replication_source = 'testReplicationSync'
utils.get_associated_replication_from_source_volume = (
mock.Mock(return_value=replication_source))
self.assertRaises(
exception.VolumeBackendAPIException,
utils.check_volume_not_replication_source,
conn, self.data.storage_system_v3,
self.data.test_volume['device_id'])
def test_check_is_volume_in_cinder_managed_pool_fail(self):
conn = FakeEcomConnection()
utils = self.driver.common.utils
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
poolInstanceName = {}
poolInstanceName['InstanceID'] = "SATA_GOLD1"
deviceId = '0123'
self.assertRaises(
exception.VolumeBackendAPIException,
utils.check_is_volume_in_cinder_managed_pool,
conn, volumeInstanceName, poolInstanceName, deviceId)
def test_check_is_volume_in_cinder_managed_pool_pass(self):
conn = FakeEcomConnection()
utils = self.driver.common.utils
volumeInstanceName = {}
poolInstanceName = {}
poolInstanceName['InstanceID'] = "SATA_GOLD2"
deviceId = self.data.test_volume['device_id']
utils.get_assoc_v2_pool_from_volume = (
mock.Mock(return_value=poolInstanceName))
self.assertTrue(
utils.check_is_volume_in_cinder_managed_pool(
conn, volumeInstanceName, poolInstanceName, deviceId))
def test_find_volume_by_device_id_on_array(self):
conn = FakeEcomConnection()
utils = self.driver.common.utils
bindings = {'CreationClassName': 'Symm_StorageVolume',
'SystemName': self.data.storage_system,
'DeviceID': self.data.test_volume['device_id'],
'SystemCreationClassName': 'Symm_StorageSystem'}
inst = FakeCIMInstanceName()
fake_inst = inst.fake_getinstancename('Symm_StorageVolume', bindings)
utils.find_volume_by_device_id_on_array = mock.Mock(
return_value=fake_inst)
volumeInstanceName = utils.find_volume_by_device_id_on_array(
self.data.storage_system, self.data.test_volume['device_id'])
expectVolume = {}
expectVolume['CreationClassName'] = 'Symm_StorageVolume'
expectVolume['DeviceID'] = self.data.test_volume['device_id']
expect = conn.GetInstance(expectVolume)
provider_location = ast.literal_eval(expect['provider_location'])
bindings = provider_location['keybindings']
self.assertEqual(bindings, volumeInstanceName)
def test_get_array_and_device_id(self):
utils = self.driver.common.utils
volume = self.data.test_volume.copy()
volume['volume_metadata'] = {'array': self.data.array_v3}
external_ref = {u'source-name': u'00002'}
array, device_id = utils.get_array_and_device_id(
volume, external_ref)
self.assertEqual(self.data.array_v3, array)
self.assertEqual('00002', device_id)
def test_get_array_and_device_id_exception(self):
utils = self.driver.common.utils
volume = self.data.test_volume.copy()
volume['volume_metadata'] = {'array': self.data.array}
external_ref = {u'source-name': None}
self.assertRaises(exception.VolumeBackendAPIException,
utils.get_array_and_device_id, volume, external_ref)
class VMAXCommonTest(test.TestCase):
def setUp(self):
@ -9567,6 +9720,152 @@ class VMAXCommonTest(test.TestCase):
exception.VolumeBackendAPIException,
common._get_port_group_from_source, deviceInfoDict)
def test_manage_existing_get_size(self):
common = self.driver.common
common.conn = FakeEcomConnection()
gb_size = 2
exp_size = 2
volume = {}
metadata = {'key': 'array',
'value': '12345'}
volume['volume_metadata'] = [metadata]
volume['host'] = 'HostX@Backend#Bronze+SRP_1+1234567891011'
external_ref = {'source-name': '0123'}
volumeInstanceName = {'CreationClassName': "Symm_StorageVolume",
'DeviceID': "0123",
'SystemName': "12345"}
utils = self.driver.common.utils
utils.get_volume_size = mock.Mock(
return_value=int(gb_size * units.Gi))
utils.find_volume_by_device_id_on_array = mock.Mock(
return_value=volumeInstanceName)
size = self.driver.manage_existing_get_size(volume, external_ref)
self.assertEqual(exp_size, size)
def test_manage_existing_get_size_fail(self):
common = self.driver.common
common.conn = FakeEcomConnection()
gb_size = 2
volume = {}
metadata = {'key': 'array',
'value': '12345'}
volume['volume_metadata'] = [metadata]
volume['host'] = 'HostX@Backend#Bronze+SRP_1+1234567891011'
external_ref = {'source-name': '0123'}
utils = self.driver.common.utils
utils.get_volume_size = mock.Mock(
return_value=int(gb_size * units.Gi))
utils.find_volume_by_device_id_on_array = mock.Mock(
return_value=None)
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.common.manage_existing_get_size,
volume, external_ref)
def test_set_volume_replication_if_enabled(self):
common = self.driver.common
common.conn = FakeEcomConnection()
volume = {}
provider_location = {}
replication_status = 'replicated'
replication_driver_data = 'replication_data'
model_update = {}
model_update.update(
{'replication_status': replication_status})
model_update.update(
{'replication_driver_data': six.text_type(
replication_driver_data)})
extra_specs = self.data.extra_specs_is_re
common.setup_volume_replication = mock.Mock(
return_value=(replication_status, replication_driver_data))
new_model_update = common.set_volume_replication_if_enabled(
common.conn, extra_specs, volume, provider_location)
self.assertEqual(new_model_update, model_update)
@mock.patch.object(
common.VMAXCommon,
'set_volume_replication_if_enabled',
return_value={'replication_status': 'replicated',
'replication_driver_data': 'driver_data',
'display_name': 'vol1',
'provider_location':
VMAXCommonData.provider_location3})
@mock.patch.object(
utils.VMAXUtils,
'rename_volume',
return_value=VMAXCommonData.manage_vol)
@mock.patch.object(
utils.VMAXUtils,
'check_is_volume_in_cinder_managed_pool',
return_value=True)
@mock.patch.object(
utils.VMAXUtils,
'check_volume_not_replication_source',
return_value=True)
@mock.patch.object(
common.VMAXCommon,
'_get_pool_and_storage_system',
return_value=('cinder_pool', 'vmax_storage_system'))
@mock.patch.object(
utils.VMAXUtils,
'check_volume_not_in_masking_view',
return_value=True)
@mock.patch.object(
utils.VMAXUtils,
'find_volume_by_device_id_on_array',
return_value=VMAXCommonData.test_volume)
@mock.patch.object(
utils.VMAXUtils,
'check_volume_no_fast',
return_value=True)
@mock.patch.object(
utils.VMAXUtils,
'get_array_and_device_id',
return_value=('12345', '1'))
@mock.patch.object(
common.VMAXCommon,
'_get_ecom_connection',
return_value=FakeEcomConnection())
@mock.patch.object(
common.VMAXCommon,
'_initial_setup',
return_value=VMAXCommonData.extra_specs_is_re)
def test_manage_existing(self, mock_setup, mock_ecom, mock_ids,
mock_vol_fast, mock_vol_by_deviceId,
mock_vol_in_mv, mock_pool_sg, mock_vol_rep_src,
mock_vol_in_mng_pool, mock_rename_vol,
mock_set_vol_rep):
common = self.driver.common
volume = EMC_StorageVolume()
volume.name = 'vol1'
volume.display_name = 'vol1'
external_ref = {}
model_update = {
'replication_status': 'replicated',
'replication_driver_data': 'driver_data',
'display_name': 'vol1',
'provider_location': six.text_type(
self.data.provider_location_manage)}
new_model_update = common.manage_existing(volume,
external_ref)
self.assertEqual(model_update, new_model_update)
class VMAXProvisionTest(test.TestCase):
def setUp(self):

View File

@ -14,7 +14,6 @@
# under the License.
import ast
import math
import os.path
from oslo_config import cfg
@ -4636,84 +4635,34 @@ class VMAXCommon(object):
"""
extraSpecs = self._initial_setup(volume)
self.conn = self._get_ecom_connection()
arrayName, deviceId = self.utils.get_array_and_device_id(volume,
external_ref)
arrayName, deviceId = self.utils.get_array_and_device_id(
volume, external_ref)
self.utils.check_volume_no_fast(extraSpecs)
# Manage existing volume is not supported if fast enabled.
if extraSpecs[FASTPOLICY]:
LOG.warning(_LW(
"FAST is enabled. Policy: %(fastPolicyName)s."),
{'fastPolicyName': extraSpecs[FASTPOLICY]})
exceptionMessage = (_(
"Manage volume is not supported if FAST is enable. "
"FAST policy: %(fastPolicyName)s.")
% {'fastPolicyName': extraSpecs[FASTPOLICY]})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
# Check if the volume is attached by checking if in any masking view.
volumeInstanceName = (
self.utils.find_volume_by_device_id_on_array(self.conn,
arrayName, deviceId))
sgInstanceNames = (
self.utils.get_storage_groups_from_volume(
self.conn, volumeInstanceName))
self.utils.find_volume_by_device_id_on_array(
arrayName, deviceId))
for sgInstanceName in sgInstanceNames:
mvInstanceNames = (
self.masking.get_masking_view_from_storage_group(
self.conn, sgInstanceName))
for mvInstanceName in mvInstanceNames:
exceptionMessage = (_(
"Unable to import volume %(deviceId)s to cinder. "
"Volume is in masking view %(mv)s.")
% {'deviceId': deviceId,
'mv': mvInstanceName})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
self.utils.check_volume_not_in_masking_view(
self.conn, volumeInstanceName, deviceId)
# Check if there is any associated snapshots with the volume.
cinderPoolInstanceName, storageSystemName = (
self._get_pool_and_storage_system(extraSpecs))
repSessionInstanceName = (
self.utils.get_associated_replication_from_source_volume(
self.conn, storageSystemName, deviceId))
if repSessionInstanceName:
exceptionMessage = (_(
"Unable to import volume %(deviceId)s to cinder. "
"It is the source volume of replication session %(sync)s.")
% {'deviceId': deviceId,
'sync': repSessionInstanceName})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
# Make sure the existing external volume is in the same storage pool.
volumePoolInstanceName = (
self.utils.get_assoc_pool_from_volume(self.conn,
volumeInstanceName))
volumePoolName = volumePoolInstanceName['InstanceID']
cinderPoolName = cinderPoolInstanceName['InstanceID']
LOG.debug("Storage pool of existing volume: %(volPool)s, "
"Storage pool currently managed by cinder: %(cinderPool)s.",
{'volPool': volumePoolName,
'cinderPool': cinderPoolName})
if volumePoolName != cinderPoolName:
exceptionMessage = (_(
"Unable to import volume %(deviceId)s to cinder. The external "
"volume is not in the pool managed by current cinder host.")
% {'deviceId': deviceId})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
self.utils.check_volume_not_replication_source(
self.conn, storageSystemName, deviceId)
# Rename the volume
volumeId = volume['id']
self.utils.check_is_volume_in_cinder_managed_pool(
self.conn, volumeInstanceName, cinderPoolInstanceName,
deviceId)
volumeId = volume.name
volumeElementName = self.utils.get_volume_element_name(volumeId)
LOG.debug("Rename volume %(vol)s to %(elementName)s.",
LOG.debug("Rename volume %(vol)s to %(volumeId)s.",
{'vol': volumeInstanceName,
'elementName': volumeElementName})
'volumeId': volumeElementName})
volumeInstance = self.utils.rename_volume(self.conn,
volumeInstanceName,
volumeElementName)
@ -4724,25 +4673,44 @@ class VMAXCommon(object):
keys['DeviceID'] = volpath['DeviceID']
keys['SystemCreationClassName'] = volpath['SystemCreationClassName']
model_update = {}
provider_location = {}
provider_location['classname'] = volpath['CreationClassName']
provider_location['keybindings'] = keys
# set-up volume replication, if enabled
model_update = self.set_volume_replication_if_enabled(
self.conn, extraSpecs, volume, provider_location)
volumeDisplayName = volume.display_name
model_update.update(
{'display_name': volumeDisplayName})
model_update.update(
{'provider_location': six.text_type(provider_location)})
return model_update
def set_volume_replication_if_enabled(self, conn, extraSpecs,
volume, provider_location):
"""Set volume replication if enabled
If volume replication is enabled, set relevant
values in associated model_update dict.
:param conn: connection to the ecom server
:param extraSpecs: additional info
:param volume: the volume object
:param provider_location: volume classname & keybindings
:return: updated model_update
"""
model_update = {}
if self.utils.is_replication_enabled(extraSpecs):
replication_status, replication_driver_data = (
self.setup_volume_replication(
self.conn, volume, provider_location, extraSpecs))
conn, volume, provider_location, extraSpecs))
model_update.update(
{'replication_status': replication_status})
model_update.update(
{'replication_driver_data': six.text_type(
replication_driver_data)})
model_update.update({'display_name': volumeElementName})
model_update.update(
{'provider_location': six.text_type(provider_location)})
return model_update
def manage_existing_get_size(self, volume, external_ref):
@ -4758,15 +4726,25 @@ class VMAXCommon(object):
arrayName, deviceId = self.utils.get_array_and_device_id(volume,
external_ref)
volumeInstanceName = (
self.utils.find_volume_by_device_id_on_array(self.conn,
arrayName, deviceId))
volumeInstance = self.conn.GetInstance(volumeInstanceName)
byteSize = self.utils.get_volume_size(self.conn, volumeInstance)
gbSize = int(math.ceil(float(byteSize) / units.Gi))
self.utils.find_volume_by_device_id_on_array(arrayName, deviceId))
try:
volumeInstance = self.conn.GetInstance(volumeInstanceName)
byteSize = self.utils.get_volume_size(self.conn, volumeInstance)
fByteSize = float(byteSize)
gbSize = int(fByteSize / units.Gi)
except Exception:
exceptionMessage = (_("Volume %(deviceID)s not found.")
% {'deviceID': deviceId})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(data=exceptionMessage)
LOG.debug(
"Size of volume %(deviceID)s is %(volumeSize)s GB.",
"Size of volume %(deviceID)s is %(volumeSize)s GB",
{'deviceID': deviceId,
'volumeSize': gbSize})
return gbSize
def unmanage(self, volume):
@ -4793,9 +4771,7 @@ class VMAXCommon(object):
raise exception.VolumeBackendAPIException(data=exceptionMessage)
# Rename the volume to volumeId, thus remove the 'OS-' prefix.
volumeInstance = self.utils.rename_volume(self.conn,
volumeInstance,
volumeId)
self.utils.rename_volume(self.conn, volumeInstance, volumeId)
def update_consistencygroup(self, group, add_volumes,
remove_volumes):

View File

@ -799,6 +799,61 @@ class VMAXUtils(object):
foundPoolInstanceName = foundPoolInstanceNames[0]
return foundPoolInstanceName
def get_assoc_v2_pool_from_volume(self, conn, volumeInstanceName):
"""Give the volume instance get the associated pool instance.
:param conn: connection to the ecom server
:param volumeInstanceName: the volume instance name
:returns: foundPoolInstanceName
"""
foundPoolInstanceName = None
foundPoolInstanceNames = (
conn.AssociatorNames(volumeInstanceName,
ResultClass='EMC_VirtualProvisioningPool'))
if not foundPoolInstanceNames:
deviceID = volumeInstanceName['DeviceID']
LOG.debug("Volume %(deviceId)s not in V2 pool",
{'deviceId': deviceID})
else:
LOG.debug("Retrieved pool: %(foundPoolInstanceNames)s",
{'foundPoolInstanceNames': foundPoolInstanceNames})
if foundPoolInstanceNames and len(foundPoolInstanceNames) > 0:
foundPoolInstanceName = foundPoolInstanceNames[0]
return foundPoolInstanceName
def get_assoc_v3_pool_from_volume(self, conn, volumeInstanceName):
"""Give the volume instance get the associated pool instance.
:param conn: connection to the ecom server
:param volumeInstanceName: the volume instance name
:returns: foundPoolInstanceName
"""
foundPoolInstanceName = None
foundPoolInstanceNames = (
conn.AssociatorNames(volumeInstanceName,
ResultClass='Symm_SRPStoragePool'))
if not foundPoolInstanceNames:
deviceID = volumeInstanceName['DeviceID']
LOG.debug("Volume %(deviceId)s not in V3 SRP",
{'deviceId': deviceID})
exceptionMessage = ("Unable to locate volume %(deviceId)s",
{'deviceId': deviceID})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
else:
LOG.debug("Retrieved pool: %(foundPoolInstanceNames)s",
{'foundPoolInstanceNames': foundPoolInstanceNames})
if foundPoolInstanceNames and len(foundPoolInstanceNames) > 0:
foundPoolInstanceName = foundPoolInstanceNames[0]
return foundPoolInstanceName
def check_if_volume_is_extendable(self, conn, volumeInstance):
"""Checks if a volume is extendable or not.
@ -2156,32 +2211,153 @@ class VMAXUtils(object):
myList.append(kwargs)
return myList
def find_volume_by_device_id_on_array(self, conn, storageSystem, deviceID):
def find_volume_by_device_id_on_array(self, storageSystem, deviceID):
"""Find the volume by device ID on a specific array.
:param conn: connection to the ecom server
:param storageSystem: the storage system name
:param deviceID: string value of the volume device ID
:returns: foundVolumeInstanceName
"""
foundVolumeInstanceName = None
volumeInstanceNames = conn.EnumerateInstanceNames(
'CIM_StorageVolume')
for volumeInstanceName in volumeInstanceNames:
if storageSystem not in volumeInstanceName['SystemName']:
continue
if deviceID == volumeInstanceName['DeviceID']:
foundVolumeInstanceName = volumeInstanceName
LOG.debug("Found volume: %(vol)s",
{'vol': foundVolumeInstanceName})
break
if foundVolumeInstanceName is None:
exceptionMessage = (_("Volume %(deviceID)s not found.")
% {'deviceID': deviceID})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(data=exceptionMessage)
systemName = 'SYMMETRIX-+-%s' % storageSystem
bindings = {'CreationClassName': 'Symm_StorageVolume',
'SystemName': systemName,
'DeviceID': deviceID,
'SystemCreationClassName': 'Symm_StorageSystem'}
return foundVolumeInstanceName
instanceName = pywbem.CIMInstanceName(
classname='Symm_StorageVolume',
namespace=EMC_ROOT,
keybindings=bindings)
LOG.debug("Retrieved volume from VMAX: %(instanceName)s",
{'instanceName': instanceName})
return instanceName
def check_volume_no_fast(self, extraSpecs):
"""Check if the volume's extraSpecs indicate FAST is enabled.
:param extraSpecs: dict -- extra spec dict
:return: True if not fast
:raises: VolumeBackendAPIException
"""
try:
if extraSpecs['storagetype:fastpolicy'] is not None:
LOG.warning(_LW(
"FAST is enabled. Policy: %(fastPolicyName)s."),
{'fastPolicyName': extraSpecs['storagetype:fastpolicy']})
exceptionMessage = (_(
"Manage volume is not supported if FAST is enabled. "
"FAST policy: %(fastPolicyName)s."
) % {'fastPolicyName': extraSpecs[
'storagetype:fastpolicy']})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
else:
return True
except KeyError:
return True
def check_volume_not_in_masking_view(self, conn, volumeInstanceName,
deviceId):
"""Check if volume is in Masking View.
:param conn: connection to the ecom server
:param volumeInstanceName: the volume instance name
:param deviceId: string value of the volume device ID
:raises: VolumeBackendAPIException
:return: True if not in Masking View
"""
sgInstanceNames = (
self.get_storage_groups_from_volume(
conn, volumeInstanceName))
mvInstanceName = None
for sgInstanceName in sgInstanceNames:
maskingViews = conn.AssociatorNames(
sgInstanceName,
ResultClass='Symm_LunMaskingView')
if len(maskingViews) > 0:
mvInstanceName = maskingViews[0]
if mvInstanceName:
exceptionMessage = (_(
"Unable to import volume %(deviceId)s to cinder. "
"Volume is in masking view %(mv)s.")
% {'deviceId': deviceId, 'mv': mvInstanceName})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
if not mvInstanceName:
return True
def check_volume_not_replication_source(self, conn, storageSystemName,
deviceId):
"""Check volume not replication source.
Check if the volume is the source of a replicated
volume.
:param conn: connection to the ecom server
:param storageSystemName: the storage system name
:param deviceId: string value of the volume device ID
:raises: VolumeBackendAPIException
:returns: True if not replication source
"""
repSessionInstanceName = (
self.get_associated_replication_from_source_volume(
conn, storageSystemName, deviceId))
if repSessionInstanceName:
exceptionMessage = (_(
"Unable to import volume %(deviceId)s to cinder. "
"It is the source volume of replication session %(sync)s.")
% {'deviceId': deviceId, 'sync': repSessionInstanceName})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
else:
return True
def check_is_volume_in_cinder_managed_pool(
self, conn, volumeInstanceName, cinderPoolInstanceName,
deviceId):
"""Check if volume is in a Cinder managed pool.
:param conn: connection to the ecom server
:param volumeInstanceName: the volume instance name
:param cinderPoolInstanceName: the name of the storage pool
:param deviceId: string value of the volume device ID
:raises: VolumeBackendAPIException
:returns: True if volume in cinder managed pool
"""
volumePoolInstanceName = (
self.get_assoc_v2_pool_from_volume(conn,
volumeInstanceName))
if not volumePoolInstanceName:
volumePoolInstanceName = (
self.get_assoc_v3_pool_from_volume(conn,
volumeInstanceName))
volumePoolName = volumePoolInstanceName['InstanceID']
cinderPoolName = cinderPoolInstanceName['InstanceID']
LOG.debug("Storage pool of existing volume: %(volPool)s, "
"Storage pool currently managed by cinder: %(cinderPool)s.",
{'volPool': volumePoolName,
'cinderPool': cinderPoolName})
if volumePoolName != cinderPoolName:
exceptionMessage = (_(
"Unable to import volume %(deviceId)s to cinder. The external "
"volume is not in the pool managed by current cinder host.")
% {'deviceId': deviceId})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
else:
return True
def get_volume_element_name(self, volumeId):
"""Get volume element name follows naming convention, i.e. 'OS-UUID'.
@ -2214,6 +2390,7 @@ class VMAXUtils(object):
"""
if type(volume) is pywbem.cim_obj.CIMInstance:
volumeInstance = volume
volumeInstance['ElementName'] = newName
else:
volumeInstance = conn.GetInstance(volume)
volumeInstance['ElementName'] = newName
@ -2225,30 +2402,32 @@ class VMAXUtils(object):
return volumeInstance
def get_array_and_device_id(self, volume, external_ref):
@staticmethod
def get_array_and_device_id(volume, external_ref):
"""Helper function for manage volume to get array name and device ID.
:param volume: volume object from API
:param external_ref: the existing volume object to be manged
:returns: string value of the array name and device ID
"""
deviceId = external_ref.get(u'source-name', None)
arrayName = ''
for metadata in volume['volume_metadata']:
if metadata['key'].lower() == 'array':
arrayName = metadata['value']
break
device_id = external_ref.get(u'source-name', None)
LOG.debug("External_ref: %(er)s", {'er': external_ref})
if not device_id:
device_id = external_ref.get(u'source-id', None)
host = volume['host']
host_list = host.split('+')
array = host_list[(len(host_list) - 1)]
if deviceId:
if device_id:
LOG.debug("Get device ID of existing volume - device ID: "
"%(deviceId)s, Array: %(arrayName)s.",
{'deviceId': deviceId,
'arrayName': arrayName})
"%(device_id)s, Array: %(array)s.",
{'device_id': device_id,
'array': array})
else:
exception_message = (_("Source volume device ID is required."))
raise exception.VolumeBackendAPIException(
data=exception_message)
return (arrayName, deviceId)
return array, device_id
def get_associated_replication_from_source_volume(
self, conn, storageSystem, sourceDeviceId):