import mock
from oslo_log import log as logging
+from oslo_utils import units
import six
from cinder import exception
subscribedcapacity_bits = '500000000000'
totalmanagedspace_gbs = 931
subscribedcapacity_gbs = 466
-
+ fake_host = 'HostX@Backend#gold+1234567891011'
unit_creationclass = 'CIM_ProtocolControllerForUnit'
storage_type = 'gold'
keybindings = {'CreationClassName': u'Symm_StorageVolume',
result = self._enum_storagevolumes()
elif name == 'Symm_StorageVolume':
result = self._enum_storagevolumes()
+ elif name == 'CIM_StorageVolume':
+ result = self._enum_storagevolumes()
elif name == 'CIM_ProtocolControllerForUnit':
result = self._enum_unitnames()
elif name == 'EMC_LunMaskingSCSIProtocolController':
result = self._enum_policyrules()
elif name == 'CIM_ReplicationServiceCapabilities':
result = self._enum_repservcpbls()
+ elif name == 'SE_StorageSynchronized_SV_SV':
+ result = self._enum_storageSyncSvSv()
else:
result = self._default_enum()
return result
return result
+ def ModifyInstance(self, objectpath, PropertyList=None):
+ pass
+
def DeleteInstance(self, objectpath):
pass
def _enum_metavolume(self):
return []
+ def _enum_storageSyncSvSv(self):
+ conn = FakeEcomConnection()
+ sourceVolume = {}
+ sourceVolume['CreationClassName'] = 'Symm_StorageVolume'
+ sourceVolume['DeviceID'] = self.data.test_volume['device_id']
+ sourceInstanceName = conn.GetInstance(sourceVolume)
+ svInstances = []
+ svInstance = {}
+ svInstance['SyncedElement'] = 'SyncedElement'
+ svInstance['SystemElement'] = sourceInstanceName
+ svInstance['CreationClassName'] = 'SE_StorageSynchronized_SV_SV'
+ svInstance['PercentSynced'] = 100
+ svInstances.append(svInstance)
+ return svInstances
+
def _default_enum(self):
names = []
name = {}
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(volumeInstanceName, expect)
+
+ def test_get_volume_element_name(self):
+ volumeId = 'ea95aa39-080b-4f11-9856-a03acf9112ad'
+ utils = self.driver.common.utils
+ volumeElementName = utils.get_volume_element_name(volumeId)
+ expectVolumeElementName = (
+ emc_vmax_utils.VOLUME_ELEMENT_NAME_PREFIX + volumeId)
+ self.assertEqual(volumeElementName, expectVolumeElementName)
+
+ def test_get_associated_replication_from_source_volume(self):
+ conn = self.fake_ecom_connection()
+ utils = self.driver.common.utils
+ repInstanceName = (
+ utils.get_associated_replication_from_source_volume(
+ conn, self.data.storage_system,
+ self.data.test_volume['device_id']))
+ expectInstanceName = (
+ conn.EnumerateInstanceNames('SE_StorageSynchronized_SV_SV')[0])
+ self.assertEqual(repInstanceName, expectInstanceName)
+
+ def test_get_array_and_device_id_success(self):
+ deviceId = '0123'
+ arrayId = u'array1234'
+ external_ref = {u'source-name': deviceId}
+ volume = {'volume_metadata': [{'key': 'array', 'value': arrayId}]
+ }
+ utils = self.driver.common.utils
+ (arrId, devId) = utils.get_array_and_device_id(volume, external_ref)
+ self.assertEqual(arrId, arrayId)
+ self.assertEqual(devId, deviceId)
+
+ def test_get_array_and_device_id_failed(self):
+ deviceId = '0123'
+ arrayId = u'array1234'
+ external_ref = {u'no-source-name': deviceId}
+ volume = {'volume_metadata': [{'key': 'array', 'value': arrayId}]
+ }
+ utils = self.driver.common.utils
+ self.assertRaises(exception.VolumeBackendAPIException,
+ utils.get_array_and_device_id,
+ volume,
+ external_ref)
+
+ def test_rename_volume(self):
+ conn = self.fake_ecom_connection()
+ utils = self.driver.common.utils
+ newName = 'new_name'
+ volume = {}
+ volume['CreationClassName'] = 'Symm_StorageVolume'
+ volume['DeviceID'] = '1'
+ volume['ElementName'] = 'original_name'
+ pywbem = mock.Mock()
+ pywbem.cim_obj = mock.Mock()
+ pywbem.cim_obj.CIMInstance = mock.Mock()
+ emc_vmax_utils.pywbem = pywbem
+ volumeInstance = conn.GetInstance(volume)
+ originalName = volumeInstance['ElementName']
+ volumeInstance = utils.rename_volume(conn, volumeInstance, newName)
+ self.assertEqual(newName, volumeInstance['ElementName'])
+ volumeInstance = utils.rename_volume(
+ conn, volumeInstance, originalName)
+ self.assertEqual(originalName, volumeInstance['ElementName'])
+
def _cleanup(self):
if self.config_file_path:
bExists = os.path.exists(self.config_file_path)
if bExists:
os.remove(self.config_file_parse_port_group)
+ def test_manage_existing_get_size(self):
+ volume = {}
+ metadata = {'key': 'array',
+ 'value': '12345'}
+ volume['volume_metadata'] = [metadata]
+ external_ref = {'source-name': '0123'}
+ utils = self.driver.common.utils
+ gbSize = 2
+ utils.get_volume_size = mock.Mock(
+ return_value=gbSize * 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(gbSize, 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"
+ 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['name']
+ 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=None)
+ 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',
+ 'DeviceID': u'1',
+ 'SystemCreationClassName': u'Symm_StorageSystem'}
+ provider_location = {'classname': 'Symm_StorageVolume',
+ 'keybindings': keybindings}
+
+ volume = {'name': 'vol1',
+ 'size': 1,
+ 'id': '1',
+ 'device_id': '1',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'vol1',
+ 'display_description': 'test volume',
+ 'volume_type_id': 'abc',
+ 'provider_location': six.text_type(provider_location),
+ 'status': 'available',
+ 'host': self.data.fake_host,
+ 'NumberOfBlocks': 100,
+ 'BlockSize': 512
+ }
+ common = self.driver.common
+ common._initial_setup = mock.Mock(
+ return_value={'volume_backend_name': 'FCNoFAST',
+ 'storagetype:fastpolicy': None})
+ utils = self.driver.common.utils
+ utils.rename_volume = mock.Mock(return_value=None)
+ self.driver.unmanage(volume)
+ utils.rename_volume.assert_called_once_with(
+ common.conn, common._find_lun(volume), '1')
+
+ def test_unmanage_no_fast_failed(self):
+ keybindings = {'CreationClassName': u'Symm_StorageVolume',
+ 'SystemName': u'SYMMETRIX+000195900000',
+ 'DeviceID': u'999',
+ 'SystemCreationClassName': u'Symm_StorageSystem'}
+ provider_location = {'classname': 'Symm_StorageVolume',
+ 'keybindings': keybindings}
+
+ volume = {'name': 'NO_SUCH_VOLUME',
+ 'size': 1,
+ 'id': '999',
+ 'device_id': '999',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'No such volume',
+ 'display_description': 'volume not on the array',
+ 'volume_type_id': 'abc',
+ 'provider_location': six.text_type(provider_location),
+ 'status': 'available',
+ 'host': self.data.fake_host,
+ 'NumberOfBlocks': 100,
+ 'BlockSize': 512
+ }
+ common = self.driver.common
+ common._initial_setup = mock.Mock(
+ return_value={'volume_backend_name': 'FCNoFAST',
+ 'fastpolicy': None})
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.unmanage,
+ volume)
+
def _cleanup(self):
bExists = os.path.exists(self.config_file_path)
if bExists:
self.data.test_volume,
EMCVMAXCommonData.test_source_volume)
+ def test_manage_existing_fast_failed(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"
+ external_ref = {'source-name': '0123'}
+ common = self.driver.common
+ common._initial_setup = mock.Mock(
+ return_value={'volume_backend_name': 'FCFAST',
+ 'storagetype:fastpolicy': 'GOLD'})
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.manage_existing,
+ volume,
+ external_ref)
+
def _cleanup(self):
bExists = os.path.exists(self.config_file_path)
if bExists:
common._create_v3_volume = (
mock.Mock(return_value=(0L, volumeDict, self.data.storage_system)))
conn = self.fake_ecom_connection()
- storageConfigService = []
storageConfigService = {}
storageConfigService['SystemName'] = EMCVMAXCommonData.storage_system
storageConfigService['CreationClassName'] = \
from oslo_config import cfg
from oslo_log import log as logging
+from oslo_utils import units
+
import six
from cinder import exception
extraSpecs[FASTPOLICY],
extraSpecs)
return rc
+
+ def manage_existing(self, volume, external_ref):
+ """Manages an existing VMAX Volume (import to Cinder).
+
+ Renames the existing volume to match the expected name for the volume.
+ Also need to consider things like QoS, Emulation, account/tenant.
+
+ :param volume: the volume object including the volume_type_id
+ :param external_ref: reference to the existing volume
+ :returns: dict -- model_update
+ :raises: VolumeBackendAPIException
+ """
+ extraSpecs = self._initial_setup(volume)
+ self.conn = self._get_ecom_connection()
+ arrayName, deviceId = self.utils.get_array_and_device_id(volume,
+ external_ref)
+
+ # 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))
+
+ for sgInstanceName in sgInstanceNames:
+ mvInstanceName = self.masking.get_masking_view_from_storage_group(
+ self.conn, sgInstanceName)
+ 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)
+
+ # 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)
+
+ # Rename the volume
+ volumeId = volume['name']
+ volumeElementName = self.utils.get_volume_element_name(volumeId)
+ LOG.debug("Rename volume %(vol)s to %(elementName)s.",
+ {'vol': volumeInstanceName,
+ 'elementName': volumeElementName})
+ volumeInstance = self.utils.rename_volume(self.conn,
+ volumeInstanceName,
+ volumeElementName)
+ keys = {}
+ volpath = volumeInstance.path
+ keys['CreationClassName'] = volpath['CreationClassName']
+ keys['SystemName'] = volpath['SystemName']
+ keys['DeviceID'] = volpath['DeviceID']
+ keys['SystemCreationClassName'] = volpath['SystemCreationClassName']
+
+ model_update = {}
+ provider_location = {}
+ provider_location['classname'] = volpath['CreationClassName']
+ provider_location['keybindings'] = keys
+
+ model_update.update({'display_name': volumeElementName})
+ volume['provider_location'] = six.text_type(provider_location)
+ model_update.update({'provider_location': volume['provider_location']})
+ return model_update
+
+ def manage_existing_get_size(self, volume, external_ref):
+ """Return size of an existing VMAX volume to manage_existing.
+
+ :param self: reference to class
+ :param volume: the volume object including the volume_type_id
+ :param external_ref: reference to the existing volume
+ :returns: size of the volume in GB
+ """
+ LOG.debug("Volume in manage_existing_get_size: %(volume)s.",
+ {'volume': volume})
+ 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(byteSize) / units.Gi
+ LOG.debug(
+ "Size of volume %(deviceID)s is %(volumeSize)s GB.",
+ {'deviceID': deviceId,
+ 'volumeSize': gbSize})
+ return gbSize
+
+ def unmanage(self, volume):
+ """Export VMAX volume from Cinder.
+
+ Leave the volume intact on the backend array.
+
+ :param volume: the volume object
+ :raises: VolumeBackendAPIException
+ """
+ volumeName = volume['name']
+ volumeId = volume['id']
+ LOG.debug("Unmanage volume %(name)s, id=%(id)s",
+ {'name': volumeName,
+ 'id': volumeId})
+ self._initial_setup(volume)
+ self.conn = self._get_ecom_connection()
+ volumeInstance = self._find_lun(volume)
+ if volumeInstance is None:
+ exceptionMessage = (_("Cannot find Volume: %(id)s. "
+ "unmanage operation. Exiting...")
+ % {'id': volumeId})
+ LOG.error(exceptionMessage)
+ raise exception.VolumeBackendAPIException(data=exceptionMessage)
+
+ # Rename the volume to volumeId, thus remove the 'OS-' prefix.
+ volumeInstance = self.utils.rename_volume(self.conn,
+ volumeInstance,
+ volumeId)
2.1.1 - Fixed issue with mismatched config (bug #1442376)
2.1.2 - Clean up failed clones (bug #1440154)
2.1.3 - Fixed a problem with FAST support (bug #1435069)
+ 2.2.0 - Add manage/unmanage
"""
- VERSION = "2.1.3"
+ VERSION = "2.2.0"
def __init__(self, *args, **kwargs):
def delete_cgsnapshot(self, context, cgsnapshot):
"""Deletes a cgsnapshot."""
return self.common.delete_cgsnapshot(context, cgsnapshot, self.db)
+
+ def manage_existing(self, volume, external_ref):
+ """Manages an existing VMAX Volume (import to Cinder).
+
+ Renames the Volume to match the expected name for the volume.
+ Also need to consider things like QoS, Emulation, account/tenant.
+ """
+ return self.common.manage_existing(volume, external_ref)
+
+ def manage_existing_get_size(self, volume, external_ref):
+ """Return size of an existing VMAX volume to manage_existing.
+
+ :param self: reference to class
+ :param volume: the volume object including the volume_type_id
+ :param external_ref: reference to the existing volume
+ :returns: size of the volume in GB
+ """
+ return self.common.manage_existing_get_size(volume, external_ref)
+
+ def unmanage(self, volume):
+ """Export VMAX volume from Cinder.
+
+ Leave the volume intact on the backend array.
+ """
+ return self.common.unmanage(volume)
2.1.1 - Fixed issue with mismatched config (bug #1442376)
2.1.2 - Clean up failed clones (bug #1440154)
2.1.3 - Fixed a problem with FAST support (bug #1435069)
+ 2.2.0 - Add manage/unmanage
"""
- VERSION = "2.1.3"
+ VERSION = "2.2.0"
def __init__(self, *args, **kwargs):
if 'iscsi_ip_address' in open(CINDER_CONF).read():
return True
return False
+
+ def manage_existing(self, volume, external_ref):
+ """Manages an existing VMAX Volume (import to Cinder).
+
+ Renames the Volume to match the expected name for the volume.
+ Also need to consider things like QoS, Emulation, account/tenant.
+ """
+ return self.common.manage_existing(volume, external_ref)
+
+ def manage_existing_get_size(self, volume, external_ref):
+ """Return size of an existing VMAX volume to manage_existing.
+
+ :param self: reference to class
+ :param volume: the volume object including the volume_type_id
+ :param external_ref: reference to the existing volume
+ :returns: size of the volume in GB
+ """
+ return self.common.manage_existing_get_size(volume, external_ref)
+
+ def unmanage(self, volume):
+ """Export VMAX volume from Cinder, leave the volume intact on the
+ backend array.
+ """
+ return self.common.unmanage(volume)
INTERVAL = 'storagetype:interval'
RETRIES = 'storagetype:retries'
CIM_ERR_NOT_FOUND = 6
+VOLUME_ELEMENT_NAME_PREFIX = 'OS-'
class EMCVMAXUtils(object):
if hardwareTypeId == 0:
LOG.warning(_LW("Cannot determine the hardware type."))
return hardwareTypeId
+
+ def find_volume_by_device_id_on_array(self, conn, 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)
+
+ return foundVolumeInstanceName
+
+ def get_volume_element_name(self, volumeId):
+ """Get volume element name follows naming convention, i.e. 'OS-UUID'.
+
+ :param volumeId: volume id containing uuid
+ :returns: volume element name in format of OS-UUID
+ """
+ elementName = volumeId
+ uuid_regex = (re.compile(
+ '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}',
+ re.I))
+ match = uuid_regex.search(volumeId)
+ if match:
+ volumeUUID = match.group()
+ elementName = ("%(prefix)s%(volumeUUID)s"
+ % {'prefix': VOLUME_ELEMENT_NAME_PREFIX,
+ 'volumeUUID': volumeUUID})
+ LOG.debug(
+ "get_volume_element_name elementName: %(elementName)s.",
+ {'elementName': elementName})
+ return elementName
+
+ def rename_volume(self, conn, volume, newName):
+ """Change the volume ElementName to specified new name.
+
+ :param conn: connection to the ecom server
+ :param volume: the volume instance name or volume instance
+ :param newName: new ElementName of the volume
+ :returns: volumeInstance after rename
+ """
+ if type(volume) is pywbem.cim_obj.CIMInstance:
+ volumeInstance = volume
+ else:
+ volumeInstance = conn.GetInstance(volume)
+ volumeInstance['ElementName'] = newName
+
+ LOG.debug("Rename volume to new ElementName %(newName)s.",
+ {'newName': newName})
+
+ conn.ModifyInstance(volumeInstance, PropertyList=['ElementName'])
+
+ return volumeInstance
+
+ def get_array_and_device_id(self, 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
+
+ if deviceId:
+ LOG.debug("Get device ID of existing volume - device ID: "
+ "%(deviceId)s, Array: %(arrayName)s.",
+ {'deviceId': deviceId,
+ 'arrayName': arrayName})
+ else:
+ exception_message = (_("Source volume device ID is required."))
+ raise exception.VolumeBackendAPIException(
+ data=exception_message)
+ return (arrayName, deviceId)
+
+ def get_associated_replication_from_source_volume(
+ self, conn, storageSystem, sourceDeviceId):
+ """Given the source volume device ID, find associated replication
+ storage synchronized instance names.
+
+ :param conn: connection to the ecom server
+ :param storageSystem: the storage system name
+ :param source: target volume object
+ :returns: foundSyncName (String)
+ """
+ foundSyncInstanceName = None
+ syncInstanceNames = conn.EnumerateInstanceNames(
+ 'SE_StorageSynchronized_SV_SV')
+ for syncInstanceName in syncInstanceNames:
+ sourceVolume = syncInstanceName['SystemElement']
+ if storageSystem != sourceVolume['SystemName']:
+ continue
+ if sourceVolume['DeviceID'] == sourceDeviceId:
+ # Check that it hasn't recently been deleted.
+ try:
+ conn.GetInstance(syncInstanceName)
+ foundSyncInstanceName = syncInstanceName
+ LOG.debug("Found sync Name: "
+ "%(syncName)s.",
+ {'syncName': foundSyncInstanceName})
+ except Exception:
+ foundSyncInstanceName = None
+ break
+
+ if foundSyncInstanceName is None:
+ LOG.info(_LI(
+ "No replication synchronization session found associated "
+ "with source volume %(source)s on %(storageSystem)s."),
+ {'source': sourceDeviceId, 'storageSystem': storageSystem})
+
+ return foundSyncInstanceName