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:
parent
260701bacc
commit
8b2bfe46b5
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 '
|
||||
|
|
Loading…
Reference in New Issue