Merge "Dell SC: Fix legacy bug, init_conn bug and REST API bug."
This commit is contained in:
commit
4f197feb0d
@ -474,29 +474,6 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
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'controller': {u'instanceId': u'64702.64703',
|
||||
u'instanceName': u'SN 64703',
|
||||
u'objectType': u'ScController'},
|
||||
u'lunUsed': [1],
|
||||
u'server': {u'instanceId': u'64702.47',
|
||||
u'instanceName': u'Server_21000024ff30441d',
|
||||
u'objectType': u'ScPhysicalServer'},
|
||||
u'volume':
|
||||
{u'instanceId': u'64702.6025',
|
||||
u'instanceName': u'Server_21000024ff30441d Test Vol',
|
||||
u'objectType': u'ScVolume'},
|
||||
u'connectivity': u'Up',
|
||||
u'readOnly': False,
|
||||
u'objectType': u'ScMappingProfile',
|
||||
u'hostCache': False,
|
||||
u'mappedVia': u'Server',
|
||||
u'mapCount': 3,
|
||||
u'instanceName': u'6025-47',
|
||||
u'lunRequested': u'N/A'}]
|
||||
|
||||
MAP_PROFILE = {u'instanceId': u'64702.2941',
|
||||
u'scName': u'Storage Center 64702',
|
||||
u'scSerialNumber': 64702,
|
||||
@ -520,6 +497,8 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
u'instanceName': u'6025-47',
|
||||
u'lunRequested': u'N/A'}
|
||||
|
||||
MAP_PROFILES = [MAP_PROFILE]
|
||||
|
||||
MAPPINGS = [{u'profile': {u'instanceId': u'64702.104',
|
||||
u'instanceName': u'92-30',
|
||||
u'objectType': u'ScMappingProfile'},
|
||||
@ -1473,6 +1452,57 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
u'userCreated': False,
|
||||
u'volumeCount': 0}]
|
||||
|
||||
ISCSI_CONFIG = {
|
||||
u'initialReadyToTransfer': True,
|
||||
u'scSerialNumber': 64065,
|
||||
u'macAddress': u'00c0dd-1da173',
|
||||
u'instanceId': u'64065.5764839588723573038.6',
|
||||
u'vlanTagging': False,
|
||||
u'mapCount': 8,
|
||||
u'cardModel': u'Qle4062',
|
||||
u'portNumber': 3260,
|
||||
u'firstBurstSize': 256,
|
||||
u'deviceName': u'PCIDEV09',
|
||||
u'subnetMask': u'255.255.255.0',
|
||||
u'speed': u'1 Gbps',
|
||||
u'maximumVlanCount': 0,
|
||||
u'gatewayIpAddress': u'192.168.0.1',
|
||||
u'slot': 4,
|
||||
u'sfpData': u'',
|
||||
u'dataDigest': False,
|
||||
u'chapEnabled': False,
|
||||
u'firmwareVersion': u'03.00.01.77',
|
||||
u'preferredControllerIndex': 64066,
|
||||
u'defaultTimeToRetain': 20,
|
||||
u'objectType': u'ScControllerPortIscsiConfiguration',
|
||||
u'instanceName': u'5000d31000FCBE43',
|
||||
u'scName': u'sc64065',
|
||||
u'revision': u'0',
|
||||
u'controllerPortIndex': 5764839588723573038,
|
||||
u'maxBurstSize': 512,
|
||||
u'targetCount': 20,
|
||||
u'description': u'QLogic QLE4062 iSCSI Adapter Rev 0 Copper',
|
||||
u'vlanSupported': True,
|
||||
u'chapName': u'iqn.2002-03.com.compellent:5000d31000fcbe43',
|
||||
u'windowSize': 128,
|
||||
u'vlanId': 0,
|
||||
u'defaultTimeToWait': 2,
|
||||
u'headerDigest': False,
|
||||
u'slotPort': 2,
|
||||
u'immediateDataWrite': False,
|
||||
u'storageCenterTargetCount': 20,
|
||||
u'vlanCount': 0,
|
||||
u'scsiCommandTimeout': 60,
|
||||
u'slotType': u'PCI4',
|
||||
u'ipAddress': u'192.168.0.21',
|
||||
u'vlanUserPriority': 0,
|
||||
u'bothCount': 0,
|
||||
u'initiatorCount': 33,
|
||||
u'keepAliveTimeout': 30,
|
||||
u'homeControllerIndex': 64066,
|
||||
u'chapSecret': u'',
|
||||
u'maximumTransmissionUnit': 1500}
|
||||
|
||||
IQN = 'iqn.2002-03.com.compellent:5000D31000000001'
|
||||
WWN = u'21000024FF30441C'
|
||||
|
||||
@ -2839,6 +2869,37 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
self.assertTrue(mock_get_json.called)
|
||||
self.assertEqual([], res, 'Mapping count mismatch')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
return_value=MAP_PROFILES)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_find_mapping_profiles(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
# Test case where ScVolume has no mappings
|
||||
res = self.scapi._find_mapping_profiles(self.VOLUME)
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertTrue(mock_get_json.called)
|
||||
self.assertEqual(self.MAP_PROFILES, res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_400)
|
||||
def test_find_mapping_profiles_error(self,
|
||||
mock_get,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
# Test case where ScVolume has no mappings
|
||||
res = self.scapi._find_mapping_profiles(self.VOLUME)
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertEqual([], res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_first_result',
|
||||
return_value=CTRLR_PORT)
|
||||
@ -3075,7 +3136,11 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=True)
|
||||
def test_find_iscsi_properties_mappings(self,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_domains,
|
||||
mock_find_ctrl_port,
|
||||
@ -3084,6 +3149,7 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.find_iscsi_properties(self.VOLUME)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_domains.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
@ -3112,7 +3178,11 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=True)
|
||||
def test_find_iscsi_properties_by_address(self,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_domains,
|
||||
mock_find_ctrl_port,
|
||||
@ -3123,6 +3193,7 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
# Test case to find iSCSI mappings by IP Address & port
|
||||
res = self.scapi.find_iscsi_properties(
|
||||
self.VOLUME, '192.168.0.21', 3260)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_domains.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
@ -3151,17 +3222,23 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@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_domains,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_ctrl,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=True)
|
||||
def test_find_iscsi_properties_by_address_not_found(
|
||||
self,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_domains,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_ctrl,
|
||||
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)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_domains.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
@ -3204,7 +3281,11 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=True)
|
||||
def test_find_iscsi_properties_no_domain(self,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_domains,
|
||||
mock_find_ctrl_port,
|
||||
@ -3216,6 +3297,7 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.scapi.find_iscsi_properties,
|
||||
self.VOLUME)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_domains.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
@ -3227,15 +3309,15 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@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)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=True)
|
||||
def test_find_iscsi_properties_no_ctrl_port(self,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_domains,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_controller,
|
||||
mock_close_connection,
|
||||
@ -3245,8 +3327,8 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.scapi.find_iscsi_properties,
|
||||
self.VOLUME)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_domains.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_active_controller.called)
|
||||
|
||||
@ -3262,7 +3344,11 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS_READ_ONLY)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=True)
|
||||
def test_find_iscsi_properties_ro(self,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_domains,
|
||||
mock_find_ctrl_port,
|
||||
@ -3272,6 +3358,7 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
mock_init):
|
||||
# Test case where Read Only mappings are found
|
||||
res = self.scapi.find_iscsi_properties(self.VOLUME)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_domains.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
@ -3300,7 +3387,11 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS_MULTI_PORTAL)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=True)
|
||||
def test_find_iscsi_properties_multi_portals(self,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_domains,
|
||||
mock_find_ctrl_port,
|
||||
@ -3314,6 +3405,7 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
self.assertTrue(mock_find_domains.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_active_controller.called)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
expected = {'access_mode': 'rw',
|
||||
'target_discovered': False,
|
||||
'target_iqn':
|
||||
@ -3332,13 +3424,275 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
u'192.168.0.25:3260']}
|
||||
self.assertEqual(expected, res, 'Wrong Target Info')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_active_controller',
|
||||
return_value='64702.5764839588723736131.91')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port',
|
||||
return_value=ISCSI_CTRLR_PORT)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=False)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port_iscsi_config',
|
||||
return_value=ISCSI_CONFIG)
|
||||
def test_find_iscsi_properties_mappings_legacy(
|
||||
self,
|
||||
mock_find_controller_port_iscsi_config,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_controller,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.find_iscsi_properties(self.VOLUME)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_controller_port_iscsi_config.called)
|
||||
self.assertTrue(mock_find_active_controller.called)
|
||||
expected = {'access_mode': 'rw',
|
||||
'target_discovered': False,
|
||||
'target_iqn':
|
||||
u'iqn.2002-03.com.compellent:5000d31000fcbe43',
|
||||
'target_iqns':
|
||||
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
|
||||
'target_lun': 1,
|
||||
'target_luns': [1],
|
||||
'target_portal': u'192.168.0.21:3260',
|
||||
'target_portals': [u'192.168.0.21:3260']}
|
||||
self.assertEqual(expected, res, 'Wrong Target Info')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_active_controller',
|
||||
return_value='64702.5764839588723736131.91')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port',
|
||||
return_value=ISCSI_CTRLR_PORT)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=False)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port_iscsi_config',
|
||||
return_value=None)
|
||||
def test_find_iscsi_properties_mappings_legacy_no_iscsi_config(
|
||||
self,
|
||||
mock_find_controller_port_iscsi_config,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_controller,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.scapi.find_iscsi_properties,
|
||||
self.VOLUME)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_controller_port_iscsi_config.called)
|
||||
self.assertTrue(mock_find_active_controller.called)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_active_controller',
|
||||
return_value='64702.64702')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port',
|
||||
return_value=ISCSI_CTRLR_PORT)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=False)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port_iscsi_config',
|
||||
return_value=ISCSI_CONFIG)
|
||||
def test_find_iscsi_properties_by_address_legacy(
|
||||
self,
|
||||
mock_find_controller_port_iscsi_config,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_controller,
|
||||
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)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_active_controller.called)
|
||||
self.assertTrue(mock_find_controller_port_iscsi_config.called)
|
||||
expected = {'access_mode': 'rw',
|
||||
'target_discovered': False,
|
||||
'target_iqn':
|
||||
u'iqn.2002-03.com.compellent:5000d31000fcbe43',
|
||||
'target_iqns':
|
||||
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
|
||||
'target_lun': 1,
|
||||
'target_luns': [1],
|
||||
'target_portal': u'192.168.0.21:3260',
|
||||
'target_portals': [u'192.168.0.21:3260']}
|
||||
self.assertEqual(expected, res, 'Wrong Target Info')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_active_controller',
|
||||
return_value='64702.64702')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port',
|
||||
return_value=ISCSI_CTRLR_PORT)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=False)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port_iscsi_config',
|
||||
return_value=ISCSI_CONFIG)
|
||||
def test_find_iscsi_properties_by_address_not_found_legacy(
|
||||
self,
|
||||
mock_find_controller_port_iscsi_config,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_ctrl,
|
||||
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)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_active_ctrl.called)
|
||||
self.assertTrue(mock_find_controller_port_iscsi_config.called)
|
||||
expected = {'access_mode': 'rw',
|
||||
'target_discovered': False,
|
||||
'target_iqn':
|
||||
u'iqn.2002-03.com.compellent:5000d31000fcbe43',
|
||||
'target_iqns':
|
||||
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
|
||||
'target_lun': 1,
|
||||
'target_luns': [1],
|
||||
'target_portal': u'192.168.0.21:3260',
|
||||
'target_portals': [u'192.168.0.21:3260']}
|
||||
self.assertEqual(expected, res, 'Wrong Target Info')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_active_controller',
|
||||
return_value='64702.64702')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port',
|
||||
return_value=ISCSI_CTRLR_PORT)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS_READ_ONLY)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=False)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port_iscsi_config',
|
||||
return_value=ISCSI_CONFIG)
|
||||
def test_find_iscsi_properties_ro_legacy(self,
|
||||
mock_find_iscsi_config,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_controller,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
# Test case where Read Only mappings are found
|
||||
res = self.scapi.find_iscsi_properties(self.VOLUME)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_active_controller.called)
|
||||
self.assertTrue(mock_find_iscsi_config.called)
|
||||
expected = {'access_mode': 'ro',
|
||||
'target_discovered': False,
|
||||
'target_iqn':
|
||||
u'iqn.2002-03.com.compellent:5000d31000fcbe43',
|
||||
'target_iqns':
|
||||
[u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
|
||||
'target_lun': 1,
|
||||
'target_luns': [1],
|
||||
'target_portal': u'192.168.0.21:3260',
|
||||
'target_portals': [u'192.168.0.21:3260']}
|
||||
self.assertEqual(expected, res, 'Wrong Target Info')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_active_controller',
|
||||
return_value='64702.64702')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port',
|
||||
return_value=ISCSI_CTRLR_PORT)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mappings',
|
||||
return_value=MAPPINGS_MULTI_PORTAL)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_is_virtualport_mode',
|
||||
return_value=False)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_controller_port_iscsi_config',
|
||||
return_value=ISCSI_CONFIG)
|
||||
def test_find_iscsi_properties_multi_portals_legacy(
|
||||
self,
|
||||
mock_find_controller_port_iscsi_config,
|
||||
mock_is_virtualport_mode,
|
||||
mock_find_mappings,
|
||||
mock_find_ctrl_port,
|
||||
mock_find_active_controller,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
# Test case where there are multiple portals
|
||||
res = self.scapi.find_iscsi_properties(self.VOLUME)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_find_ctrl_port.called)
|
||||
self.assertTrue(mock_find_active_controller.called)
|
||||
self.assertTrue(mock_is_virtualport_mode.called)
|
||||
self.assertTrue(mock_find_controller_port_iscsi_config.called)
|
||||
# Since we're feeding the same info back multiple times the information
|
||||
# will be duped.
|
||||
expected = {'access_mode': 'rw',
|
||||
'target_discovered': False,
|
||||
'target_iqn':
|
||||
u'iqn.2002-03.com.compellent:5000d31000fcbe43',
|
||||
'target_iqns':
|
||||
[u'iqn.2002-03.com.compellent:5000d31000fcbe43',
|
||||
u'iqn.2002-03.com.compellent:5000d31000fcbe43'],
|
||||
'target_lun': 1,
|
||||
'target_luns': [1, 1],
|
||||
'target_portal': u'192.168.0.21:3260',
|
||||
'target_portals': [u'192.168.0.21:3260',
|
||||
u'192.168.0.21:3260']}
|
||||
self.assertEqual(expected, res, 'Wrong Target Info')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_first_result',
|
||||
return_value=MAP_PROFILE)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'post',
|
||||
return_value=RESPONSE_200)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mapping_profiles',
|
||||
return_value=[])
|
||||
def test_map_volume(self,
|
||||
mock_find_mapping_profiles,
|
||||
mock_post,
|
||||
mock_first_result,
|
||||
mock_close_connection,
|
||||
@ -3346,6 +3700,54 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
mock_init):
|
||||
res = self.scapi.map_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertTrue(mock_find_mapping_profiles.called)
|
||||
self.assertTrue(mock_post.called)
|
||||
self.assertTrue(mock_first_result.called)
|
||||
self.assertEqual(self.MAP_PROFILE, res, 'Incorrect ScMappingProfile')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_first_result',
|
||||
return_value=MAP_PROFILE)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'post',
|
||||
return_value=RESPONSE_200)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mapping_profiles',
|
||||
return_value=MAP_PROFILES)
|
||||
def test_map_volume_existing_mapping(self,
|
||||
mock_find_mappings,
|
||||
mock_post,
|
||||
mock_first_result,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.map_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertFalse(mock_post.called)
|
||||
self.assertFalse(mock_first_result.called)
|
||||
self.assertEqual(self.MAP_PROFILE, res, 'Incorrect ScMappingProfile')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_first_result',
|
||||
return_value=MAP_PROFILE)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'post',
|
||||
return_value=RESPONSE_200)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mapping_profiles',
|
||||
return_value=[])
|
||||
def test_map_volume_existing_mapping_not_us(self,
|
||||
mock_find_mappings,
|
||||
mock_post,
|
||||
mock_first_result,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
server = {'instanceId': 64702.48}
|
||||
res = self.scapi.map_volume(self.VOLUME,
|
||||
server)
|
||||
self.assertTrue(mock_find_mappings.called)
|
||||
self.assertTrue(mock_post.called)
|
||||
self.assertTrue(mock_first_result.called)
|
||||
self.assertEqual(self.MAP_PROFILE, res, 'Incorrect ScMappingProfile')
|
||||
@ -3395,7 +3797,11 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'post',
|
||||
return_value=RESPONSE_204)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mapping_profiles',
|
||||
return_value=[])
|
||||
def test_map_volume_failure(self,
|
||||
mock_find_mapping_profiles,
|
||||
mock_post,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
@ -3403,6 +3809,7 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
# Test case where mapping volume to server fails
|
||||
res = self.scapi.map_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertTrue(mock_find_mapping_profiles.called)
|
||||
self.assertTrue(mock_post.called)
|
||||
self.assertIsNone(res, 'None expected')
|
||||
|
||||
@ -3410,76 +3817,66 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
'delete',
|
||||
return_value=RESPONSE_200)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
'_find_mapping_profiles',
|
||||
return_value=MAP_PROFILES)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_unmap_volume(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_find_mapping_profiles,
|
||||
mock_delete,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.unmap_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertTrue(mock_get_json.called)
|
||||
self.assertTrue(mock_find_mapping_profiles.called)
|
||||
self.assertTrue(mock_delete.called)
|
||||
self.assertTrue(res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_mapping_profiles',
|
||||
return_value=MAP_PROFILES)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
'delete',
|
||||
return_value=RESPONSE_204)
|
||||
def test_unmap_volume_failure(self,
|
||||
mock_get,
|
||||
mock_delete,
|
||||
mock_find_mapping_profiles,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.unmap_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertTrue(mock_find_mapping_profiles.called)
|
||||
self.assertTrue(mock_delete.called)
|
||||
self.assertFalse(res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
'_find_mapping_profiles',
|
||||
return_value=[])
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_unmap_volume_no_map_profile(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_find_mapping_profiles,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.unmap_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertTrue(mock_get_json.called)
|
||||
self.assertTrue(mock_find_mapping_profiles.called)
|
||||
self.assertTrue(res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'delete',
|
||||
return_value=RESPONSE_204)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
'_find_mapping_profiles',
|
||||
return_value=MAP_PROFILES)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_unmap_volume_del_fail(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_find_mapping_profiles,
|
||||
mock_delete,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.unmap_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertTrue(mock_get_json.called)
|
||||
self.assertTrue(mock_find_mapping_profiles.called)
|
||||
self.assertTrue(mock_delete.called)
|
||||
self.assertFalse(res, False)
|
||||
|
||||
@ -3489,14 +3886,10 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
'delete',
|
||||
return_value=RESPONSE_200)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
'_find_mapping_profiles',
|
||||
return_value=MAP_PROFILES)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_unmap_volume_no_vol_id(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_find_mapping_profiles,
|
||||
mock_delete,
|
||||
mock_get_id,
|
||||
mock_close_connection,
|
||||
@ -3506,8 +3899,7 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
mock_get_id.side_effect = [None, '64702.47']
|
||||
res = self.scapi.unmap_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertFalse(mock_get.called)
|
||||
self.assertFalse(mock_get_json.called)
|
||||
self.assertFalse(mock_find_mapping_profiles.called)
|
||||
self.assertFalse(mock_delete.called)
|
||||
self.assertTrue(res)
|
||||
|
||||
@ -3517,14 +3909,10 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
'delete',
|
||||
return_value=RESPONSE_200)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
'_find_mapping_profiles',
|
||||
return_value=MAP_PROFILES)
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_unmap_volume_no_server_id(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_find_mapping_profiles,
|
||||
mock_delete,
|
||||
mock_get_id,
|
||||
mock_close_connection,
|
||||
@ -3534,11 +3922,41 @@ class DellSCSanAPITestCase(test.TestCase):
|
||||
mock_get_id.side_effect = ['64702.3494', None]
|
||||
res = self.scapi.unmap_volume(self.VOLUME,
|
||||
self.SCSERVER)
|
||||
self.assertFalse(mock_get.called)
|
||||
self.assertFalse(mock_get_json.called)
|
||||
self.assertFalse(mock_find_mapping_profiles.called)
|
||||
self.assertFalse(mock_delete.called)
|
||||
self.assertTrue(res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
return_value=[{'a': 1}, {'a': 2}])
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_find_controller_port_iscsi_config(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
# Not much to test here. Just make sure we call our stuff and
|
||||
# that we return the first item returned to us.
|
||||
res = self.scapi._find_controller_port_iscsi_config('guid')
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertTrue(mock_get_json.called)
|
||||
self.assertEqual({'a': 1}, res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_400)
|
||||
def test_find_controller_port_iscsi_config_err(self,
|
||||
mock_get,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi._find_controller_port_iscsi_config('guid')
|
||||
self.assertTrue(mock_get.called)
|
||||
self.assertEqual(None, res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
return_value=STRG_USAGE)
|
||||
@ -5234,6 +5652,29 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
|
||||
response_nc.reason = u'duplicate'
|
||||
RESPONSE_204 = response_nc
|
||||
|
||||
APIDICT = {u'instanceId': u'0',
|
||||
u'hostName': u'192.168.0.200',
|
||||
u'userId': 434226,
|
||||
u'connectionKey': u'',
|
||||
u'minApiVersion': u'0.1',
|
||||
u'webServicesPort': 3033,
|
||||
u'locale': u'en_US',
|
||||
u'objectType': u'ApiConnection',
|
||||
u'secureString': u'',
|
||||
u'applicationVersion': u'2.0.1',
|
||||
u'source': u'REST',
|
||||
u'commandLine': False,
|
||||
u'application': u'Cinder REST Driver',
|
||||
u'sessionKey': 1436460614863,
|
||||
u'provider': u'EnterpriseManager',
|
||||
u'instanceName': u'ApiConnection',
|
||||
u'connected': True,
|
||||
u'userName': u'Admin',
|
||||
u'useHttps': False,
|
||||
u'providerVersion': u'15.3.1.186',
|
||||
u'apiVersion': u'2.2',
|
||||
u'apiBuild': 199}
|
||||
|
||||
def setUp(self):
|
||||
super(DellSCSanAPIConnectionTestCase, self).setUp()
|
||||
|
||||
@ -5278,7 +5719,11 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'post',
|
||||
return_value=RESPONSE_200)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
return_value=APIDICT)
|
||||
def test_open_connection(self,
|
||||
mock_get_json,
|
||||
mock_post):
|
||||
self.scapi.open_connection()
|
||||
self.assertTrue(mock_post.called)
|
||||
|
@ -33,16 +33,34 @@ class PayloadFilter(object):
|
||||
'''PayloadFilter
|
||||
|
||||
Simple class for creating filters for interacting with the Dell
|
||||
Storage API.
|
||||
|
||||
Note that this defaults to "AND" filter types. This is a pretty limited
|
||||
class. It only does the trivial filters required for this driver.
|
||||
Storage API DropTop2 and later.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, filtertype='AND'):
|
||||
self.payload = {}
|
||||
self.payload['filterType'] = 'AND'
|
||||
self.payload['filters'] = []
|
||||
self.payload['filter'] = {'filterType': filtertype,
|
||||
'filters': []}
|
||||
|
||||
def append(self, name, val, filtertype='Equals'):
|
||||
if val is not None:
|
||||
apifilter = {}
|
||||
apifilter['attributeName'] = name
|
||||
apifilter['attributeValue'] = val
|
||||
apifilter['filterType'] = filtertype
|
||||
self.payload['filter']['filters'].append(apifilter)
|
||||
|
||||
|
||||
class LegacyPayloadFilter(object):
|
||||
|
||||
'''LegacyPayloadFilter
|
||||
|
||||
Simple class for creating filters for interacting with the Dell
|
||||
Storage API pre DropTop2.
|
||||
'''
|
||||
|
||||
def __init__(self, filter_type='AND'):
|
||||
self.payload = {'filterType': filter_type,
|
||||
'filters': []}
|
||||
|
||||
def append(self, name, val, filtertype='Equals'):
|
||||
if val is not None:
|
||||
@ -167,9 +185,18 @@ class StorageCenterApi(object):
|
||||
'''StorageCenterApi
|
||||
|
||||
Handles calls to Dell Enterprise Manager (EM) via the REST API interface.
|
||||
'''
|
||||
|
||||
APIVERSION = '2.0.1'
|
||||
Version history:
|
||||
1.0.0 - Initial driver
|
||||
1.1.0 - Added extra spec support for Storage Profile selection
|
||||
1.2.0 - Added consistency group support.
|
||||
2.0.0 - Switched to inheriting functional objects rather than volume
|
||||
driver.
|
||||
2.1.0 - Added support for ManageableVD.
|
||||
2.2.0 - Added API 2.2 support.
|
||||
2.3.0 - Added Legacy Port Mode Support
|
||||
'''
|
||||
APIVERSION = '2.3.0'
|
||||
|
||||
def __init__(self, host, port, user, password, verify):
|
||||
'''This creates a connection to Dell Enterprise Manager.
|
||||
@ -185,6 +212,8 @@ class StorageCenterApi(object):
|
||||
self.ssn = None
|
||||
self.vfname = 'openstack'
|
||||
self.sfname = 'openstack'
|
||||
self.legacypayloadfilters = False
|
||||
self.consisgroups = True
|
||||
self.client = HttpClient(host,
|
||||
port,
|
||||
user,
|
||||
@ -286,6 +315,12 @@ class StorageCenterApi(object):
|
||||
blob)
|
||||
return None
|
||||
|
||||
def _get_payload_filter(self, filterType='AND'):
|
||||
# 2.1 or earlier and we are talking LegacyPayloadFilters.
|
||||
if self.legacypayloadfilters:
|
||||
return LegacyPayloadFilter(filterType)
|
||||
return PayloadFilter(filterType)
|
||||
|
||||
def open_connection(self):
|
||||
'''Authenticate against Dell Enterprise Manager.
|
||||
|
||||
@ -297,13 +332,41 @@ class StorageCenterApi(object):
|
||||
payload['ApplicationVersion'] = self.APIVERSION
|
||||
r = self.client.post('ApiConnection/Login',
|
||||
payload)
|
||||
# TODO(Swanson): If we get a 400 back we should also print the text.
|
||||
if r.status_code != 200:
|
||||
|
||||
if r.status_code == 200:
|
||||
# We should be logged in. Try to grab the api version out of the
|
||||
# response.
|
||||
try:
|
||||
apidict = self._get_json(r)
|
||||
version = apidict['apiVersion']
|
||||
splitver = version.split('.')
|
||||
if splitver[0] == '2':
|
||||
if splitver[1] == '0':
|
||||
self.consisgroups = False
|
||||
self.legacypayloadfilters = True
|
||||
|
||||
elif splitver[1] == '1':
|
||||
self.legacypayloadfilters = True
|
||||
return
|
||||
|
||||
except Exception:
|
||||
# Good return but not the login response we were expecting.
|
||||
# Log it and error out.
|
||||
LOG.error(_LE('Unrecognized Login Response: %s'), r)
|
||||
else:
|
||||
# Call error.
|
||||
LOG.error(_LE('Login error: %(code)d %(reason)s'),
|
||||
{'code': r.status_code,
|
||||
'reason': r.reason})
|
||||
raise exception.VolumeBackendAPIException(
|
||||
_('Failed to connect to Enterprise Manager'))
|
||||
|
||||
# Bad request.
|
||||
# TODO(Swanson): Should add this to all returns.
|
||||
if r.status_code == 400:
|
||||
LOG.debug('Bad Request. Return text: %s', r.text)
|
||||
|
||||
# If we fell to this point then raise an exception.
|
||||
raise exception.VolumeBackendAPIException(
|
||||
_('Failed to connect to Enterprise Manager'))
|
||||
|
||||
def close_connection(self):
|
||||
'''Logout of Dell Enterprise Manager.'''
|
||||
@ -427,7 +490,7 @@ class StorageCenterApi(object):
|
||||
:param foldername: Full path to the folder we are looking for.
|
||||
:returns: Dell folder object.
|
||||
'''
|
||||
pf = PayloadFilter()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('scSerialNumber', self.ssn)
|
||||
basename = os.path.basename(foldername)
|
||||
pf.append('Name', basename)
|
||||
@ -476,7 +539,7 @@ class StorageCenterApi(object):
|
||||
Don't wig out if this fails.
|
||||
:param scvolume: Dell Volume object.
|
||||
'''
|
||||
pf = PayloadFilter()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('scSerialNumber', scvolume.get('scSerialNumber'), 'Equals')
|
||||
r = self.client.post('StorageCenter/ScServer/GetList', pf.payload)
|
||||
if r.status_code == 200:
|
||||
@ -521,7 +584,7 @@ class StorageCenterApi(object):
|
||||
# and look through for the one we want. Never many profiles, so
|
||||
# this doesn't cause as much overhead as it might seem.
|
||||
storage_profile = storage_profile.replace(' ', '').lower()
|
||||
pf = PayloadFilter()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('scSerialNumber', self.ssn, 'Equals')
|
||||
r = self.client.post(
|
||||
'StorageCenter/ScStorageProfile/GetList', pf.payload)
|
||||
@ -617,7 +680,7 @@ class StorageCenterApi(object):
|
||||
result = None
|
||||
# We need a name or a device ID to find a volume.
|
||||
if name or deviceid:
|
||||
pf = PayloadFilter()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('scSerialNumber', self.ssn)
|
||||
if name is not None:
|
||||
pf.append('Name', name)
|
||||
@ -773,7 +836,7 @@ class StorageCenterApi(object):
|
||||
:param osname: The name of the OS to look for.
|
||||
:returns: InstanceId of the ScServerOperatingSystem object.
|
||||
'''
|
||||
pf = PayloadFilter()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('scSerialNumber', self.ssn)
|
||||
r = self.client.post('StorageCenter/ScServerOperatingSystem/GetList',
|
||||
pf.payload)
|
||||
@ -888,7 +951,7 @@ class StorageCenterApi(object):
|
||||
# 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:
|
||||
pf = PayloadFilter()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('scSerialNumber', self.ssn)
|
||||
pf.append('instanceId', self._get_id(hba['server']))
|
||||
r = self.client.post('StorageCenter/ScServer/GetList',
|
||||
@ -916,7 +979,7 @@ class StorageCenterApi(object):
|
||||
'''
|
||||
scserverhba = None
|
||||
# We search for our server by first finding our HBA
|
||||
pf = PayloadFilter()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('scSerialNumber', self.ssn)
|
||||
pf.append('instanceName', instance_name)
|
||||
r = self.client.post('StorageCenter/ScServerHba/GetList',
|
||||
@ -1006,6 +1069,28 @@ class StorageCenterApi(object):
|
||||
LOG.error(_LE('_find_mappings: volume is not active'))
|
||||
return mappings
|
||||
|
||||
def _find_mapping_profiles(self, scvolume):
|
||||
'''Find the Dell volume object mapping profiles.
|
||||
|
||||
:param scvolume: Dell volume object.
|
||||
:returns: A list of Dell mapping profile objects.
|
||||
'''
|
||||
mapping_profiles = []
|
||||
if scvolume.get('active', False):
|
||||
r = self.client.get('StorageCenter/ScVolume/%s/MappingProfileList'
|
||||
% self._get_id(scvolume))
|
||||
if r.status_code == 200:
|
||||
mapping_profiles = self._get_json(r)
|
||||
else:
|
||||
LOG.debug('MappingProfileList error: %(code)d %(reason)s',
|
||||
{'code': r.status_code,
|
||||
'reason': r.reason})
|
||||
LOG.error(_LE('Unable to find volume mapping profiles: %s'),
|
||||
scvolume.get('name'))
|
||||
else:
|
||||
LOG.error(_LE('_find_mappings: volume is not active'))
|
||||
return mapping_profiles
|
||||
|
||||
def _find_controller_port(self, cportid):
|
||||
'''Finds the SC controller port object for the specified cportid.
|
||||
|
||||
@ -1124,6 +1209,38 @@ class StorageCenterApi(object):
|
||||
iqn = controllerport.get('iscsiName')
|
||||
return iqn
|
||||
|
||||
def _is_virtualport_mode(self):
|
||||
isvpmode = False
|
||||
r = self.client.get('StorageCenter/ScConfiguration/%s' % self.ssn)
|
||||
if r.status_code == 200:
|
||||
scconfig = self._get_json(r)
|
||||
if scconfig:
|
||||
isvpmode = True if (scconfig['iscsiTransportMode'] ==
|
||||
'VirtualPort') else False
|
||||
return isvpmode
|
||||
|
||||
def _find_controller_port_iscsi_config(self, cportid):
|
||||
'''Finds the SC controller port object for the specified cportid.
|
||||
|
||||
:param cportid: The instanceID of the Dell backend controller port.
|
||||
:returns: The controller port object.
|
||||
'''
|
||||
controllerport = None
|
||||
r = self.client.get('StorageCenter/'
|
||||
'ScControllerPortIscsiConfiguration/%s'
|
||||
% cportid)
|
||||
if r.status_code == 200:
|
||||
controllerport = self._first_result(r)
|
||||
else:
|
||||
LOG.debug('ScControllerPortIscsiConfiguration error: '
|
||||
'%(code)d %(reason)s',
|
||||
{'code': r.status_code,
|
||||
'reason': r.reason})
|
||||
LOG.error(_LE('Unable to find controller '
|
||||
'port iscsi configuration: %s'),
|
||||
cportid)
|
||||
return controllerport
|
||||
|
||||
def find_iscsi_properties(self, scvolume, ip=None, port=None):
|
||||
'''Finds target information for a given Dell scvolume object mapping.
|
||||
|
||||
@ -1137,58 +1254,108 @@ class StorageCenterApi(object):
|
||||
'''
|
||||
LOG.debug('enter find_iscsi_properties')
|
||||
LOG.debug('scvolume: %s', scvolume)
|
||||
active = -1
|
||||
up = -1
|
||||
access_mode = 'rw'
|
||||
# Our mutable process object.
|
||||
pdata = {'active': -1,
|
||||
'up': -1,
|
||||
'access_mode': 'rw',
|
||||
'ip': ip,
|
||||
'port': port}
|
||||
# Our output lists.
|
||||
portals = []
|
||||
luns = []
|
||||
iqns = []
|
||||
|
||||
# Process just looks for the best port to return.
|
||||
def process(lun, iqn, address, port, readonly, status, active):
|
||||
'''Process this mapping information.
|
||||
|
||||
:param lun: SCSI Lun.
|
||||
:param iqn: iSCSI IQN address.
|
||||
:param address: IP address.
|
||||
:param port: IP Port number
|
||||
:param readonly: Boolean indicating mapping is readonly.
|
||||
:param status: String indicating mapping status. (Up is what we
|
||||
are looking for.)
|
||||
:param active: Boolean indicating whether this is on the active
|
||||
controller or not.
|
||||
:return: Nothing
|
||||
'''
|
||||
portals.append(address + ':' +
|
||||
six.text_type(port))
|
||||
iqns.append(iqn)
|
||||
luns.append(lun)
|
||||
|
||||
# We've all the information. We need to find
|
||||
# the best single portal to return. So check
|
||||
# this one if it is on the right IP, port and
|
||||
# if the access and status are correct.
|
||||
if ((pdata['ip'] is None or pdata['ip'] == address) and
|
||||
(pdata['port'] is None or pdata['port'] == port)):
|
||||
|
||||
# We need to point to the best link.
|
||||
# So state active and status up is preferred
|
||||
# but we don't actually need the state to be
|
||||
# up at this point.
|
||||
if pdata['up'] == -1:
|
||||
pdata['access_mode'] = 'rw' if readonly is False else 'ro'
|
||||
if active:
|
||||
pdata['active'] = len(iqns) - 1
|
||||
if status == 'Up':
|
||||
pdata['up'] = pdata['active']
|
||||
|
||||
# Start by getting our mappings.
|
||||
mappings = self._find_mappings(scvolume)
|
||||
|
||||
# We should have mappings at the time of this call but do check.
|
||||
if len(mappings) > 0:
|
||||
# In multipath (per Liberty) we will return all paths. But
|
||||
# if multipath is not set (ip and port are None) then we need
|
||||
# to return a mapping from the controller on which the volume
|
||||
# is active. So find that controller.
|
||||
actvctrl = self._find_active_controller(scvolume)
|
||||
# Two different methods are used to find our luns and portals
|
||||
# depending on whether we are in virtual or legacy port mode.
|
||||
isvpmode = self._is_virtualport_mode()
|
||||
# Trundle through our mappings.
|
||||
for mapping in mappings:
|
||||
# The lun, ro mode and status are in the mapping.
|
||||
LOG.debug('mapping: %s', mapping)
|
||||
lun = mapping.get('lun')
|
||||
ro = mapping.get('readOnly', False)
|
||||
status = mapping.get('status')
|
||||
# Dig a bit to get our domains,IQN and controller id.
|
||||
domains = self._get_domains(mapping)
|
||||
# Get our IQN from our mapping.
|
||||
iqn = self._get_iqn(mapping)
|
||||
ctrlid = self._get_controller_id(mapping)
|
||||
if domains and iqn is not None:
|
||||
for dom in domains:
|
||||
LOG.debug('domain: %s', dom)
|
||||
ipaddress = dom.get('targetIpv4Address',
|
||||
dom.get('wellKnownIpAddress'))
|
||||
portnumber = dom.get('portNumber')
|
||||
# We save our portal.
|
||||
portals.append(ipaddress + ':' +
|
||||
six.text_type(portnumber))
|
||||
iqns.append(iqn)
|
||||
luns.append(lun)
|
||||
# Check if our controller ID matches our active controller ID.
|
||||
isactive = True if (self._get_controller_id(mapping) ==
|
||||
actvctrl) else False
|
||||
# If we have an IQN and are in virtual port mode.
|
||||
if isvpmode and iqn:
|
||||
domains = self._get_domains(mapping)
|
||||
if domains:
|
||||
for dom in domains:
|
||||
LOG.debug('domain: %s', dom)
|
||||
ipaddress = dom.get('targetIpv4Address',
|
||||
dom.get('wellKnownIpAddress'))
|
||||
portnumber = dom.get('portNumber')
|
||||
# We have all our information. Process this portal.
|
||||
process(lun, iqn, ipaddress, portnumber,
|
||||
ro, status, isactive)
|
||||
# Else we are in legacy mode.
|
||||
elif iqn:
|
||||
# Need to get individual ports
|
||||
cportid = self._get_id(mapping.get('controllerPort'))
|
||||
# Legacy mode stuff is in the ISCSI configuration object.
|
||||
cpconfig = self._find_controller_port_iscsi_config(cportid)
|
||||
# This should really never fail. Things happen so if it
|
||||
# does just keep moving. Return what we can.
|
||||
if cpconfig:
|
||||
ipaddress = cpconfig.get('ipAddress')
|
||||
portnumber = cpconfig.get('portNumber')
|
||||
# We have all our information. Process this portal.
|
||||
process(lun, iqn, ipaddress, portnumber,
|
||||
ro, status, isactive)
|
||||
|
||||
# We've all the information. We need to find
|
||||
# the best single portal to return. So check
|
||||
# this one if it is on the right IP, port and
|
||||
# if the access and status are correct.
|
||||
if ((ip is None or ip == ipaddress) and
|
||||
(port is None or port == portnumber)):
|
||||
|
||||
# We need to point to the best link.
|
||||
# So state active and status up is preferred
|
||||
# but we don't actually need the state to be
|
||||
# up at this point.
|
||||
if up == -1:
|
||||
access_mode = 'rw' if ro is False else 'ro'
|
||||
if actvctrl == ctrlid:
|
||||
active = len(iqns) - 1
|
||||
if status == 'Up':
|
||||
up = active
|
||||
# We've gone through all our mappings.
|
||||
# Make sure we found something to return.
|
||||
if len(luns) == 0:
|
||||
# Since we just mapped this and can't find that mapping the world
|
||||
@ -1199,26 +1366,25 @@ class StorageCenterApi(object):
|
||||
# Make sure we point to the best portal we can. This means it is
|
||||
# on the active controller and, preferably, up. If it isn't return
|
||||
# what we have.
|
||||
if up != -1:
|
||||
if pdata['up'] != -1:
|
||||
# We found a connection that is already up. Return that.
|
||||
active = up
|
||||
elif active == -1:
|
||||
pdata['active'] = pdata['up']
|
||||
elif pdata['active'] == -1:
|
||||
# This shouldn't be able to happen. Maybe a controller went
|
||||
# down in the middle of this so just return the first one and
|
||||
# hope the ports are up by the time the connection is attempted.
|
||||
LOG.debug('Volume is not yet active on any controller.')
|
||||
active = 0
|
||||
pdata['active'] = 0
|
||||
|
||||
data = {'target_discovered': False,
|
||||
'target_iqn': iqns[active],
|
||||
'target_iqn': iqns[pdata['active']],
|
||||
'target_iqns': iqns,
|
||||
'target_portal': portals[active],
|
||||
'target_portal': portals[pdata['active']],
|
||||
'target_portals': portals,
|
||||
'target_lun': luns[active],
|
||||
'target_lun': luns[pdata['active']],
|
||||
'target_luns': luns,
|
||||
'access_mode': access_mode
|
||||
'access_mode': pdata['access_mode']
|
||||
}
|
||||
|
||||
LOG.debug('find_iscsi_properties return: %s',
|
||||
data)
|
||||
|
||||
@ -1232,12 +1398,18 @@ class StorageCenterApi(object):
|
||||
|
||||
:param scvolume: Storage Center volume object.
|
||||
:param scserver: Storage Center server opbject.
|
||||
:returns: scmapping or None
|
||||
:returns: SC mapping profile or None
|
||||
'''
|
||||
# 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:
|
||||
# If we have a mapping to our server return it here.
|
||||
mprofiles = self._find_mapping_profiles(scvolume)
|
||||
for mprofile in mprofiles:
|
||||
if self._get_id(mprofile.get('server')) == serverid:
|
||||
return mprofile
|
||||
# No? Then map it up.
|
||||
payload = {}
|
||||
payload['server'] = serverid
|
||||
advanced = {}
|
||||
@ -1273,35 +1445,26 @@ class StorageCenterApi(object):
|
||||
serverid = self._get_id(scserver)
|
||||
volumeid = self._get_id(scvolume)
|
||||
if serverid is not None and volumeid is not None:
|
||||
r = self.client.get('StorageCenter/ScVolume/%s/MappingProfileList'
|
||||
% volumeid)
|
||||
if r.status_code == 200:
|
||||
profiles = self._get_json(r)
|
||||
for profile in profiles:
|
||||
prosrv = profile.get('server')
|
||||
if prosrv is not None and self._get_id(prosrv) == serverid:
|
||||
r = self.client.delete(
|
||||
'StorageCenter/ScMappingProfile/%s'
|
||||
% self._get_id(profile))
|
||||
if (r.status_code != 200 or r.ok is False):
|
||||
LOG.debug('ScMappingProfile error: '
|
||||
'%(code)d %(reason)s',
|
||||
{'code': r.status_code,
|
||||
'reason': r.reason})
|
||||
LOG.error(_LE('Unable to unmap Volume %s'),
|
||||
volumeid)
|
||||
# 1 failed unmap is as good as 100.
|
||||
# Fail it and leave
|
||||
rtn = False
|
||||
break
|
||||
LOG.debug('Volume %(vol)s unmapped from %(srv)s',
|
||||
{'vol': volumeid,
|
||||
'srv': serverid})
|
||||
else:
|
||||
LOG.debug('MappingProfileList error: %(code)d %(reason)s',
|
||||
{'code': r.status_code,
|
||||
'reason': r.reason})
|
||||
rtn = False
|
||||
profiles = self._find_mapping_profiles(scvolume)
|
||||
for profile in profiles:
|
||||
prosrv = profile.get('server')
|
||||
if prosrv is not None and self._get_id(prosrv) == serverid:
|
||||
r = self.client.delete('StorageCenter/ScMappingProfile/%s'
|
||||
% self._get_id(profile))
|
||||
if (r.status_code != 200 or r.ok is False):
|
||||
LOG.debug('ScMappingProfile error: '
|
||||
'%(code)d %(reason)s',
|
||||
{'code': r.status_code,
|
||||
'reason': r.reason})
|
||||
LOG.error(_LE('Unable to unmap Volume %s'),
|
||||
volumeid)
|
||||
# 1 failed unmap is as good as 100.
|
||||
# Fail it and leave
|
||||
rtn = False
|
||||
break
|
||||
LOG.debug('Volume %(vol)s unmapped from %(srv)s',
|
||||
{'vol': volumeid,
|
||||
'srv': serverid})
|
||||
return rtn
|
||||
|
||||
def get_storage_usage(self):
|
||||
@ -1641,7 +1804,8 @@ class StorageCenterApi(object):
|
||||
:return: Dell SC replay profile or None.
|
||||
:raises: VolumeBackendAPIException
|
||||
'''
|
||||
pf = PayloadFilter()
|
||||
self.cg_except_on_no_support()
|
||||
pf = self._get_payload_filter()
|
||||
pf.append('ScSerialNumber', self.ssn)
|
||||
pf.append('Name', name)
|
||||
r = self.client.post('StorageCenter/ScReplayProfile/GetList',
|
||||
@ -1666,6 +1830,7 @@ class StorageCenterApi(object):
|
||||
the name on the Dell SC.
|
||||
:return: SC profile or None.
|
||||
'''
|
||||
self.cg_except_on_no_support()
|
||||
profile = self.find_replay_profile(name)
|
||||
if not profile:
|
||||
payload = {}
|
||||
@ -1688,6 +1853,7 @@ class StorageCenterApi(object):
|
||||
:return: Nothing.
|
||||
:raises: VolumeBackendAPIException
|
||||
'''
|
||||
self.cg_except_on_no_support()
|
||||
r = self.client.delete('StorageCenter/ScReplayProfile/%s' %
|
||||
self._get_id(profile))
|
||||
# 200 is a good return. Log and leave.
|
||||
@ -1804,6 +1970,7 @@ class StorageCenterApi(object):
|
||||
removing the profile from this list of volumes.)
|
||||
:return: True/False on success/failure.
|
||||
'''
|
||||
self.cg_except_on_no_support()
|
||||
ret = True
|
||||
profileid = self._get_id(profile)
|
||||
if add_volumes:
|
||||
@ -1838,6 +2005,7 @@ class StorageCenterApi(object):
|
||||
expiration.
|
||||
:returns: Dell SC replay object.
|
||||
'''
|
||||
self.cg_except_on_no_support()
|
||||
if profile:
|
||||
# We have to make sure these are snappable.
|
||||
self._init_cg_volumes(self._get_id(profile))
|
||||
@ -1871,6 +2039,7 @@ class StorageCenterApi(object):
|
||||
GUID in the replay description.
|
||||
:returns: Dell replay object or None.
|
||||
'''
|
||||
self.cg_except_on_no_support()
|
||||
r = self.client.get('StorageCenter/ScReplayProfile/%s/ReplayList'
|
||||
% self._get_id(profile))
|
||||
replays = self._get_json(r)
|
||||
@ -1906,7 +2075,7 @@ class StorageCenterApi(object):
|
||||
replay description.
|
||||
:returns: Boolean for success or failure.
|
||||
'''
|
||||
|
||||
self.cg_except_on_no_support()
|
||||
LOG.debug('Expiring consistency group replay %s', replayid)
|
||||
replay = self.find_replay(profile,
|
||||
replayid)
|
||||
@ -1922,6 +2091,12 @@ class StorageCenterApi(object):
|
||||
# We either couldn't find it or expired it.
|
||||
return True
|
||||
|
||||
def cg_except_on_no_support(self):
|
||||
if not self.consisgroups:
|
||||
msg = _('Dell API 2.1 or later required'
|
||||
' for Consistency Group support')
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def _size_to_gb(self, spacestring):
|
||||
'''Splits a SC size string into GB and a remainder.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user