# License for the specific language governing permissions and limitations
# under the License.
+import os
+from xml.dom.minidom import Document
+
+from cinder import exception
+from cinder import flags
from cinder.openstack.common import log as logging
from cinder import test
from cinder.volume.drivers.emc.emc_smis_common import EMCSMISCommon
from cinder.volume.drivers.emc.emc_smis_iscsi import EMCSMISISCSIDriver
+FLAGS = flags.FLAGS
+
LOG = logging.getLogger(__name__)
+config_file_name = 'cinder_emc_config.xml'
storage_system = 'CLARiiON+APM00123456789'
+storage_system_vmax = 'SYMMETRIX+000195900551'
lunmaskctrl_id = 'CLARiiON+APM00123456789+00aa11bb22cc33dd44ff55gg66hh77ii88jj'
initiator1 = 'iqn.1993-08.org.debian:01:1a2b3c4d5f6g'
stconf_service_creationclass = 'Clar_StorageConfigurationService'
'display_name': 'vol1',
'display_description': 'test volume',
'volume_type_id': None}
+test_failed_volume = {'name': 'failed_vol',
+ 'size': 1,
+ 'volume_name': 'failed_vol',
+ 'id': '4',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'failed_vol',
+ 'display_description': 'test failed volume',
+ 'volume_type_id': None}
test_snapshot = {'name': 'snapshot1',
'size': 1,
'id': '4444',
'display_name': 'clone3',
'display_description': 'cloned volume',
'volume_type_id': None}
+test_snapshot_vmax = {'name': 'snapshot_vmax',
+ 'size': 1,
+ 'id': '4445',
+ 'volume_name': 'vol1',
+ 'volume_size': 1,
+ 'project_id': 'project'}
+failed_snapshot_replica = {'name': 'failed_snapshot_replica',
+ 'size': 1,
+ 'volume_name': 'vol1',
+ 'id': '5',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'vol1',
+ 'display_description': 'failed snapshot replica',
+ 'volume_type_id': None}
+failed_snapshot_sync = {'name': 'failed_snapshot_sync',
+ 'size': 1,
+ 'volume_name': 'vol1',
+ 'id': '6',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'failed_snapshot_sync',
+ 'display_description': 'failed snapshot sync',
+ 'volume_type_id': None}
+failed_clone_replica = {'name': 'failed_clone_replica',
+ 'size': 1,
+ 'volume_name': 'vol1',
+ 'id': '7',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'vol1',
+ 'display_description': 'failed clone replica',
+ 'volume_type_id': None}
+failed_clone_sync = {'name': 'failed_clone_sync',
+ 'size': 1,
+ 'volume_name': 'vol1',
+ 'id': '8',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'vol1',
+ 'display_description': 'failed clone sync',
+ 'volume_type_id': None}
+failed_delete_vol = {'name': 'failed_delete_vol',
+ 'size': 1,
+ 'volume_name': 'failed_delete_vol',
+ 'id': '99999',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'failed delete vol',
+ 'display_description': 'failed delete volume',
+ 'volume_type_id': None}
class EMC_StorageVolume(dict):
pass
+class SE_ConcreteJob(dict):
+ pass
+
+
class FakeEcomConnection():
def InvokeMethod(self, MethodName, Service, ElementName=None, InPool=None,
LUNames=None, InitiatorPortIDs=None, DeviceAccesses=None,
ProtocolControllers=None,
MaskingGroup=None, Members=None):
+
rc = 0L
- job = {'status': 'success'}
+ myjob = SE_ConcreteJob()
+ myjob.classname = 'SE_ConcreteJob'
+ myjob['InstanceID'] = '9999'
+ myjob['status'] = 'success'
+ if ElementName == 'failed_vol' and \
+ MethodName == 'CreateOrModifyElementFromStoragePool':
+ rc = 10L
+ myjob['status'] = 'failure'
+ elif ElementName == 'failed_snapshot_replica' and \
+ MethodName == 'CreateElementReplica':
+ rc = 10L
+ myjob['status'] = 'failure'
+ elif Synchronization and \
+ Synchronization['SyncedElement']['ElementName'] \
+ == 'failed_snapshot_sync' and \
+ MethodName == 'ModifyReplicaSynchronization':
+ rc = 10L
+ myjob['status'] = 'failure'
+ elif ElementName == 'failed_clone_replica' and \
+ MethodName == 'CreateElementReplica':
+ rc = 10L
+ myjob['status'] = 'failure'
+ elif Synchronization and \
+ Synchronization['SyncedElement']['ElementName'] \
+ == 'failed_clone_sync' and \
+ MethodName == 'ModifyReplicaSynchronization':
+ rc = 10L
+ myjob['status'] = 'failure'
+ elif TheElements and \
+ TheElements[0]['DeviceID'] == '99999' and \
+ MethodName == 'EMCReturnToStoragePool':
+ rc = 10L
+ myjob['status'] = 'failure'
+
+ job = {'Job': myjob}
return rc, job
def EnumerateInstanceNames(self, name):
result = self._default_enum()
return result
+ def EnumerateInstances(self, name):
+ result = None
+ if name == 'EMC_VirtualProvisioningPool':
+ result = self._enum_pool_details()
+ elif name == 'EMC_UnifiedStoragePool':
+ result = self._enum_pool_details()
+ else:
+ result = self._default_enum()
+ return result
+
def GetInstance(self, objectpath, LocalOnly=False):
- name = objectpath['CreationClassName']
+ try:
+ name = objectpath['CreationClassName']
+ except KeyError:
+ name = objectpath.classname
result = None
if name == 'Clar_StorageVolume':
result = self._getinstance_storagevolume(objectpath)
result = self._getinstance_unit(objectpath)
elif name == 'Clar_LunMaskingSCSIProtocolController':
result = self._getinstance_lunmask()
+ elif name == 'SE_ConcreteJob':
+ result = self._getinstance_job(objectpath)
else:
result = self._default_getinstance(objectpath)
return result
return unit
+ def _getinstance_job(self, jobpath):
+ jobinstance = {}
+ jobinstance['InstanceID'] = '9999'
+ if jobpath['status'] == 'failure':
+ jobinstance['JobState'] = 10
+ jobinstance['ErrorCode'] = 99
+ jobinstance['ErrorDescription'] = 'Failure'
+ else:
+ jobinstance['JobState'] = 7
+ jobinstance['ErrorCode'] = 0
+ jobinstance['ErrorDescription'] = ''
+ return jobinstance
+
def _default_getinstance(self, objectpath):
return objectpath
pools.append(pool)
return pools
+ def _enum_pool_details(self):
+ pools = []
+ pool = {}
+ pool['InstanceID'] = storage_system + '+U+' + storage_type
+ pool['CreationClassName'] = 'Clar_UnifiedStoragePool'
+ pool['TotalManagedSpace'] = 12345678
+ pool['RemainingManagedSpace'] = 123456
+ pools.append(pool)
+ return pools
+
def _enum_storagevolumes(self):
vols = []
vol = EMC_StorageVolume()
clone_vol3.path = {'DeviceID': clone_vol3['DeviceID']}
vols.append(clone_vol3)
+ snap_vol_vmax = EMC_StorageVolume()
+ snap_vol_vmax['CreationClassName'] = 'Symm_StorageVolume'
+ snap_vol_vmax['ElementName'] = test_snapshot_vmax['name']
+ snap_vol_vmax['DeviceID'] = test_snapshot_vmax['id']
+ snap_vol_vmax['SystemName'] = storage_system_vmax
+ snap_vol_vmax.path = {'DeviceID': snap_vol_vmax['DeviceID']}
+ vols.append(snap_vol_vmax)
+
+ failed_snap_replica = EMC_StorageVolume()
+ failed_snap_replica['CreationClassName'] = 'Clar_StorageVolume'
+ failed_snap_replica['ElementName'] = failed_snapshot_replica['name']
+ failed_snap_replica['DeviceID'] = failed_snapshot_replica['id']
+ failed_snap_replica['SystemName'] = storage_system
+ failed_snap_replica.path = {
+ 'DeviceID': failed_snap_replica['DeviceID']}
+ vols.append(failed_snap_replica)
+
+ failed_snap_sync = EMC_StorageVolume()
+ failed_snap_sync['CreationClassName'] = 'Clar_StorageVolume'
+ failed_snap_sync['ElementName'] = failed_snapshot_sync['name']
+ failed_snap_sync['DeviceID'] = failed_snapshot_sync['id']
+ failed_snap_sync['SystemName'] = storage_system
+ failed_snap_sync.path = {
+ 'DeviceID': failed_snap_sync['DeviceID']}
+ vols.append(failed_snap_sync)
+
+ failed_clone_rep = EMC_StorageVolume()
+ failed_clone_rep['CreationClassName'] = 'Clar_StorageVolume'
+ failed_clone_rep['ElementName'] = failed_clone_replica['name']
+ failed_clone_rep['DeviceID'] = failed_clone_replica['id']
+ failed_clone_rep['SystemName'] = storage_system
+ failed_clone_rep.path = {
+ 'DeviceID': failed_clone_rep['DeviceID']}
+ vols.append(failed_clone_rep)
+
+ failed_clone_s = EMC_StorageVolume()
+ failed_clone_s['CreationClassName'] = 'Clar_StorageVolume'
+ failed_clone_s['ElementName'] = failed_clone_sync['name']
+ failed_clone_s['DeviceID'] = failed_clone_sync['id']
+ failed_clone_s['SystemName'] = storage_system
+ failed_clone_s.path = {
+ 'DeviceID': failed_clone_s['DeviceID']}
+ vols.append(failed_clone_s)
+
+ failed_delete_vol = EMC_StorageVolume()
+ failed_delete_vol['CreationClassName'] = 'Clar_StorageVolume'
+ failed_delete_vol['ElementName'] = 'failed_delete_vol'
+ failed_delete_vol['DeviceID'] = '99999'
+ failed_delete_vol['SystemName'] = storage_system
+ failed_delete_vol.path = {'DeviceID': failed_delete_vol['DeviceID']}
+ vols.append(failed_delete_vol)
+
return vols
def _enum_syncsvsvs(self):
sync3['CreationClassName'] = 'SE_StorageSynchronized_SV_SV'
syncs.append(sync3)
+ objpath1 = vols[1]
+ for vol in vols:
+ if vol['ElementName'] == 'failed_snapshot_sync':
+ objpath2 = vol
+ break
+ sync4 = {}
+ sync4['SyncedElement'] = objpath2
+ sync4['SystemElement'] = objpath1
+ sync4['CreationClassName'] = 'SE_StorageSynchronized_SV_SV'
+ syncs.append(sync4)
+
+ objpath1 = vols[0]
+ for vol in vols:
+ if vol['ElementName'] == 'failed_clone_sync':
+ objpath2 = vol
+ break
+ sync5 = {}
+ sync5['SyncedElement'] = objpath2
+ sync5['SystemElement'] = objpath1
+ sync5['CreationClassName'] = 'SE_StorageSynchronized_SV_SV'
+ syncs.append(sync5)
+
return syncs
def _enum_unitnames(self):
def setUp(self):
super(EMCSMISISCSIDriverTestCase, self).setUp()
- driver = EMCSMISISCSIDriver()
- self.driver = driver
+ self.config_file_path = None
+ self.create_fake_config_file()
+ FLAGS.cinder_emc_config_file = self.config_file_path
self.stubs.Set(EMCSMISISCSIDriver, '_get_iscsi_properties',
self.fake_get_iscsi_properties)
self.stubs.Set(EMCSMISCommon, '_get_ecom_connection',
self.fake_ecom_connection)
- self.stubs.Set(EMCSMISCommon, '_get_storage_type',
- self.fake_storage_type)
+ driver = EMCSMISISCSIDriver()
+ self.driver = driver
+
+ def create_fake_config_file(self):
+ doc = Document()
+ emc = doc.createElement("EMC")
+ doc.appendChild(emc)
+
+ storagetype = doc.createElement("StorageType")
+ storagetypetext = doc.createTextNode("gold")
+ emc.appendChild(storagetype)
+ storagetype.appendChild(storagetypetext)
+
+ ecomserverip = doc.createElement("EcomServerIp")
+ ecomserveriptext = doc.createTextNode("1.1.1.1")
+ emc.appendChild(ecomserverip)
+ ecomserverip.appendChild(ecomserveriptext)
+
+ ecomserverport = doc.createElement("EcomServerPort")
+ ecomserverporttext = doc.createTextNode("10")
+ emc.appendChild(ecomserverport)
+ ecomserverport.appendChild(ecomserverporttext)
+
+ ecomusername = doc.createElement("EcomUserName")
+ ecomusernametext = doc.createTextNode("user")
+ emc.appendChild(ecomusername)
+ ecomusername.appendChild(ecomusernametext)
+
+ ecompassword = doc.createElement("EcomPassword")
+ ecompasswordtext = doc.createTextNode("pass")
+ emc.appendChild(ecompassword)
+ ecompassword.appendChild(ecompasswordtext)
+
+ dir_path = os.getcwd()
+ self.config_file_path = dir_path + '/' + config_file_name
+ f = open(self.config_file_path, 'w')
+ doc.writexml(f)
+ f.close()
def fake_ecom_connection(self):
conn = FakeEcomConnection()
LOG.info(_("Fake ISCSI properties: %s") % (properties))
return properties
- def fake_storage_type(self, filename=None):
- return storage_type
+ def test_get_volume_stats(self):
+ self.driver.get_volume_stats(True)
def test_create_destroy(self):
self.driver.create_volume(test_volume)
self.driver.terminate_connection(test_volume, connector)
self.driver.remove_export(None, test_volume)
self.driver.delete_volume(test_volume)
+
+ def test_create_volume_failed(self):
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_volume,
+ test_failed_volume)
+
+ def test_create_volume_snapshot_unsupported(self):
+ self.driver.create_volume(test_volume)
+ self.driver.create_snapshot(test_snapshot_vmax)
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_volume_from_snapshot,
+ test_clone,
+ test_snapshot_vmax)
+ self.driver.delete_snapshot(test_snapshot_vmax)
+ self.driver.delete_volume(test_volume)
+
+ def test_create_volume_snapshot_replica_failed(self):
+ self.driver.create_volume(test_volume)
+ self.driver.create_snapshot(test_snapshot)
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_volume_from_snapshot,
+ failed_snapshot_replica,
+ test_snapshot)
+ self.driver.delete_snapshot(test_snapshot)
+ self.driver.delete_volume(test_volume)
+
+ def test_create_volume_snapshot_sync_failed(self):
+ self.driver.create_volume(test_volume)
+ self.driver.create_snapshot(test_snapshot)
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_volume_from_snapshot,
+ failed_snapshot_sync,
+ test_snapshot)
+ self.driver.delete_snapshot(test_snapshot)
+ self.driver.delete_volume(test_volume)
+
+ def test_create_volume_clone_replica_failed(self):
+ self.driver.create_volume(test_volume)
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_cloned_volume,
+ failed_clone_replica,
+ test_volume)
+ self.driver.delete_volume(test_volume)
+
+ def test_create_volume_clone_sync_failed(self):
+ self.driver.create_volume(test_volume)
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.create_cloned_volume,
+ failed_clone_sync,
+ test_volume)
+ self.driver.delete_volume(test_volume)
+
+ def test_delete_volume_notfound(self):
+ notfound_delete_vol = {}
+ notfound_delete_vol['name'] = 'notfound_delete_vol'
+ notfound_delete_vol['id'] = '10'
+ self.driver.delete_volume(notfound_delete_vol)
+
+ def test_delete_volume_failed(self):
+ self.driver.create_volume(failed_delete_vol)
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.delete_volume,
+ failed_delete_vol)
+
+ def TearDown(self):
+ bExists = os.path.exists(self.config_file_path)
+ if bExists:
+ os.remove(self.config_file_path)
+ super(EMCSMISISCSIDriverTestCase, self).tearDown()
Common class for SMI-S based EMC volume drivers.
This common class is for EMC volume drivers based on SMI-S.
-It supports ISCSI and FC protocols on VNX and VMAX/VMAXe arrays.
+It supports VNX and VMAX arrays.
"""
class EMCSMISCommon():
- """Common code used by ISCSI and FC drivers."""
+ """Common code that can be used by ISCSI and FC drivers."""
stats = {'driver_version': '1.0',
'free_capacity_gb': 0,
'vendor_name': 'EMC',
'volume_backend_name': None}
- def __init__(self):
+ def __init__(self, prtcl):
opt = cfg.StrOpt('cinder_emc_config_file',
default=CINDER_EMC_CONFIG_FILE,
help='use this file for cinder emc plugin '
'config data')
FLAGS.register_opt(opt)
+ self.protocol = prtcl
+ ip, port = self._get_ecom_server()
+ self.user, self.passwd = self._get_ecom_cred()
+ self.url = 'http://' + ip + ':' + port
+ self.conn = self._get_ecom_connection()
def create_volume(self, volume):
- """Creates a EMC(VMAX/VMAXe/VNX) volume. """
+ """Creates a EMC(VMAX/VNX) volume."""
LOG.debug(_('Entering create_volume.'))
volumesize = int(volume['size']) * 1073741824
% {'volume': volumename,
'size': volumesize})
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_("Error Create Volume: %(volumename)s. "
- "Cannot connect to ECOM server.")
- % {'volumename': volumename})
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
+ self.conn = self._get_ecom_connection()
storage_type = self._get_storage_type()
- if storage_type is None:
- exception_message = (_("Error Create Volume: %(volumename)s. "
- "Storage type %(storage_type)s not found.")
- % {'volumename': volumename,
- 'storage_type': storage_type})
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
LOG.debug(_('Create Volume: %(volume)s '
'Storage type: %(storage_type)s')
'storage_type': storage_type})
pool, storage_system = self._find_pool(storage_type)
- if pool is None:
- exception_message = (_("Error Create Volume: %(volumename)s. "
- "Pool %(storage_type)s not found.")
- % {'volumename': volumename,
- 'storage_type': storage_type})
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
-
- if storage_system is None:
- exception_message = (_("Error Create Volume: %(volumename)s. "
- "Storage system not found for pool "
- "%(storage_type)s.")
- % {'volumename': volumename,
- 'storage_type': storage_type})
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
LOG.debug(_('Create Volume: %(volume)s Pool: %(pool)s '
'Storage System: %(storage_system)s')
'pool': str(pool),
'size': volumesize})
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'CreateOrModifyElementFromStoragePool',
configservice, ElementName=volumename, InPool=pool,
ElementType=self._getnum(5, '16'),
% {'volumename': volumename,
'snapshotname': snapshotname})
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_('Error Create Volume from Snapshot: '
- 'Volume: %(volumename)s Snapshot: '
- '%(snapshotname)s. Cannot connect to'
- ' ECOM server.')
- % {'volumename': volumename,
- 'snapshotname': snapshotname})
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
+ self.conn = self._get_ecom_connection()
snapshot_instance = self._find_lun(snapshot)
storage_system = snapshot_instance['SystemName']
'sourceelement': str(snapshot_instance.path)})
# Create a Clone from snapshot
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'CreateElementReplica', repservice,
ElementName=volumename,
SyncType=self._getnum(8, '16'),
'service': str(repservice),
'sync_name': str(sync_name)})
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'ModifyReplicaSynchronization',
repservice,
Operation=self._getnum(8, '16'),
% {'volumename': volumename,
'srcname': srcname})
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_('Error Create Cloned Volume: '
- 'Volume: %(volumename)s Source Volume: '
- '%(srcname)s. Cannot connect to'
- ' ECOM server.')
- % {'volumename': volumename,
- 'srcname': srcname})
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
+ self.conn = self._get_ecom_connection()
src_instance = self._find_lun(src_vref)
storage_system = src_instance['SystemName']
'elementname': volumename,
'sourceelement': str(src_instance.path)})
- # Create a Clone from snapshot
- rc, job = conn.InvokeMethod(
+ # Create a Clone from source volume
+ rc, job = self.conn.InvokeMethod(
'CreateElementReplica', repservice,
ElementName=volumename,
SyncType=self._getnum(8, '16'),
'service': str(repservice),
'sync_name': str(sync_name)})
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'ModifyReplicaSynchronization',
repservice,
Operation=self._getnum(8, '16'),
LOG.info(_('Delete Volume: %(volume)s')
% {'volume': volumename})
+ self.conn = self._get_ecom_connection()
+
vol_instance = self._find_lun(volume)
if vol_instance is None:
LOG.error(_('Volume %(name)s not found on the array. '
% {'name': volumename})
return
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_("Error Delete Volume: %(volumename)s. "
- "Cannot connect to ECOM server.")
- % {'volumename': volumename})
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
-
storage_system = vol_instance['SystemName']
configservice = self._find_storage_configuration_service(
'name': volumename,
'vol_instance': str(vol_instance.path)})
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'EMCReturnToStoragePool',
configservice, TheElements=[vol_instance.path])
% {'snapshot': snapshotname,
'volume': volumename})
- conn = self._get_ecom_connection()
- if conn is None:
- LOG.error(_('Cannot connect to ECOM server.'))
- exception_message = (_("Cannot connect to ECOM server"))
- raise exception.VolumeBackendAPIException(data=exception_message)
+ self.conn = self._get_ecom_connection()
volume = {}
volume['name'] = volumename
'elementname': snapshotname,
'sourceelement': str(vol_instance.path)})
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'CreateElementReplica', repservice,
ElementName=snapshotname,
SyncType=self._getnum(7, '16'),
% {'snapshot': snapshotname,
'volume': volumename})
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_("Cannot connect to ECOM server"))
- raise exception.VolumeBackendAPIException(data=exception_message)
+ self.conn = self._get_ecom_connection()
LOG.debug(_('Delete Snapshot: %(snapshot)s: volume: %(volume)s. '
'Finding StorageSychronization_SV_SV.')
'service': str(repservice),
'sync_name': str(sync_name)})
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'ModifyReplicaSynchronization',
repservice,
Operation=self._getnum(19, '16'),
'snapshotname': snapshotname,
'rc': rc})
- def _iscsi_location(ip, target, iqn, lun=None):
- return "%s:%s,%s %s %s" % (ip, FLAGS.iscsi_port, target, iqn, lun)
-
- def ensure_export(self, context, volume):
- """Driver entry point to get the export info for an existing volume."""
- vol_instance = self._find_lun(volume)
- device_id = vol_instance['DeviceID']
- volumename = volume['name']
- LOG.debug(_('ensure_export: Volume: %(volume)s Device ID: '
- '%(device_id)s')
- % {'volume': volumename,
- 'device_id': device_id})
-
- return {'provider_location': device_id}
-
def create_export(self, context, volume):
"""Driver entry point to get the export info for a new volume."""
+ self.conn = self._get_ecom_connection()
volumename = volume['name']
LOG.info(_('Create export: %(volume)s')
% {'volume': volumename})
return {'provider_location': device_id}
# Mapping method for VNX
- def _expose_paths(self, conn, configservice, vol_instance, connector):
+ def _expose_paths(self, configservice, vol_instance,
+ connector):
"""This method maps a volume to a host.
It adds a volume and initiator to a Storage Group
"""
volumename = vol_instance['ElementName']
lun_name = vol_instance['DeviceID']
- initiator = self._find_initiator_name(connector)
+ initiators = self._find_initiator_names(connector)
storage_system = vol_instance['SystemName']
lunmask_ctrl = self._find_lunmasking_scsi_protocol_controller(
storage_system, connector)
% {'vol': str(vol_instance.path),
'service': str(configservice),
'lun_name': lun_name,
- 'initiator': initiator})
+ 'initiator': initiators})
if lunmask_ctrl is None:
- rc, controller = conn.InvokeMethod(
+ rc, controller = self.conn.InvokeMethod(
'ExposePaths',
configservice, LUNames=[lun_name],
- InitiatorPortIDs=[initiator],
+ InitiatorPortIDs=initiators,
DeviceAccesses=[self._getnum(2, '16')])
else:
LOG.debug(_('ExposePaths parameter '
'LunMaskingSCSIProtocolController: '
'%(lunmasking)s')
% {'lunmasking': str(lunmask_ctrl)})
- rc, controller = conn.InvokeMethod(
+ rc, controller = self.conn.InvokeMethod(
'ExposePaths',
configservice, LUNames=[lun_name],
DeviceAccesses=[self._getnum(2, '16')],
% volumename)
# Unmapping method for VNX
- def _hide_paths(self, conn, configservice, vol_instance, connector):
- """Removes a volume from the Storage Group
+ def _hide_paths(self, configservice, vol_instance,
+ connector):
+ """This method unmaps a volume from the host.
+
+ Removes a volume from the Storage Group
and therefore unmaps the volume from the host.
"""
volumename = vol_instance['ElementName']
'device_id': device_id,
'lunmasking': str(lunmask_ctrl)})
- rc, controller = conn.InvokeMethod(
+ rc, controller = self.conn.InvokeMethod(
'HidePaths', configservice,
LUNames=[device_id], ProtocolControllers=[lunmask_ctrl])
LOG.debug(_('HidePaths for volume %s completed successfully.')
% volumename)
- # Mapping method for VMAX/VMAXe
- def _add_members(self, conn, configservice, vol_instance, connector):
- """Add volume to the Device Masking Group that belongs to
- a Masking View"""
+ # Mapping method for VMAX
+ def _add_members(self, configservice, vol_instance):
+ """This method maps a volume to a host.
+
+ Add volume to the Device Masking Group that belongs to
+ a Masking View.
+ """
volumename = vol_instance['ElementName']
masking_group = self._find_device_masking_group()
'masking_group': str(masking_group),
'vol': str(vol_instance.path)})
- rc, job = conn.InvokeMethod(
+ rc, job = self.conn.InvokeMethod(
'AddMembers', configservice,
MaskingGroup=masking_group, Members=[vol_instance.path])
LOG.debug(_('AddMembers for volume %s completed successfully.')
% volumename)
- # Unmapping method for VMAX/VMAXe
- def _remove_members(self, conn, configservice, vol_instance, connector):
- """Removes an export for a volume."""
+ # Unmapping method for VMAX
+ def _remove_members(self, configservice, vol_instance):
+ """This method unmaps a volume from a host.
+
+ Removes volume from the Device Masking Group that belongs to
+ a Masking View.
+ """
volumename = vol_instance['ElementName']
masking_group = self._find_device_masking_group()
'masking_group': str(masking_group),
'vol': str(vol_instance.path)})
- rc, job = conn.InvokeMethod('RemoveMembers', configservice,
- MaskingGroup=masking_group,
- Members=[vol_instance.path])
+ rc, job = self.conn.InvokeMethod('RemoveMembers', configservice,
+ MaskingGroup=masking_group,
+ Members=[vol_instance.path])
if rc != 0L:
rc, errordesc = self._wait_for_job_complete(job)
LOG.info(_('Map volume: %(volume)s')
% {'volume': volumename})
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_("Cannot connect to ECOM server"))
- raise exception.VolumeBackendAPIException(data=exception_message)
-
vol_instance = self._find_lun(volume)
storage_system = vol_instance['SystemName']
isVMAX = storage_system.find('SYMMETRIX')
if isVMAX > -1:
- self._add_members(conn, configservice, vol_instance, connector)
+ self._add_members(configservice, vol_instance)
else:
- self._expose_paths(conn, configservice, vol_instance, connector)
+ self._expose_paths(configservice, vol_instance, connector)
def _unmap_lun(self, volume, connector):
"""Unmaps a volume from the host."""
LOG.info(_('Unmap volume: %(volume)s')
% {'volume': volumename})
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_("Cannot connect to ECOM server"))
- raise exception.VolumeBackendAPIException(data=exception_message)
-
- device_number = self.find_device_number(volume)
+ device_info = self.find_device_number(volume)
+ device_number = device_info['hostlunid']
if device_number is None:
LOG.info(_("Volume %s is not mapped. No volume to unmap.")
% (volumename))
isVMAX = storage_system.find('SYMMETRIX')
if isVMAX > -1:
- self._remove_members(conn, configservice, vol_instance, connector)
+ self._remove_members(configservice, vol_instance)
else:
- self._hide_paths(conn, configservice, vol_instance, connector)
+ self._hide_paths(configservice, vol_instance, connector)
def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info."""
volumename = volume['name']
LOG.info(_('Initialize connection: %(volume)s')
% {'volume': volumename})
- device_number = self.find_device_number(volume)
+ self.conn = self._get_ecom_connection()
+ device_info = self.find_device_number(volume)
+ device_number = device_info['hostlunid']
if device_number is not None:
LOG.info(_("Volume %s is already mapped.")
% (volumename))
else:
self._map_lun(volume, connector)
+ # Find host lun id again after the volume is exported to the host
+ device_info = self.find_device_number(volume)
+
+ return device_info
def terminate_connection(self, volume, connector):
- """Disallow connection from connector"""
+ """Disallow connection from connector."""
volumename = volume['name']
LOG.info(_('Terminate connection: %(volume)s')
% {'volume': volumename})
+ self.conn = self._get_ecom_connection()
self._unmap_lun(volume, connector)
def update_volume_status(self):
"""Retrieve status info."""
LOG.debug(_("Updating volume status"))
-
+ self.conn = self._get_ecom_connection()
storage_type = self._get_storage_type()
- if storage_type is None:
- exception_message = (_("Storage type name not found."))
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
pool, storagesystem = self._find_pool(storage_type, True)
- if pool is None:
- exception_message = (_("Error finding pool details."))
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
self.stats['total_capacity_gb'] = pool['TotalManagedSpace']
self.stats['free_capacity_gb'] = pool['RemainingManagedSpace']
return self.stats
def _get_storage_type(self, filename=None):
- """Get the storage type from the config file
- """
+ """Get the storage type from the config file."""
if filename == None:
filename = FLAGS.cinder_emc_config_file
LOG.debug(_("Found Storage Type: %s") % (storageType))
return storageType
else:
- LOG.debug(_("Storage Type not found."))
- return None
+ exception_message = (_("Storage type not found."))
+ LOG.error(exception_message)
+ raise exception.VolumeBackendAPIException(data=exception_message)
def _get_masking_view(self, filename=None):
if filename == None:
return None
def _get_ecom_connection(self, filename=None):
- ip, port = self._get_ecom_server()
- user, passwd = self._get_ecom_cred()
- url = 'http://' + ip + ':' + port
- conn = pywbem.WBEMConnection(url, (user, passwd),
+ conn = pywbem.WBEMConnection(self.url, (self.user, self.passwd),
default_namespace='root/emc')
+ if conn is None:
+ exception_message = (_("Cannot connect to ECOM server"))
+ raise exception.VolumeBackendAPIException(data=exception_message)
return conn
def _find_replication_service(self, storage_system):
foundRepService = None
- conn = self._get_ecom_connection()
- repservices = conn.EnumerateInstanceNames('EMC_ReplicationService')
+ repservices = self.conn.EnumerateInstanceNames(
+ 'EMC_ReplicationService')
for repservice in repservices:
if storage_system == repservice['SystemName']:
foundRepService = repservice
def _find_storage_configuration_service(self, storage_system):
foundConfigService = None
- conn = self._get_ecom_connection()
- configservices = conn.EnumerateInstanceNames(
+ configservices = self.conn.EnumerateInstanceNames(
'EMC_StorageConfigurationService')
for configservice in configservices:
if storage_system == configservice['SystemName']:
def _find_controller_configuration_service(self, storage_system):
foundConfigService = None
- conn = self._get_ecom_connection()
- configservices = conn.EnumerateInstanceNames(
+ configservices = self.conn.EnumerateInstanceNames(
'EMC_ControllerConfigurationService')
for configservice in configservices:
if storage_system == configservice['SystemName']:
return foundConfigService
+ def _find_storage_hardwareid_service(self, storage_system):
+ foundConfigService = None
+ configservices = self.conn.EnumerateInstanceNames(
+ 'EMC_StorageHardwareIDManagementService')
+ for configservice in configservices:
+ if storage_system == configservice['SystemName']:
+ foundConfigService = configservice
+ LOG.debug(_("Found Storage Hardware ID Management Service: %s")
+ % (str(configservice)))
+ break
+
+ return foundConfigService
+
# Find pool based on storage_type
def _find_pool(self, storage_type, details=False):
foundPool = None
systemname = None
- conn = self._get_ecom_connection()
# Only get instance names if details flag is False;
# Otherwise get the whole instances
if details is False:
- vpools = conn.EnumerateInstanceNames('EMC_VirtualProvisioningPool')
- upools = conn.EnumerateInstanceNames('EMC_UnifiedStoragePool')
+ vpools = self.conn.EnumerateInstanceNames(
+ 'EMC_VirtualProvisioningPool')
+ upools = self.conn.EnumerateInstanceNames(
+ 'EMC_UnifiedStoragePool')
else:
- vpools = conn.EnumerateInstances('EMC_VirtualProvisioningPool')
- upools = conn.EnumerateInstances('EMC_UnifiedStoragePool')
+ vpools = self.conn.EnumerateInstances(
+ 'EMC_VirtualProvisioningPool')
+ upools = self.conn.EnumerateInstances(
+ 'EMC_UnifiedStoragePool')
for upool in upools:
poolinstance = upool['InstanceID']
# Example: CLARiiON+APM00115204878+U+Pool 0
poolname, systemname = self._parse_pool_instance_id(poolinstance)
if poolname is not None and systemname is not None:
- if storage_type == poolname:
+ if str(storage_type) == str(poolname):
foundPool = upool
break
- if foundPool is not None and systemname is not None:
- return foundPool, systemname
- for vpool in vpools:
- poolinstance = vpool['InstanceID']
- # Example: SYMMETRIX+000195900551+TP+Sol_Innov
- poolname, systemname = self._parse_pool_instance_id(poolinstance)
- if poolname is not None and systemname is not None:
- if storage_type == poolname:
- foundPool = vpool
- break
+ if foundPool is None:
+ for vpool in vpools:
+ poolinstance = vpool['InstanceID']
+ # Example: SYMMETRIX+000195900551+TP+Sol_Innov
+ poolname, systemname = self._parse_pool_instance_id(
+ poolinstance)
+ if poolname is not None and systemname is not None:
+ if str(storage_type) == str(poolname):
+ foundPool = vpool
+ break
+
+ if foundPool is None:
+ exception_message = (_("Pool %(storage_type)s is not found.")
+ % {'storage_type': storage_type})
+ LOG.error(exception_message)
+ raise exception.VolumeBackendAPIException(data=exception_message)
+
+ if systemname is None:
+ exception_message = (_("Storage system not found for pool "
+ "%(storage_type)s.")
+ % {'storage_type': storage_type})
+ LOG.error(exception_message)
+ raise exception.VolumeBackendAPIException(data=exception_message)
LOG.debug(_("Pool: %(pool)s SystemName: %(systemname)s.")
% {'pool': str(foundPool), 'systemname': systemname})
device_id = None
volumename = volume['name']
- conn = self._get_ecom_connection()
- names = conn.EnumerateInstanceNames('EMC_StorageVolume')
+ names = self.conn.EnumerateInstanceNames('EMC_StorageVolume')
for n in names:
if device_id is not None:
if n['DeviceID'] == device_id:
- vol_instance = conn.GetInstance(n)
+ vol_instance = self.conn.GetInstance(n)
foundinstance = vol_instance
break
else:
continue
else:
- vol_instance = conn.GetInstance(n)
+ vol_instance = self.conn.GetInstance(n)
if vol_instance['ElementName'] == volumename:
foundinstance = vol_instance
volume['provider_location'] = foundinstance['DeviceID']
LOG.debug(_("Source: %(volumename)s Target: %(snapshotname)s.")
% {'volumename': volumename, 'snapshotname': snapshotname})
- conn = self._get_ecom_connection()
-
- names = conn.EnumerateInstanceNames('SE_StorageSynchronized_SV_SV')
+ names = self.conn.EnumerateInstanceNames(
+ 'SE_StorageSynchronized_SV_SV')
for n in names:
- snapshot_instance = conn.GetInstance(n['SyncedElement'],
- LocalOnly=False)
+ snapshot_instance = self.conn.GetInstance(n['SyncedElement'],
+ LocalOnly=False)
if snapshotname != snapshot_instance['ElementName']:
continue
- vol_instance = conn.GetInstance(n['SystemElement'],
- LocalOnly=False)
+ vol_instance = self.conn.GetInstance(n['SystemElement'],
+ LocalOnly=False)
if vol_instance['ElementName'] == volumename:
foundsyncname = n
storage_system = vol_instance['SystemName']
'sync': str(foundsyncname)})
return foundsyncname, storage_system
- def _find_initiator_name(self, connector):
- """ Get initiator name from connector['initiator']
- """
- foundinitiatorname = None
- if connector['initiator']:
- foundinitiatorname = connector['initiator']
+ def _find_initiator_names(self, connector):
+ foundinitiatornames = []
+ iscsi = 'iscsi'
+ fc = 'fc'
+ name = 'initiator name'
+ if self.protocol.lower() == iscsi and connector['initiator']:
+ foundinitiatornames.append(connector['initiator'])
+ elif self.protocol.lower() == fc and connector['wwpns']:
+ for wwn in connector['wwpns']:
+ foundinitiatornames.append(wwn)
+ name = 'world wide port names'
+
+ if foundinitiatornames is None or len(foundinitiatornames) == 0:
+ msg = (_('Error finding %s.') % name)
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
- LOG.debug(_("Initiator name: %(initiator)s.")
- % {'initiator': foundinitiatorname})
- return foundinitiatorname
+ LOG.debug(_("Found %(name)s: %(initiator)s.")
+ % {'name': name,
+ 'initiator': foundinitiatornames})
+ return foundinitiatornames
def _wait_for_job_complete(self, job):
jobinstancename = job['Job']
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_("Cannot connect to ECOM server"))
- raise exception.VolumeBackendAPIException(data=exception_message)
-
while True:
- jobinstance = conn.GetInstance(jobinstancename, LocalOnly=False)
+ jobinstance = self.conn.GetInstance(jobinstancename,
+ LocalOnly=False)
jobstate = jobinstance['JobState']
# From ValueMap of JobState in CIM_ConcreteJob
# 2L=New, 3L=Starting, 4L=Running, 32767L=Queue Pending
def _find_lunmasking_scsi_protocol_controller(self, storage_system,
connector):
foundCtrl = None
- conn = self._get_ecom_connection()
- initiator = self._find_initiator_name(connector)
- controllers = conn.EnumerateInstanceNames(
+ initiators = self._find_initiator_names(connector)
+ controllers = self.conn.EnumerateInstanceNames(
'EMC_LunMaskingSCSIProtocolController')
for ctrl in controllers:
if storage_system != ctrl['SystemName']:
continue
- associators = conn.Associators(ctrl,
- resultClass='EMC_StorageHardwareID')
+ associators = self.conn.Associators(
+ ctrl,
+ resultClass='EMC_StorageHardwareID')
for assoc in associators:
# if EMC_StorageHardwareID matches the initiator,
# we found the existing EMC_LunMaskingSCSIProtocolController
# (Storage Group for VNX)
# we can use for masking a new LUN
- if assoc['StorageID'] == initiator:
- foundCtrl = ctrl
+ hardwareid = assoc['StorageID']
+ for initiator in initiators:
+ if hardwareid.lower() == initiator.lower():
+ foundCtrl = ctrl
+ break
+
+ if foundCtrl is not None:
break
if foundCtrl is not None:
"%(storage_system)s and initiator %(initiator)s is "
"%(ctrl)s.")
% {'storage_system': storage_system,
- 'initiator': initiator,
+ 'initiator': initiators,
'ctrl': str(foundCtrl)})
return foundCtrl
def _find_lunmasking_scsi_protocol_controller_for_vol(self, vol_instance,
connector):
foundCtrl = None
- conn = self._get_ecom_connection()
- initiator = self._find_initiator_name(connector)
- controllers = conn.AssociatorNames(
+ initiators = self._find_initiator_names(connector)
+ controllers = self.conn.AssociatorNames(
vol_instance.path,
resultClass='EMC_LunMaskingSCSIProtocolController')
for ctrl in controllers:
- associators = conn.Associators(
+ associators = self.conn.Associators(
ctrl,
resultClass='EMC_StorageHardwareID')
for assoc in associators:
# we found the existing EMC_LunMaskingSCSIProtocolController
# (Storage Group for VNX)
# we can use for masking a new LUN
- if assoc['StorageID'] == initiator:
- foundCtrl = ctrl
+ hardwareid = assoc['StorageID']
+ for initiator in initiators:
+ if hardwareid.lower() == initiator.lower():
+ foundCtrl = ctrl
+ break
+
+ if foundCtrl is not None:
break
if foundCtrl is not None:
LOG.debug(_("LunMaskingSCSIProtocolController for storage volume "
"%(vol)s and initiator %(initiator)s is %(ctrl)s.")
- % {'vol': str(vol_instance.path), 'initiator': initiator,
+ % {'vol': str(vol_instance.path), 'initiator': initiators,
'ctrl': str(foundCtrl)})
return foundCtrl
+ # Find out how many volumes are mapped to a host
+ # assoociated to the LunMaskingSCSIProtocolController
+ def get_num_volumes_mapped(self, volume, connector):
+ numVolumesMapped = 0
+ volumename = volume['name']
+ vol_instance = self._find_lun(volume)
+ if vol_instance is None:
+ msg = (_('Volume %(name)s not found on the array. '
+ 'Cannot determine if there are volumes mapped.')
+ % {'name': volumename})
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ storage_system = vol_instance['SystemName']
+
+ ctrl = self._find_lunmasking_scsi_protocol_controller(
+ storage_system,
+ connector)
+
+ LOG.debug(_("LunMaskingSCSIProtocolController for storage system "
+ "%(storage)s and %(connector)s is %(ctrl)s.")
+ % {'storage': storage_system,
+ 'connector': connector,
+ 'ctrl': str(ctrl)})
+
+ associators = conn.Associators(
+ ctrl,
+ resultClass='EMC_StorageVolume')
+
+ numVolumesMapped = len(associators)
+
+ LOG.debug(_("Found %(numVolumesMapped)d volumes on storage system "
+ "%(storage)s mapped to %(initiator)s.")
+ % {'numVolumesMapped': numVolumesMapped,
+ 'storage': storage_system,
+ 'connector': connector})
+
+ return numVolumesMapped
+
# Find an available device number that a host can see
def _find_avail_device_number(self, storage_system):
out_device_number = '000000'
numlist = []
myunitnames = []
- conn = self._get_ecom_connection()
- unitnames = conn.EnumerateInstanceNames(
+ unitnames = self.conn.EnumerateInstanceNames(
'CIM_ProtocolControllerForUnit')
for unitname in unitnames:
controller = unitname['Antecedent']
classname = controller['CreationClassName']
index = classname.find('LunMaskingSCSIProtocolController')
if index > -1:
- unitinstance = conn.GetInstance(unitname, LocalOnly=False)
+ unitinstance = self.conn.GetInstance(unitname,
+ LocalOnly=False)
numDeviceNumber = int(unitinstance['DeviceNumber'])
numlist.append(numDeviceNumber)
myunitnames.append(unitname)
def find_device_number(self, volume):
out_num_device_number = None
- conn = self._get_ecom_connection()
volumename = volume['name']
vol_instance = self._find_lun(volume)
+ storage_system = vol_instance['SystemName']
- unitnames = conn.ReferenceNames(
+ unitnames = self.conn.ReferenceNames(
vol_instance.path,
ResultClass='CIM_ProtocolControllerForUnit')
index = classname.find('LunMaskingSCSIProtocolController')
if index > -1: # VNX
# Get an instance of CIM_ProtocolControllerForUnit
- unitinstance = conn.GetInstance(unitname, LocalOnly=False)
+ unitinstance = self.conn.GetInstance(unitname,
+ LocalOnly=False)
numDeviceNumber = int(unitinstance['DeviceNumber'], 16)
out_num_device_number = numDeviceNumber
break
else:
index = classname.find('Symm_LunMaskingView')
- if index > -1: # VMAX/VMAXe
- unitinstance = conn.GetInstance(unitname, LocalOnly=False)
+ if index > -1: # VMAX
+ unitinstance = self.conn.GetInstance(unitname,
+ LocalOnly=False)
numDeviceNumber = int(unitinstance['DeviceNumber'], 16)
out_num_device_number = numDeviceNumber
break
'volumename': volumename,
'vol_instance': str(vol_instance.path)})
- return out_num_device_number
+ data = {'hostlunid': out_num_device_number,
+ 'storagesystem': storage_system}
+
+ LOG.debug(_("Device info: %(data)s.") % {'data': data})
+
+ return data
def _find_device_masking_group(self):
"""Finds the Device Masking Group in a masking view."""
foundMaskingGroup = None
maskingview_name = self._get_masking_view()
- conn = self._get_ecom_connection()
- if conn is None:
- exception_message = (_("Cannot connect to ECOM server"))
- raise exception.VolumeBackendAPIException(data=exception_message)
- maskingviews = conn.EnumerateInstanceNames(
+ maskingviews = self.conn.EnumerateInstanceNames(
'EMC_LunMaskingSCSIProtocolController')
for view in maskingviews:
- instance = conn.GetInstance(view, LocalOnly=False)
+ instance = self.conn.GetInstance(view, LocalOnly=False)
if maskingview_name == instance['ElementName']:
foundView = view
break
- groups = conn.AssociatorNames(foundView,
- ResultClass='SE_DeviceMaskingGroup')
+ groups = self.conn.AssociatorNames(
+ foundView,
+ ResultClass='SE_DeviceMaskingGroup')
foundMaskingGroup = groups[0]
LOG.debug(_("Masking view: %(view)s DeviceMaskingGroup: %(masking)s.")
result = num
return result
+
+ # Find target WWNs
+ def get_target_wwns(self, storage_system, connector):
+ target_wwns = []
+
+ configservice = self._find_storage_hardwareid_service(
+ storage_system)
+ if configservice is None:
+ exception_msg = (_("Error finding Storage Hardware ID Service."))
+ LOG.error(exception_msg)
+ raise exception.VolumeBackendAPIException(data=exception_msg)
+
+ hardwareids = self._find_storage_hardwareids(connector)
+
+ LOG.debug(_('EMCGetTargetEndpoints: Service: %(service)s '
+ 'Storage HardwareIDs: %(hardwareids)s.')
+ % {'service': str(configservice),
+ 'hardwareids': str(hardwareids)})
+
+ for hardwareid in hardwareids:
+ rc, targetendpoints = self.conn.InvokeMethod(
+ 'EMCGetTargetEndpoints',
+ configservice,
+ HardwareId=hardwareid)
+
+ if rc != 0L:
+ msg = (_('Error finding Target WWNs.'))
+ LOG.error(msg)
+ raise exception.VolumeBackendAPIException(data=msg)
+
+ endpoints = targetendpoints['TargetEndpoints']
+ for targetendpoint in endpoints:
+ wwn = targetendpoint['Name']
+ # Add target wwn to the list if it is not already there
+ if not any(d.get('wwn', None) == wwn for d in target_wwns):
+ target_wwns.append({'wwn': wwn})
+ LOG.debug(_('Add target WWN: %s.') % wwn)
+
+ LOG.debug(_('Target WWNs: %s.') % target_wwns)
+
+ return target_wwns
+
+ # Find Storage Hardware IDs
+ def _find_storage_hardwareids(self, connector):
+ foundInstances = []
+ wwpns = self._find_initiator_names(connector)
+ hardwareids = self.conn.EnumerateInstances(
+ 'SE_StorageHardwareID')
+ for hardwareid in hardwareids:
+ storid = hardwareid['StorageID']
+ for wwpn in wwpns:
+ if wwpn.lower() == storid.lower():
+ foundInstances.append(hardwareid.path)
+
+ LOG.debug(_("Storage Hardware IDs for %(wwpns)s is "
+ "%(foundInstances)s.")
+ % {'wwpns': str(wwpns),
+ 'foundInstances': str(foundInstances)})
+
+ return foundInstances
# License for the specific language governing permissions and limitations
# under the License.
"""
-ISCSI Drivers for EMC VNX and VMAX/VMAXe arrays based on SMI-S.
+ISCSI Drivers for EMC VNX and VMAX arrays based on SMI-S.
"""
FLAGS = flags.FLAGS
-def get_iscsi_initiator():
- """Get iscsi initiator name for this machine."""
- # NOTE openiscsi stores initiator name in a file that
- # needs root permission to read.
- contents = utils.read_file_as_root('/etc/iscsi/initiatorname.iscsi')
- for l in contents.split('\n'):
- if l.startswith('InitiatorName='):
- return l[l.index('=') + 1:].strip()
-
-
class EMCSMISISCSIDriver(driver.ISCSIDriver):
- """EMC ISCSI Drivers for VMAX/VMAXe and VNX using SMI-S."""
+ """EMC ISCSI Drivers for VMAX and VNX using SMI-S."""
def __init__(self, *args, **kwargs):
super(EMCSMISISCSIDriver, self).__init__(*args, **kwargs)
- self.common = emc_smis_common.EMCSMISCommon()
+ self.common = emc_smis_common.EMCSMISCommon('iSCSI')
def check_for_setup_error(self):
pass
def create_volume(self, volume):
- """Creates a EMC(VMAX/VMAXe/VNX) volume."""
+ """Creates a EMC(VMAX/VNX) volume."""
self.common.create_volume(volume)
def create_volume_from_snapshot(self, volume, snapshot):
"""Deletes a snapshot."""
self.common.delete_snapshot(snapshot)
- def _iscsi_location(ip, target, iqn, lun=None):
- return "%s:%s,%s %s %s" % (ip, FLAGS.iscsi_port, target, iqn, lun)
-
def ensure_export(self, context, volume):
"""Driver entry point to get the export info for an existing volume."""
- return self.common.ensure_export(context, volume)
+ pass
def create_export(self, context, volume):
"""Driver entry point to get the export info for a new volume."""
def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info.
- the iscsi driver returns a driver_volume_type of 'iscsi'.
+ The iscsi driver returns a driver_volume_type of 'iscsi'.
the format of the driver data is defined in _get_iscsi_properties.
Example return value::
FLAGS.iscsi_ip_address,
run_as_root=True)
for target in out.splitlines():
- return target
+ index = target.find(FLAGS.iscsi_ip_address)
+ if index != -1:
+ return target
return None
def _get_iscsi_properties(self, volume):
- """Gets iscsi configuration
+ """Gets iscsi configuration.
We ideally get saved information in the volume entity, but fall back
to discovery if need be. Discovery may be completely removed in future
properties['target_portal'] = results[0].split(",")[0]
properties['target_iqn'] = results[1]
- device_number = self.common.find_device_number(volume)
- if device_number is None:
+ device_info = self.common.find_device_number(volume)
+ if device_info is None or device_info['hostlunid'] is None:
exception_message = (_("Cannot find device number for volume %s")
% volume['name'])
raise exception.VolumeBackendAPIException(data=exception_message)
+ device_number = device_info['hostlunid']
+
properties['target_lun'] = device_number
properties['volume_id'] = volume['id']
return properties
- def _run_iscsiadm(self, iscsi_properties, iscsi_command, **kwargs):
- check_exit_code = kwargs.pop('check_exit_code', 0)
- (out, err) = self._execute('iscsiadm', '-m', 'node', '-T',
- iscsi_properties['target_iqn'],
- '-p', iscsi_properties['target_portal'],
- *iscsi_command, run_as_root=True,
- check_exit_code=check_exit_code)
- LOG.debug("iscsiadm %s: stdout=%s stderr=%s" %
- (iscsi_command, out, err))
- return (out, err)
-
def terminate_connection(self, volume, connector, **kwargs):
- """Disallow connection from connector"""
+ """Disallow connection from connector."""
self.common.terminate_connection(volume, connector)
- def copy_image_to_volume(self, context, volume, image_service, image_id):
- """Fetch the image from image_service and write it to the volume."""
- LOG.debug(_('copy_image_to_volume %s.') % volume['name'])
- initiator = get_iscsi_initiator()
- connector = {}
- connector['initiator'] = initiator
-
- iscsi_properties, volume_path = self._attach_volume(
- context, volume, connector)
-
- with utils.temporary_chown(volume_path):
- with utils.file_open(volume_path, "wb") as image_file:
- image_service.download(context, image_id, image_file)
-
- self.terminate_connection(volume, connector)
-
- def _attach_volume(self, context, volume, connector):
- """Attach the volume."""
- iscsi_properties = None
- host_device = None
- init_conn = self.initialize_connection(volume, connector)
- iscsi_properties = init_conn['data']
-
- self._run_iscsiadm(iscsi_properties, ("--login",),
- check_exit_code=[0, 255])
-
- self._iscsiadm_update(iscsi_properties, "node.startup", "automatic")
-
- host_device = ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" %
- (iscsi_properties['target_portal'],
- iscsi_properties['target_iqn'],
- iscsi_properties.get('target_lun', 0)))
-
- tries = 0
- while not os.path.exists(host_device):
- if tries >= FLAGS.num_iscsi_scan_tries:
- raise exception.CinderException(
- _("iSCSI device not found at %s") % (host_device))
-
- LOG.warn(_("ISCSI volume not yet found at: %(host_device)s. "
- "Will rescan & retry. Try number: %(tries)s") %
- locals())
-
- # The rescan isn't documented as being necessary(?), but it helps
- self._run_iscsiadm(iscsi_properties, ("--rescan",))
-
- tries = tries + 1
- if not os.path.exists(host_device):
- time.sleep(tries ** 2)
-
- if tries != 0:
- LOG.debug(_("Found iSCSI node %(host_device)s "
- "(after %(tries)s rescans)") %
- locals())
-
- return iscsi_properties, host_device
-
- def copy_volume_to_image(self, context, volume, image_service, image_meta):
- """Copy the volume to the specified image."""
- LOG.debug(_('copy_volume_to_image %s.') % volume['name'])
- initiator = get_iscsi_initiator()
- connector = {}
- connector['initiator'] = initiator
-
- iscsi_properties, volume_path = self._attach_volume(
- context, volume, connector)
-
- with utils.temporary_chown(volume_path):
- with utils.file_open(volume_path) as volume_file:
- image_service.update(context, image_meta['id'], {},
- volume_file)
-
- self.terminate_connection(volume, connector)
-
def get_volume_stats(self, refresh=False):
"""Get volume status.
- If 'refresh' is True, run update the stats first."""
+
+ If 'refresh' is True, run update the stats first.
+ """
if refresh:
self.update_volume_status()