Dell sc driver iscsi multipath enhancement

Added support for the connector's multipath boolean.  If this is
set the initialize_connection function returns arrays of all the
IQNs, LUNs and portals associated with the mapping of the volume
to the server.  If this is not set the standard return of a lone
IQN, LUN and portal is implemented.

Minor change to find_volume.  If a volume name or instance isn't
set then it will not be sent as a payload filter to the Dell
Storage Center array.

Additional Unit Tests to support the changes.

Change-Id: I2f7404c76c71f5bf6d06496ab5aa4617ba80615b
Implements: blueprint dell-sc-driver-iscsi-multipath-enhancement
This commit is contained in:
Tom Swanson 2015-02-17 14:00:04 -06:00
parent 260701bacc
commit 8b2bfe46b5
5 changed files with 808 additions and 168 deletions

View File

@ -310,6 +310,38 @@ class DellSCSanFCDriverTestCase(test.TestCase):
volume,
connector)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_sc',
return_value=12345)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_server',
return_value=SCSERVER)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume',
return_value=VOLUME)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'map_volume',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_wwns',
return_value=(None, [], {}))
def test_initialize_connection_map_vol_fail(self,
mock_find_wwns,
mock_map_volume,
mock_find_volume,
mock_find_server,
mock_find_sc,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where map_volume returns None (no mappings)
volume = {'id': self.volume_name}
connector = self.connector
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
volume,
connector)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_sc',
return_value=64702)

View File

@ -176,6 +176,19 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
IQN = 'iqn.2002-03.com.compellent:5000D31000000001'
ISCSI_PROPERTIES = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns':
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
'target_luns': [1],
'target_portals': [u'192.168.0.21:3260']}
ISCSI_PROPERTIES_EMPTY = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns': [],
'target_luns': [],
'target_portals': []}
def setUp(self):
super(DellSCSanISCSIDriverTestCase, self).setUp()
@ -216,6 +229,11 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'ip': '10.0.0.2',
'initiator': 'iqn.1993-08.org.debian:01:2227dab76162',
'host': 'fakehost'}
self.connector_multipath = {
'ip': '10.0.0.2',
'initiator': 'iqn.1993-08.org.debian:01:2227dab76162',
'host': 'fakehost',
'multipath': True}
self.access_record_output = [
"ID Initiator Ipaddress AuthMethod UserName Apply-To",
"--- --------------- ------------- ---------- ---------- --------",
@ -323,10 +341,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'map_volume',
return_value=MAPPINGS[0])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_iqn',
return_value=IQN)
'find_iscsi_properties',
return_value=ISCSI_PROPERTIES)
def test_initialize_connection(self,
mock_find_iqn,
mock_find_iscsi_props,
mock_map_volume,
mock_find_volume,
mock_create_server,
@ -342,6 +360,62 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
# verify find_volume has been called and that is has been called twice
mock_find_volume.assert_any_call(12345, self.volume_name)
assert mock_find_volume.call_count == 2
expected = {'data':
{'access_mode': 'rw',
'target_discovered': False,
'target_iqn':
u'iqn.2002-03.com.compellent:5000d31000fcbe43',
'target_lun': 1,
'target_portal': u'192.168.0.21:3260'},
'driver_volume_type': 'iscsi'}
self.assertEqual(expected, data, 'Unexpected return value')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_sc',
return_value=12345)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_server',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'create_server',
return_value=SCSERVER)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume',
return_value=VOLUME)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'map_volume',
return_value=MAPPINGS[0])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_iscsi_properties',
return_value=ISCSI_PROPERTIES)
def test_initialize_connection_multi_path(self,
mock_find_iscsi_props,
mock_map_volume,
mock_find_volume,
mock_create_server,
mock_find_server,
mock_find_sc,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where connection is multipath
volume = {'id': self.volume_name}
connector = self.connector_multipath
data = self.driver.initialize_connection(volume, connector)
self.assertEqual(data['driver_volume_type'], 'iscsi')
# verify find_volume has been called and that is has been called twice
mock_find_volume.assert_any_call(12345, self.volume_name)
assert mock_find_volume.call_count == 2
expected = {'data':
{'access_mode': 'rw',
'target_discovered': False,
'target_iqns':
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
'target_luns': [1],
'target_portals': [u'192.168.0.21:3260']},
'driver_volume_type': 'iscsi'}
self.assertEqual(expected, data, 'Unexpected return value')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_sc',
@ -356,10 +430,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'map_volume',
return_value=MAPPINGS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_iqn',
return_value=None)
'find_iscsi_properties',
return_value=ISCSI_PROPERTIES_EMPTY)
def test_initialize_connection_no_iqn(self,
mock_find_iqn,
mock_find_iscsi_properties,
mock_map_volume,
mock_find_volume,
mock_find_server,
@ -390,10 +464,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'map_volume',
return_value=MAPPINGS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_iqn',
return_value=None)
'find_iscsi_properties',
return_value=ISCSI_PROPERTIES_EMPTY)
def test_initialize_connection_no_server(self,
mock_find_iqn,
mock_find_iscsi_properties,
mock_map_volume,
mock_find_volume,
mock_create_server,
@ -422,10 +496,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'map_volume',
return_value=MAPPINGS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_iqn',
return_value=None)
'find_iscsi_properties',
return_value=ISCSI_PROPERTIES_EMPTY)
def test_initialize_connection_vol_not_found(self,
mock_find_iqn,
mock_find_iscsi_properties,
mock_map_volume,
mock_find_volume,
mock_find_server,
@ -440,6 +514,42 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
volume,
connector)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_sc',
return_value=12345)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_server',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'create_server',
return_value=SCSERVER)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume',
return_value=VOLUME)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'map_volume',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_iscsi_properties',
return_value=ISCSI_PROPERTIES)
def test_initialize_connection_map_vol_fail(self,
mock_find_iscsi_props,
mock_map_volume,
mock_find_volume,
mock_create_server,
mock_find_server,
mock_find_sc,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where map_volume returns None (no mappings)
volume = {'id': self.volume_name}
connector = self.connector
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
volume,
connector)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_sc',
return_value=12345)

View File

@ -180,6 +180,7 @@ class DellSCSanAPITestCase(test.TestCase):
u'objectType': u'ScServerOperatingSystem'}
}
# ScServer where deletedAllowed=False (not allowed to be deleted)
SCSERVER_NO_DEL = {u'scName': u'Storage Center 64702',
u'volumeCount': 0,
u'removeHbasAllowed': True,
@ -278,6 +279,41 @@ class DellSCSanAPITestCase(test.TestCase):
u'instanceName': u'Other Singlepath',
u'objectType': u'ScServerOperatingSystem'}}]
# ScServers list where status = Down
SCSERVERS_DOWN = \
[{u'scName': u'Storage Center 64702',
u'volumeCount': 5,
u'removeHbasAllowed': True,
u'legacyFluidFs': False,
u'serverFolderIndex': 0,
u'alertOnConnectivity': True,
u'objectType': u'ScPhysicalServer',
u'instanceName': u'openstack4',
u'instanceId': u'64702.1',
u'serverFolderPath': u'',
u'portType': [u'Iscsi'],
u'type': u'Physical',
u'statusMessage': u'',
u'status': u'Down',
u'scSerialNumber': 64702,
u'serverFolder': {u'instanceId': u'64702.0',
u'instanceName': u'Servers',
u'objectType': u'ScServerFolder'},
u'parentIndex': 0,
u'connectivity': u'Up',
u'hostCacheIndex': 0,
u'deleteAllowed': True,
u'pathCount': 0,
u'name': u'openstack4',
u'hbaPresent': True,
u'hbaCount': 1,
u'notes': u'',
u'mapped': True,
u'operatingSystem':
{u'instanceId': u'64702.3',
u'instanceName': u'Other Multipath',
u'objectType': u'ScServerOperatingSystem'}}]
MAP_PROFILES = [{u'instanceId': u'64702.2941',
u'scName': u'Storage Center 64702',
u'scSerialNumber': 64702,
@ -363,6 +399,126 @@ class DellSCSanAPITestCase(test.TestCase):
u'transport': u'Iscsi',
u'objectType': u'ScMapping'}]
# Multiple mappings to test find_iscsi_properties with multiple portals
MAPPINGS_MULTI_PORTAL = \
[{u'profile': {u'instanceId': u'64702.104',
u'instanceName': u'92-30',
u'objectType': u'ScMappingProfile'},
u'status': u'Down',
u'statusMessage': u'',
u'instanceId': u'64702.969.64702',
u'scName': u'Storage Center 64702',
u'scSerialNumber': 64702,
u'controller': {u'instanceId': u'64702.64702',
u'instanceName': u'SN 64702',
u'objectType': u'ScController'},
u'server': {u'instanceId': u'64702.30',
u'instanceName':
u'Server_iqn.1993-08.org.debian:01:3776df826e4f',
u'objectType': u'ScPhysicalServer'},
u'volume': {u'instanceId': u'64702.92',
u'instanceName':
u'volume-74a21934-60ad-4cf2-b89b-1f0dda309ddf',
u'objectType': u'ScVolume'},
u'readOnly': False,
u'lun': 1,
u'lunUsed': [1],
u'serverHba': {u'instanceId': u'64702.3454975614',
u'instanceName':
u'iqn.1993-08.org.debian:01:3776df826e4f',
u'objectType': u'ScServerHba'},
u'path': {u'instanceId': u'64702.64702.64702.31.8',
u'instanceName':
u'iqn.1993-08.org.debian:'
'01:3776df826e4f-5000D31000FCBE43',
u'objectType': u'ScServerHbaPath'},
u'controllerPort': {u'instanceId':
u'64702.5764839588723736131.91',
u'instanceName': u'5000D31000FCBE43',
u'objectType': u'ScControllerPort'},
u'instanceName': u'64702-969',
u'transport': u'Iscsi',
u'objectType': u'ScMapping'},
{u'profile': {u'instanceId': u'64702.104',
u'instanceName': u'92-30',
u'objectType': u'ScMappingProfile'},
u'status': u'Down',
u'statusMessage': u'',
u'instanceId': u'64702.969.64702',
u'scName': u'Storage Center 64702',
u'scSerialNumber': 64702,
u'controller': {u'instanceId': u'64702.64702',
u'instanceName': u'SN 64702',
u'objectType': u'ScController'},
u'server': {u'instanceId': u'64702.30',
u'instanceName':
u'Server_iqn.1993-08.org.debian:01:3776df826e4f',
u'objectType': u'ScPhysicalServer'},
u'volume': {u'instanceId': u'64702.92',
u'instanceName':
u'volume-74a21934-60ad-4cf2-b89b-1f0dda309ddf',
u'objectType': u'ScVolume'},
u'readOnly': False,
u'lun': 1,
u'lunUsed': [1],
u'serverHba': {u'instanceId': u'64702.3454975614',
u'instanceName':
u'iqn.1993-08.org.debian:01:3776df826e4f',
u'objectType': u'ScServerHba'},
u'path': {u'instanceId': u'64702.64702.64702.31.8',
u'instanceName':
u'iqn.1993-08.org.debian:'
'01:3776df826e4f-5000D31000FCBE43',
u'objectType': u'ScServerHbaPath'},
u'controllerPort': {u'instanceId':
u'64702.5764839588723736131.91',
u'instanceName': u'5000D31000FCBE43',
u'objectType': u'ScControllerPort'},
u'instanceName': u'64702-969',
u'transport': u'Iscsi',
u'objectType': u'ScMapping'}]
MAPPINGS_READ_ONLY = \
[{u'profile': {u'instanceId': u'64702.104',
u'instanceName': u'92-30',
u'objectType': u'ScMappingProfile'},
u'status': u'Down',
u'statusMessage': u'',
u'instanceId': u'64702.969.64702',
u'scName': u'Storage Center 64702',
u'scSerialNumber': 64702,
u'controller': {u'instanceId': u'64702.64702',
u'instanceName': u'SN 64702',
u'objectType': u'ScController'},
u'server': {u'instanceId': u'64702.30',
u'instanceName':
u'Server_iqn.1993-08.org.debian:01:3776df826e4f',
u'objectType': u'ScPhysicalServer'},
u'volume': {u'instanceId': u'64702.92',
u'instanceName':
u'volume-74a21934-60ad-4cf2-b89b-1f0dda309ddf',
u'objectType': u'ScVolume'},
u'readOnly': True,
u'lun': 1,
u'lunUsed': [1],
u'serverHba': {u'instanceId': u'64702.3454975614',
u'instanceName':
u'iqn.1993-08.org.debian:01:3776df826e4f',
u'objectType': u'ScServerHba'},
u'path': {u'instanceId': u'64702.64702.64702.31.8',
u'instanceName':
u'iqn.1993-08.org.debian:'
'01:3776df826e4f-5000D31000FCBE43',
u'objectType': u'ScServerHbaPath'},
u'controllerPort': {u'instanceId':
u'64702.5764839588723736131.91',
u'instanceName':
u'5000D31000FCBE43',
u'objectType': u'ScControllerPort'},
u'instanceName': u'64702-969',
u'transport': u'Iscsi',
u'objectType': u'ScMapping'}]
FC_MAPPINGS = [{u'profile': {u'instanceId': u'64702.2941',
u'instanceName': u'6025-47',
u'objectType': u'ScMappingProfile'},
@ -725,6 +881,71 @@ class DellSCSanAPITestCase(test.TestCase):
u'bidirectionalChapSecret': u'',
u'keepAliveTimeout': u'SECONDS_30'}]
# For testing find_iscsi_properties where multiple portals are found
ISCSI_FLT_DOMAINS_MULTI_PORTALS = \
[{u'headerDigestEnabled': False,
u'classOfServicePriority': 0,
u'wellKnownIpAddress': u'192.168.0.21',
u'scSerialNumber': 64702,
u'iscsiName':
u'iqn.2002-03.com.compellent:5000d31000fcbe42',
u'portNumber': 3260,
u'subnetMask': u'255.255.255.0',
u'gateway': u'192.168.0.1',
u'objectType': u'ScIscsiFaultDomain',
u'chapEnabled': False,
u'instanceId': u'64702.6.5.3',
u'childStatus': u'Up',
u'defaultTimeToRetain': u'SECONDS_20',
u'dataDigestEnabled': False,
u'instanceName': u'iSCSI 10G 2',
u'statusMessage': u'',
u'status': u'Up',
u'transportType': u'Iscsi',
u'vlanId': 0,
u'windowSize': u'131072.0 Bytes',
u'defaultTimeToWait': u'SECONDS_2',
u'scsiCommandTimeout': u'MINUTES_1',
u'deleteAllowed': False,
u'name': u'iSCSI 10G 2',
u'immediateDataWriteEnabled': False,
u'scName': u'Storage Center 64702',
u'notes': u'',
u'mtu': u'MTU_1500',
u'bidirectionalChapSecret': u'',
u'keepAliveTimeout': u'SECONDS_30'},
{u'headerDigestEnabled': False,
u'classOfServicePriority': 0,
u'wellKnownIpAddress': u'192.168.0.25',
u'scSerialNumber': 64702,
u'iscsiName':
u'iqn.2002-03.com.compellent:5000d31000fcbe42',
u'portNumber': 3260,
u'subnetMask': u'255.255.255.0',
u'gateway': u'192.168.0.1',
u'objectType': u'ScIscsiFaultDomain',
u'chapEnabled': False,
u'instanceId': u'64702.6.5.3',
u'childStatus': u'Up',
u'defaultTimeToRetain': u'SECONDS_20',
u'dataDigestEnabled': False,
u'instanceName': u'iSCSI 10G 2',
u'statusMessage': u'',
u'status': u'Up',
u'transportType': u'Iscsi',
u'vlanId': 0,
u'windowSize': u'131072.0 Bytes',
u'defaultTimeToWait': u'SECONDS_2',
u'scsiCommandTimeout': u'MINUTES_1',
u'deleteAllowed': False,
u'name': u'iSCSI 10G 2',
u'immediateDataWriteEnabled': False,
u'scName': u'Storage Center 64702',
u'notes': u'',
u'mtu': u'MTU_1500',
u'bidirectionalChapSecret': u'',
u'keepAliveTimeout': u'SECONDS_30'}]
ISCSI_FLT_DOMAIN = {u'headerDigestEnabled': False,
u'classOfServicePriority': 0,
u'wellKnownIpAddress': u'192.168.0.21',
@ -895,7 +1116,7 @@ class DellSCSanAPITestCase(test.TestCase):
def setUp(self):
super(DellSCSanAPITestCase, self).setUp()
# configuration is a mock. A mock is pretty much a blank
# Configuration is a mock. A mock is pretty much a blank
# slate. I believe mock's done in setup are not happy time
# mocks. So we just do a few things like driver config here.
self.configuration = mock.Mock()
@ -1118,7 +1339,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_close_connection,
mock_open_connection,
mock_init):
# test case for folder path with multiple folders
# Test case for folder path with multiple folders
res = self.scapi._find_folder(
'StorageCenter/ScVolumeFolder', 12345,
u'testParentFolder/opnstktst')
@ -1209,6 +1430,31 @@ class DellSCSanAPITestCase(test.TestCase):
self.scapi._init_volume(self.VOLUME)
mock_post.assert_called()
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'unmap_volume',
return_value=True)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'map_volume',
return_value=MAPPINGS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=SCSERVERS_DOWN)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'post',
return_value=RESPONSE_200)
def test_init_volume_servers_down(self,
mock_post,
mock_get_json,
mock_map_volume,
mock_unmap_volume,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where ScServer Status = Down
self.scapi._init_volume(self.VOLUME)
mock_map_volume.assert_called()
mock_unmap_volume.assert_called()
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=VOLUME)
@ -1338,16 +1584,38 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.HttpClient,
'post',
return_value=RESPONSE_200)
def test_find_volume(self,
mock_post,
mock_first_result,
mock_close_connection,
mock_open_connection,
mock_init):
self.scapi.find_volume(12345,
self.volume_name)
def test_find_volume_by_name(self,
mock_post,
mock_first_result,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case to find volume by name
res = self.scapi.find_volume(12345,
self.volume_name)
mock_post.assert_called()
mock_first_result.assert_called()
self.assertEqual(self.VOLUME, res, 'Unexpected volume')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_first_result',
return_value=VOLUME)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'post',
return_value=RESPONSE_200)
# Test case to find volume by InstancedId
def test_find_volume_by_instanceid(self,
mock_post,
mock_first_result,
mock_close_connection,
mock_open_connection,
mock_init):
res = self.scapi.find_volume(12345,
None,
'64702.3494')
mock_post.assert_called()
mock_first_result.assert_called()
self.assertEqual(self.VOLUME, res, 'Unexpected volume')
def test_find_volume_no_name_or_instance(self,
mock_close_connection,
@ -1871,6 +2139,36 @@ class DellSCSanAPITestCase(test.TestCase):
self.IQN)
self.assertIsNone(res, 'Expected None')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=ISCSI_FLT_DOMAINS)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get',
return_value=RESPONSE_200)
def test_find_domains(self,
mock_get,
mock_get_json,
mock_close_connection,
mock_open_connection,
mock_init):
res = self.scapi._find_domains(u'64702.5764839588723736074.69')
mock_get .assert_called()
mock_get_json.assert_called()
self.assertEqual(
self.ISCSI_FLT_DOMAINS, res, 'Unexpected ScIscsiFaultDomain')
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get',
return_value=RESPONSE_204)
def test_find_domains_error(self,
mock_get,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where get of ScControllerPort FaultDomainList fails
res = self.scapi._find_domains(u'64702.5764839588723736074.69')
self.assertIsNone(res, 'Expected None')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=ISCSI_FLT_DOMAINS)
@ -1915,7 +2213,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_open_connection,
mock_init):
# Test case where domainip does not equal any WellKnownIpAddress
# of thefault domains
# of the fault domains
res = self.scapi._find_domain(u'64702.5764839588723736074.69',
u'192.168.0.22')
self.assertIsNone(res, 'Expected None')
@ -2082,7 +2380,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_close_connection,
mock_open_connection,
mock_init):
# Test case of where get of ScVolume MappingList fails
# Test case where get of ScVolume MappingList fails
res = self.scapi._find_controller_port(self.VOLUME)
mock_get.assert_called()
self.assertIsNone(res, 'None expected')
@ -2130,7 +2428,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where there are no ScMapping
# Test case where there are no ScMapping(s)
lun, wwns, itmap = self.scapi.find_wwns(self.VOLUME,
self.SCSERVER)
mock_find_fc_initiators.assert_called()
@ -2169,64 +2467,221 @@ class DellSCSanAPITestCase(test.TestCase):
'_find_controller_port',
return_value=ISCSI_CTRLR_PORT)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domain',
return_value=ISCSI_FLT_DOMAIN)
'_find_domains',
return_value=ISCSI_FLT_DOMAINS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=MAPPINGS)
def test_find_iqn_mappings(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
res = self.scapi.find_iqn(self.VOLUME,
self.SCSERVER)
def test_find_iscsi_properties_mappings(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
res = self.scapi.find_iscsi_properties(self.VOLUME)
mock_find_mappings.assert_called()
mock_find_domain.assert_called()
mock_find_ctrl_port.assert_called()
expected_iqn = u'iqn.2002-03.com.compellent:5000d31000fcbe43'
self.assertEqual(expected_iqn, res, 'Wrong IQN')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=[])
def test_find_iqn_no_mapping(self,
mock_find_mappings,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where there are no ScMapping
res = self.scapi.find_iqn(self.VOLUME,
self.SCSERVER)
mock_find_mappings.assert_called()
self.assertIsNone(res, 'No IQN expected')
expected = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns':
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
'target_luns': [1],
'target_portals': [u'192.168.0.21:3260']}
self.assertEqual(expected, res, 'Wrong Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_controller_port',
return_value=ISCSI_CTRLR_PORT)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domain',
'_find_domains',
return_value=ISCSI_FLT_DOMAINS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=MAPPINGS)
def test_find_iscsi_properties_by_address(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case to find iSCSI mappings by IP Address & port
res = self.scapi.find_iscsi_properties(
self.VOLUME, '192.168.0.21', 3260)
mock_find_mappings.assert_called()
mock_find_domain.assert_called()
mock_find_ctrl_port.assert_called()
expected = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns':
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
'target_luns': [1],
'target_portals': [u'192.168.0.21:3260']}
self.assertEqual(expected, res, 'Wrong Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_controller_port',
return_value=ISCSI_CTRLR_PORT)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domains',
return_value=ISCSI_FLT_DOMAINS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=MAPPINGS)
def test_find_iscsi_properties_by_address_not_found(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case to find iSCSI mappings by IP Address & port are not found
res = self.scapi.find_iscsi_properties(
self.VOLUME, '192.168.1.21', 3260)
mock_find_mappings.assert_called()
mock_find_domain.assert_called()
mock_find_ctrl_port.assert_called()
expected = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns': [],
'target_luns': [],
'target_portals': []}
self.assertEqual(expected, res, 'Wrong Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=[])
def test_find_iscsi_properties_no_mapping(self,
mock_find_mappings,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where there are no ScMapping(s)
res = self.scapi.find_iscsi_properties(self.VOLUME)
mock_find_mappings.assert_called()
expected = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns': [],
'target_luns': [],
'target_portals': []}
self.assertEqual(expected, res, 'Expected empty Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_controller_port',
return_value=ISCSI_CTRLR_PORT)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domains',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=MAPPINGS)
def test_find_iqn_no_domain(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where there are no ScFaultDomain
res = self.scapi.find_iqn(self.VOLUME,
self.SCSERVER)
def test_find_iscsi_properties_no_domain(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where there are no ScFaultDomain(s)
res = self.scapi.find_iscsi_properties(self.VOLUME)
mock_find_mappings.assert_called()
mock_find_domain.assert_called()
mock_find_ctrl_port.assert_called()
self.assertIsNone(res, 'No IQN expected')
expected = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns': [],
'target_luns': [],
'target_portals': []}
self.assertEqual(expected, res, 'Expected empty Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_controller_port',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domains',
return_value=ISCSI_FLT_DOMAINS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=MAPPINGS)
def test_find_iscsi_properties_no_ctrl_port(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where there are no ScFaultDomain(s)
res = self.scapi.find_iscsi_properties(self.VOLUME)
mock_find_mappings.assert_called()
mock_find_domain.assert_called()
mock_find_ctrl_port.assert_called()
expected = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns': [],
'target_luns': [],
'target_portals': []}
self.assertEqual(expected, res, 'Expected empty Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_controller_port',
return_value=ISCSI_CTRLR_PORT)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domains',
return_value=ISCSI_FLT_DOMAINS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=MAPPINGS_READ_ONLY)
def test_find_iscsi_properties_ro(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where Read Only mappings are found
res = self.scapi.find_iscsi_properties(self.VOLUME)
mock_find_mappings.assert_called()
mock_find_domain.assert_called()
mock_find_ctrl_port.assert_called()
expected = {'access_mode': 'ro',
'target_discovered': False,
'target_iqns':
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
'target_luns': [1],
'target_portals': [u'192.168.0.21:3260']}
self.assertEqual(expected, res, 'Wrong Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_controller_port',
return_value=ISCSI_CTRLR_PORT)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domains',
return_value=ISCSI_FLT_DOMAINS_MULTI_PORTALS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings',
return_value=MAPPINGS_MULTI_PORTAL)
def test_find_iscsi_properties_multi_portals(self,
mock_find_mappings,
mock_find_domain,
mock_find_ctrl_port,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where there are multiple portals
res = self.scapi.find_iscsi_properties(self.VOLUME)
mock_find_mappings.assert_called()
mock_find_domain.assert_called()
mock_find_ctrl_port.assert_called()
expected = {'access_mode': 'rw',
'target_discovered': False,
'target_iqns':
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
'target_luns': [1],
'target_portals':
[u'192.168.0.21:3260', u'192.168.0.25:3260']}
self.assertEqual(expected, res, 'Wrong Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_first_result',
@ -2833,7 +3288,7 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
def setUp(self):
super(DellSCSanAPIConnectionTestCase, self).setUp()
# configuration is a mock. A mock is pretty much a blank
# Configuration is a mock. A mock is pretty much a blank
# slate. I believe mock's done in setup are not happy time
# mocks. So we just do a few things like driver config here.
self.configuration = mock.Mock()
@ -2870,8 +3325,6 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
return_value=RESPONSE_204)
def test_open_connection_failure(self,
mock_post):
# self.scapi.open_connection()
# mock_post.assert_called()
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.open_connection)

View File

@ -17,6 +17,7 @@ import json
import os.path
import requests
import six
from cinder import exception
from cinder.i18n import _, _LE, _LI, _LW
@ -65,8 +66,6 @@ class HttpClient(object):
self.header = {}
self.header['Content-Type'] = 'application/json; charset=utf-8'
self.header['x-dell-api-version'] = '1.5'
# we don't verify. If the end user has a real SSL cert rather than
# a self signed cert they could turn this on
self.verify = False
def __enter__(self):
@ -170,7 +169,7 @@ class StorageCenterApi(object):
rsp = None
content = self._get_json(blob)
if content is not None:
# we can get a list or a dict or nothing
# We can get a list or a dict or nothing
if isinstance(content, list):
for r in content:
if attribute is None or r.get(attribute) == value:
@ -246,7 +245,7 @@ class StorageCenterApi(object):
return self._get_id(result)
# volume functions
# Volume functions
def _create_folder(self, url, ssn, parent, folder):
'''This is generic to server and volume folders.
@ -276,12 +275,12 @@ class StorageCenterApi(object):
path = self._path_to_array(foldername)
folderpath = ''
instanceId = ''
# technically the first folder is the root so that is already created.
# Technically the first folder is the root so that is already created.
found = True
f = None
for folder in path:
folderpath = folderpath + folder
# if the last was found see if this part of the path exists too
# If the last was found see if this part of the path exists too
if found:
listurl = url + '/GetList'
f = self._find_folder(listurl,
@ -289,18 +288,18 @@ class StorageCenterApi(object):
folderpath)
if f is None:
found = False
# we didn't find it so create it
# We didn't find it so create it
if found is False:
f = self._create_folder(url,
ssn,
instanceId,
folder)
# if we haven't found a folder or created it then leave
# If we haven't found a folder or created it then leave
if f is None:
LOG.error(_LE('Unable to create folder path %s'),
folderpath)
break
# next part of the path will need this
# Next part of the path will need this
instanceId = self._get_id(f)
folderpath = folderpath + '/'
return f
@ -316,7 +315,7 @@ class StorageCenterApi(object):
pf.append('scSerialNumber', ssn)
basename = os.path.basename(foldername)
pf.append('Name', basename)
# if we have any kind of path we add '/' to match the storage
# If we have any kind of path we add '/' to match the storage
# center's convention and throw it into the filters.
folderpath = os.path.dirname(foldername)
if folderpath != '':
@ -373,7 +372,7 @@ class StorageCenterApi(object):
the volume will be created in the root.
'''
scvolume = None
# find our folder
# Find our folder
LOG.debug('Create Volume %(name)s %(ssn)s %(folder)s',
{'name': name,
'ssn': ssn,
@ -381,12 +380,12 @@ class StorageCenterApi(object):
folder = self._find_volume_folder(ssn,
volfolder)
# doesn't exist? make it
# Doesn't exist? make it
if folder is None:
folder = self._create_volume_folder_path(ssn,
volfolder)
# if we actually have a place to put our volume create it
# If we actually have a place to put our volume create it
if folder is None:
LOG.error(_LE('Unable to create folder %s'),
volfolder)
@ -421,14 +420,16 @@ class StorageCenterApi(object):
{'sn': ssn,
'name': name,
'id': instanceid})
if name is None and instanceid is None:
return None
pf = PayloadFilter()
# we need at least a name and or an instance id. If we have
# that we can find a volume.
pf.append('scSerialNumber', ssn)
pf.append('Name', name)
pf.append('instanceId', instanceid)
# We need at least a name and or an instance id. If we have
# that we can find a volume.
if instanceid is not None:
pf.append('instanceId', instanceid)
elif name is not None:
pf.append('Name', name)
else:
return None
r = self.client.post('StorageCenter/ScVolume/GetList',
pf.payload)
if r.status_code != 200:
@ -491,7 +492,7 @@ class StorageCenterApi(object):
return False
return True
# we do not know that we are red hat linux 6.x but that works
# We do not know that we are red hat linux 6.x but that works
# best for red hat and ubuntu. So, there.
def _find_serveros(self, ssn, osname='Red Hat Linux 6.x'):
'''Returns the serveros instance id of the specified osname.
@ -506,7 +507,7 @@ class StorageCenterApi(object):
for srvos in oslist:
name = srvos.get('name', 'nope')
if name.lower() == osname.lower():
# found it return the id
# Found it return the id
return self._get_id(srvos)
LOG.warning(_LW('ScServerOperatingSystem GetList return: %(c)d %(r)s'),
@ -518,9 +519,9 @@ class StorageCenterApi(object):
'''Same as create_server except it can take a list of hbas. hbas
can be wwns or iqns.
'''
# add hbas
# Add hbas
scserver = None
# our instance names
# Our instance names
for wwn in wwns:
if scserver is None:
# Use the fist wwn to create the server.
@ -529,7 +530,7 @@ class StorageCenterApi(object):
wwn,
True)
else:
# add the wwn to our server
# Add the wwn to our server
self._add_hba(scserver,
wwn,
True)
@ -544,21 +545,21 @@ class StorageCenterApi(object):
payload['Name'] = 'Server_' + wwnoriscsiname
payload['StorageCenter'] = ssn
payload['Notes'] = self.notes
# we pick Red Hat Linux 6.x because it supports multipath and
# We pick Red Hat Linux 6.x because it supports multipath and
# will attach luns to paths as they are found.
scserveros = self._find_serveros(ssn, 'Red Hat Linux 6.x')
if scserveros is not None:
payload['OperatingSystem'] = scserveros
# find our folder or make it
# Find our folder or make it
folder = self._find_server_folder(ssn,
foldername)
if folder is None:
folder = self._create_server_folder_path(ssn,
foldername)
# at this point it doesn't matter if the folder was created or not
# we just attempt to create the server. let it be in the root if
# At this point it doesn't matter if the folder was created or not.
# We just attempt to create the server. Let it be in the root if
# the folder creation fails.
if folder is not None:
payload['ServerFolder'] = self._get_id(folder)
@ -572,10 +573,10 @@ class StorageCenterApi(object):
'c': r.status_code,
'r': r.reason})
else:
# server was created
# Server was created
scserver = self._first_result(r)
# add hba to our server
# Add hba to our server
if scserver is not None:
if not self._add_hba(scserver,
wwnoriscsiname,
@ -584,7 +585,7 @@ class StorageCenterApi(object):
# Can't have a server without an HBA
self._delete_server(scserver)
scserver = None
# success or failure is determined by the caller
# Success or failure is determined by the caller
return scserver
def find_server(self, ssn, instance_name):
@ -594,9 +595,9 @@ class StorageCenterApi(object):
If found, the server the HBA is attached to, if any, is returned.
'''
scserver = None
# we search for our server by first finding our HBA
# We search for our server by first finding our HBA
hba = self._find_serverhba(ssn, instance_name)
# once created hbas stay in the system. So it isn't enough
# Once created hbas stay in the system. So it isn't enough
# that we found one it actually has to be attached to a
# server.
if hba is not None and hba.get('server') is not None:
@ -623,7 +624,7 @@ class StorageCenterApi(object):
If found, the sc server HBA is returned.
'''
scserverhba = None
# we search for our server by first finding our HBA
# We search for our server by first finding our HBA
pf = PayloadFilter()
pf.append('scSerialNumber', ssn)
pf.append('instanceName', instance_name)
@ -637,20 +638,12 @@ class StorageCenterApi(object):
scserverhba = self._first_result(r)
return scserverhba
def _find_domain(self, cportid, domainip):
'''Returns the fault domain which a given controller port can
be seen by the server
'''
def _find_domains(self, cportid):
r = self.client.get('StorageCenter/ScControllerPort/%s/FaultDomainList'
% cportid)
if r.status_code == 200:
domains = self._get_json(r)
# wiffle through the domains looking for our
# configured ip
for domain in domains:
# if this is us we return the port
if domain.get('wellKnownIpAddress') == domainip:
return domain
return domains
else:
LOG.debug('FaultDomainList error: %(c)d %(r)s',
{'c': r.status_code,
@ -658,6 +651,21 @@ class StorageCenterApi(object):
LOG.error(_LE('Error getting FaultDomainList'))
return None
def _find_domain(self, cportid, domainip):
'''Returns the fault domain which a given controller port can
be seen by the server
'''
domains = self._find_domains(cportid)
if domains:
# Wiffle through the domains looking for our
# configured ip.
for domain in domains:
# If this is us we return the port.
if domain.get('targetIpv4Address',
domain.get('wellKnownIpAddress')) == domainip:
return domain
return None
def _find_fc_initiators(self, scserver):
'''_find_fc_initiators
@ -670,8 +678,8 @@ class StorageCenterApi(object):
hbas = self._get_json(r)
for hba in hbas:
wwn = hba.get('instanceName')
if hba.get('portType') == 'FibreChannel' and\
wwn is not None:
if (hba.get('portType') == 'FibreChannel' and
wwn is not None):
initiators.append(wwn)
else:
LOG.debug('HbaList error: %(c)d %(r)s',
@ -730,21 +738,21 @@ class StorageCenterApi(object):
def find_wwns(self, scvolume, scserver):
'''returns the lun and wwns of the mapped volume'''
# our returnables
# Our returnables
lun = None # our lun. We return the first lun.
wwns = [] # list of targets
itmap = {} # dict of initiators and the associated targets
# make sure we know our server's initiators. Only return
# Make sure we know our server's initiators. Only return
# mappings that contain HBA for this server.
initiators = self._find_fc_initiators(scserver)
# get our volume mappings
# Get our volume mappings
mappings = self._find_mappings(scvolume)
if len(mappings) > 0:
# we check each of our mappings. We want to return
# We check each of our mappings. We want to return
# the mapping we have been configured to use.
for mapping in mappings:
# find the controller port for this mapping
# Find the controller port for this mapping
cport = mapping.get('controllerPort')
controllerport = self._find_controller_port(
self._get_id(cport))
@ -774,36 +782,54 @@ class StorageCenterApi(object):
# pretend we succeeded.
return lun, wwns, itmap
def find_iqn(self, scvolume, ip):
'''find_iqn
returns the iqn of the port pointed to by the openstack compute
node. This is to make sure that the compute node looks for the
volume on the active controller.
'''
iqn = None
# get our volume mappings
def find_iscsi_properties(self, scvolume, ip=None, port=None):
luns = []
iqns = []
portals = []
access_mode = 'rw'
mappings = self._find_mappings(scvolume)
if len(mappings) > 0:
# we check each of our mappings. We want to return
# the mapping we have been configured to use.
for mapping in mappings:
# find the controller port for this mapping
cport = mapping.get('controllerPort')
cportid = self._get_id(cport)
domain = self._find_domain(cportid,
ip)
if domain is not None:
domains = self._find_domains(cportid)
if domains:
controllerport = self._find_controller_port(cportid)
if controllerport is not None:
iqn = controllerport.get('iscsiName')
break
else:
LOG.error(_LE('Find_iqn: Volume appears unmapped'))
# TODO(tom_swanson): if we have nothing to return raise an exception
# here. We can't do anything with an unmapped volume. We shouldn't
# pretend we succeeded.
return iqn
appendproperties = False
for d in domains:
ipaddress = d.get('targetIpv4Address',
d.get('wellKnownIpAddress'))
portnumber = d.get('portNumber')
if ((ip is None or ip == ipaddress) and
(port is None or port == portnumber)):
portal = (ipaddress + ':' +
six.text_type(portnumber))
# I'm not sure when we can have more than
# one portal for a domain but since it is an
# array being returned it is best to check.
if portals.count(portal) == 0:
appendproperties = True
portals.append(portal)
# We do not report lun and iqn info unless it is for
# the configured port OR the user has not enabled
# multipath. (In which case ip and port sent in
# will be None).
if appendproperties is True:
iqns.append(controllerport.get('iscsiName'))
luns.append(mapping.get('lun'))
if mapping['readOnly'] is True:
access_mode = 'ro'
data = {'target_discovered': False,
'target_iqns': iqns,
'target_portals': portals,
'target_luns': luns,
'access_mode': access_mode
}
return data
def map_volume(self, scvolume, scserver):
'''map_volume
@ -811,7 +837,7 @@ class StorageCenterApi(object):
The check for server existence is elsewhere; does not create the
server.
'''
# make sure we have what we think we have
# Make sure we have what we think we have
serverid = self._get_id(scserver)
volumeid = self._get_id(scvolume)
if serverid is not None and volumeid is not None:
@ -824,13 +850,13 @@ class StorageCenterApi(object):
% volumeid,
payload)
if r.status_code == 200:
# we just return our mapping
# We just return our mapping
return self._first_result(r)
# Should not be here.
LOG.debug('MapToServer error: %(c)d %(r)s',
{'c': r.status_code,
'r': r.reason})
# error out
# Error out
LOG.error(_LE('Unable to map %(vol)s to %(srv)s'),
{'vol': scvolume['name'],
'srv': scserver['name']})
@ -932,7 +958,7 @@ class StorageCenterApi(object):
% self._get_id(scvolume))
try:
content = self._get_json(r)
# this will be a list. If it isn't bail
# This will be a list. If it isn't bail
if isinstance(content, list):
for r in content:
# The only place to save our information with the public
@ -941,9 +967,9 @@ class StorageCenterApi(object):
# the max length and we compare that to the start of
# the snapshot id.
description = r.get('description')
if len(description) >= 30 and \
replayid.startswith(description) is True and \
r.get('markedForExpiration') is not True:
if (len(description) >= 30 and
replayid.startswith(description) is True and
r.get('markedForExpiration') is not True):
replay = r
break
except Exception:
@ -990,7 +1016,7 @@ class StorageCenterApi(object):
folder = self._find_volume_folder(ssn,
volfolder)
# doesn't exist? make it
# Doesn't exist? make it
if folder is None:
folder = self._create_volume_folder_path(ssn,
volfolder)

View File

@ -38,14 +38,16 @@ class DellStorageCenterISCSIDriver(san.SanISCSIDriver,
def __init__(self, *args, **kwargs):
super(DellStorageCenterISCSIDriver, self).__init__(*args, **kwargs)
self.backend_name =\
self.configuration.safe_get('volume_backend_name') or 'Dell-iSCSI'
self.backend_name = (
self.configuration.safe_get('volume_backend_name')
or 'Dell-iSCSI')
def initialize_connection(self, volume, connector):
# We use id to name the volume name as it is a
# known unique name.
volume_name = volume.get('id')
initiator_name = connector.get('initiator')
multipath = connector.get('multipath', False)
LOG.debug('initialize_ connection: %(n)s:%(i)s',
{'n': volume_name,
'i': initiator_name})
@ -75,27 +77,44 @@ class DellStorageCenterISCSIDriver(san.SanISCSIDriver,
# our sc volume object.
scvolume = api.find_volume(ssn,
volume_name)
ip = self.configuration.iscsi_ip_address
port = self.configuration.iscsi_port
iqn = api.find_iqn(scvolume,
ip)
if iqn is None:
LOG.error(_LE('Volume mapped to invalid path.'))
if multipath:
# Just return our properties with all the mappings
iscsiproperties = (
api.find_iscsi_properties(scvolume,
None,
None))
return {'driver_volume_type': 'iscsi',
'data': iscsiproperties}
else:
# Only return the iqn for the user specified port.
ip = self.configuration.iscsi_ip_address
port = self.configuration.iscsi_port
iscsiproperties = (
api.find_iscsi_properties(scvolume,
ip,
port))
properties = {}
properties['target_discovered'] = False
properties['target_lun'] = mapping['lunUsed'][0]
if mapping['readOnly'] is True:
properties['access_mode'] = 'ro'
portals = iscsiproperties['target_portals']
# We'll key off of target_portals. If we have
# one listed we can assume that we found what
# we are looking for. Otherwise error.
if len(portals) > 0:
properties['target_portal'] = portals[0]
properties['target_iqn'] = (
iscsiproperties['target_iqns'][0])
properties['target_lun'] = (
iscsiproperties['target_luns'][0])
properties['access_mode'] = (
iscsiproperties['access_mode'])
LOG.debug(properties)
return {'driver_volume_type': 'iscsi',
'data': properties
}
else:
properties['access_mode'] = 'rw'
properties['target_portal'] = ip + ':' + str(port)
properties['target_iqn'] = iqn
LOG.debug(properties)
return {'driver_volume_type': 'iscsi',
'data': properties
}
LOG.error(
_LE('Volume mapped to invalid path.'))
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Failed to initialize connection '