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()
'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",
"--- --------------- ------------- ---------- ---------- --------",
'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,
# 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',
'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,
'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,
'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,
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)
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,
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,
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'},
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',
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()
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')
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)
@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,
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)
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')
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')
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()
'_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 = {'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')
- expected_iqn = u'iqn.2002-03.com.compellent:5000d31000fcbe43'
- self.assertEqual(expected_iqn, res, 'Wrong IQN')
+ @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(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_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)
+ 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()
- 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=ISCSI_CTRLR_PORT)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
- '_find_domain',
+ '_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()
+ 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()
- 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', u'192.168.0.25:3260']}
+ self.assertEqual(expected, res, 'Wrong Target Info')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_first_result',
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()
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)
import os.path
import requests
+import six
from cinder import exception
from cinder.i18n import _, _LE, _LI, _LW
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):
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:
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.
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,
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
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 != '':
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,
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)
{'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:
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.
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'),
'''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.
wwn,
True)
else:
- # add the wwn to our server
+ # Add the wwn to our server
self._add_hba(scserver,
wwn,
True)
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)
'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,
# 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):
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:
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)
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,
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
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',
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))
# 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
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:
% 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']})
% 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
# 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:
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)