Merge "Dell SC: Fix legacy bug, init_conn bug and REST API bug."

This commit is contained in:
Jenkins 2015-07-25 04:40:31 +00:00 committed by Gerrit Code Review
commit 4f197feb0d
2 changed files with 794 additions and 174 deletions

View File

@ -474,29 +474,6 @@ class DellSCSanAPITestCase(test.TestCase):
u'instanceName': u'Other Multipath', u'instanceName': u'Other Multipath',
u'objectType': u'ScServerOperatingSystem'}}] 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', MAP_PROFILE = {u'instanceId': u'64702.2941',
u'scName': u'Storage Center 64702', u'scName': u'Storage Center 64702',
u'scSerialNumber': 64702, u'scSerialNumber': 64702,
@ -520,6 +497,8 @@ class DellSCSanAPITestCase(test.TestCase):
u'instanceName': u'6025-47', u'instanceName': u'6025-47',
u'lunRequested': u'N/A'} u'lunRequested': u'N/A'}
MAP_PROFILES = [MAP_PROFILE]
MAPPINGS = [{u'profile': {u'instanceId': u'64702.104', MAPPINGS = [{u'profile': {u'instanceId': u'64702.104',
u'instanceName': u'92-30', u'instanceName': u'92-30',
u'objectType': u'ScMappingProfile'}, u'objectType': u'ScMappingProfile'},
@ -1473,6 +1452,57 @@ class DellSCSanAPITestCase(test.TestCase):
u'userCreated': False, u'userCreated': False,
u'volumeCount': 0}] 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' IQN = 'iqn.2002-03.com.compellent:5000D31000000001'
WWN = u'21000024FF30441C' WWN = u'21000024FF30441C'
@ -2839,6 +2869,37 @@ class DellSCSanAPITestCase(test.TestCase):
self.assertTrue(mock_get_json.called) self.assertTrue(mock_get_json.called)
self.assertEqual([], res, 'Mapping count mismatch') 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, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_first_result', '_first_result',
return_value=CTRLR_PORT) return_value=CTRLR_PORT)
@ -3075,7 +3136,11 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings', '_find_mappings',
return_value=MAPPINGS) return_value=MAPPINGS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_is_virtualport_mode',
return_value=True)
def test_find_iscsi_properties_mappings(self, def test_find_iscsi_properties_mappings(self,
mock_is_virtualport_mode,
mock_find_mappings, mock_find_mappings,
mock_find_domains, mock_find_domains,
mock_find_ctrl_port, mock_find_ctrl_port,
@ -3084,6 +3149,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_open_connection, mock_open_connection,
mock_init): mock_init):
res = self.scapi.find_iscsi_properties(self.VOLUME) 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_mappings.called)
self.assertTrue(mock_find_domains.called) self.assertTrue(mock_find_domains.called)
self.assertTrue(mock_find_ctrl_port.called) self.assertTrue(mock_find_ctrl_port.called)
@ -3112,7 +3178,11 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings', '_find_mappings',
return_value=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, def test_find_iscsi_properties_by_address(self,
mock_is_virtualport_mode,
mock_find_mappings, mock_find_mappings,
mock_find_domains, mock_find_domains,
mock_find_ctrl_port, mock_find_ctrl_port,
@ -3123,6 +3193,7 @@ class DellSCSanAPITestCase(test.TestCase):
# Test case to find iSCSI mappings by IP Address & port # Test case to find iSCSI mappings by IP Address & port
res = self.scapi.find_iscsi_properties( res = self.scapi.find_iscsi_properties(
self.VOLUME, '192.168.0.21', 3260) self.VOLUME, '192.168.0.21', 3260)
self.assertTrue(mock_is_virtualport_mode.called)
self.assertTrue(mock_find_mappings.called) self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_find_domains.called) self.assertTrue(mock_find_domains.called)
self.assertTrue(mock_find_ctrl_port.called) self.assertTrue(mock_find_ctrl_port.called)
@ -3151,17 +3222,23 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings', '_find_mappings',
return_value=MAPPINGS) return_value=MAPPINGS)
def test_find_iscsi_properties_by_address_not_found(self, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
mock_find_mappings, '_is_virtualport_mode',
mock_find_domains, return_value=True)
mock_find_ctrl_port, def test_find_iscsi_properties_by_address_not_found(
mock_find_active_ctrl, self,
mock_close_connection, mock_is_virtualport_mode,
mock_open_connection, mock_find_mappings,
mock_init): 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 # Test case to find iSCSI mappings by IP Address & port are not found
res = self.scapi.find_iscsi_properties( res = self.scapi.find_iscsi_properties(
self.VOLUME, '192.168.1.21', 3260) self.VOLUME, '192.168.1.21', 3260)
self.assertTrue(mock_is_virtualport_mode.called)
self.assertTrue(mock_find_mappings.called) self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_find_domains.called) self.assertTrue(mock_find_domains.called)
self.assertTrue(mock_find_ctrl_port.called) self.assertTrue(mock_find_ctrl_port.called)
@ -3204,7 +3281,11 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings', '_find_mappings',
return_value=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, def test_find_iscsi_properties_no_domain(self,
mock_is_virtualport_mode,
mock_find_mappings, mock_find_mappings,
mock_find_domains, mock_find_domains,
mock_find_ctrl_port, mock_find_ctrl_port,
@ -3216,6 +3297,7 @@ class DellSCSanAPITestCase(test.TestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.find_iscsi_properties, self.scapi.find_iscsi_properties,
self.VOLUME) self.VOLUME)
self.assertTrue(mock_is_virtualport_mode.called)
self.assertTrue(mock_find_mappings.called) self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_find_domains.called) self.assertTrue(mock_find_domains.called)
self.assertTrue(mock_find_ctrl_port.called) self.assertTrue(mock_find_ctrl_port.called)
@ -3227,15 +3309,15 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_controller_port', '_find_controller_port',
return_value=None) return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_domains',
return_value=ISCSI_FLT_DOMAINS)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings', '_find_mappings',
return_value=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, def test_find_iscsi_properties_no_ctrl_port(self,
mock_is_virtualport_mode,
mock_find_mappings, mock_find_mappings,
mock_find_domains,
mock_find_ctrl_port, mock_find_ctrl_port,
mock_find_active_controller, mock_find_active_controller,
mock_close_connection, mock_close_connection,
@ -3245,8 +3327,8 @@ class DellSCSanAPITestCase(test.TestCase):
self.assertRaises(exception.VolumeBackendAPIException, self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.find_iscsi_properties, self.scapi.find_iscsi_properties,
self.VOLUME) self.VOLUME)
self.assertTrue(mock_is_virtualport_mode.called)
self.assertTrue(mock_find_mappings.called) self.assertTrue(mock_find_mappings.called)
self.assertTrue(mock_find_domains.called)
self.assertTrue(mock_find_ctrl_port.called) self.assertTrue(mock_find_ctrl_port.called)
self.assertTrue(mock_find_active_controller.called) self.assertTrue(mock_find_active_controller.called)
@ -3262,7 +3344,11 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings', '_find_mappings',
return_value=MAPPINGS_READ_ONLY) 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, def test_find_iscsi_properties_ro(self,
mock_is_virtualport_mode,
mock_find_mappings, mock_find_mappings,
mock_find_domains, mock_find_domains,
mock_find_ctrl_port, mock_find_ctrl_port,
@ -3272,6 +3358,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_init): mock_init):
# Test case where Read Only mappings are found # Test case where Read Only mappings are found
res = self.scapi.find_iscsi_properties(self.VOLUME) 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_mappings.called)
self.assertTrue(mock_find_domains.called) self.assertTrue(mock_find_domains.called)
self.assertTrue(mock_find_ctrl_port.called) self.assertTrue(mock_find_ctrl_port.called)
@ -3300,7 +3387,11 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mappings', '_find_mappings',
return_value=MAPPINGS_MULTI_PORTAL) 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, def test_find_iscsi_properties_multi_portals(self,
mock_is_virtualport_mode,
mock_find_mappings, mock_find_mappings,
mock_find_domains, mock_find_domains,
mock_find_ctrl_port, mock_find_ctrl_port,
@ -3314,6 +3405,7 @@ class DellSCSanAPITestCase(test.TestCase):
self.assertTrue(mock_find_domains.called) self.assertTrue(mock_find_domains.called)
self.assertTrue(mock_find_ctrl_port.called) self.assertTrue(mock_find_ctrl_port.called)
self.assertTrue(mock_find_active_controller.called) self.assertTrue(mock_find_active_controller.called)
self.assertTrue(mock_is_virtualport_mode.called)
expected = {'access_mode': 'rw', expected = {'access_mode': 'rw',
'target_discovered': False, 'target_discovered': False,
'target_iqn': 'target_iqn':
@ -3332,13 +3424,275 @@ class DellSCSanAPITestCase(test.TestCase):
u'192.168.0.25:3260']} u'192.168.0.25:3260']}
self.assertEqual(expected, res, 'Wrong Target Info') 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, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_first_result', '_first_result',
return_value=MAP_PROFILE) return_value=MAP_PROFILE)
@mock.patch.object(dell_storagecenter_api.HttpClient, @mock.patch.object(dell_storagecenter_api.HttpClient,
'post', 'post',
return_value=RESPONSE_200) return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mapping_profiles',
return_value=[])
def test_map_volume(self, def test_map_volume(self,
mock_find_mapping_profiles,
mock_post, mock_post,
mock_first_result, mock_first_result,
mock_close_connection, mock_close_connection,
@ -3346,6 +3700,54 @@ class DellSCSanAPITestCase(test.TestCase):
mock_init): mock_init):
res = self.scapi.map_volume(self.VOLUME, res = self.scapi.map_volume(self.VOLUME,
self.SCSERVER) 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_post.called)
self.assertTrue(mock_first_result.called) self.assertTrue(mock_first_result.called)
self.assertEqual(self.MAP_PROFILE, res, 'Incorrect ScMappingProfile') self.assertEqual(self.MAP_PROFILE, res, 'Incorrect ScMappingProfile')
@ -3395,7 +3797,11 @@ class DellSCSanAPITestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.HttpClient, @mock.patch.object(dell_storagecenter_api.HttpClient,
'post', 'post',
return_value=RESPONSE_204) return_value=RESPONSE_204)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mapping_profiles',
return_value=[])
def test_map_volume_failure(self, def test_map_volume_failure(self,
mock_find_mapping_profiles,
mock_post, mock_post,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
@ -3403,6 +3809,7 @@ class DellSCSanAPITestCase(test.TestCase):
# Test case where mapping volume to server fails # Test case where mapping volume to server fails
res = self.scapi.map_volume(self.VOLUME, res = self.scapi.map_volume(self.VOLUME,
self.SCSERVER) self.SCSERVER)
self.assertTrue(mock_find_mapping_profiles.called)
self.assertTrue(mock_post.called) self.assertTrue(mock_post.called)
self.assertIsNone(res, 'None expected') self.assertIsNone(res, 'None expected')
@ -3410,76 +3817,66 @@ class DellSCSanAPITestCase(test.TestCase):
'delete', 'delete',
return_value=RESPONSE_200) return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json', '_find_mapping_profiles',
return_value=MAP_PROFILES) return_value=MAP_PROFILES)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get',
return_value=RESPONSE_200)
def test_unmap_volume(self, def test_unmap_volume(self,
mock_get, mock_find_mapping_profiles,
mock_get_json,
mock_delete, mock_delete,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
res = self.scapi.unmap_volume(self.VOLUME, res = self.scapi.unmap_volume(self.VOLUME,
self.SCSERVER) self.SCSERVER)
self.assertTrue(mock_get.called) self.assertTrue(mock_find_mapping_profiles.called)
self.assertTrue(mock_get_json.called)
self.assertTrue(mock_delete.called) self.assertTrue(mock_delete.called)
self.assertTrue(res) self.assertTrue(res)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_mapping_profiles',
return_value=MAP_PROFILES)
@mock.patch.object(dell_storagecenter_api.HttpClient, @mock.patch.object(dell_storagecenter_api.HttpClient,
'get', 'delete',
return_value=RESPONSE_204) return_value=RESPONSE_204)
def test_unmap_volume_failure(self, def test_unmap_volume_failure(self,
mock_get, mock_delete,
mock_find_mapping_profiles,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
res = self.scapi.unmap_volume(self.VOLUME, res = self.scapi.unmap_volume(self.VOLUME,
self.SCSERVER) self.SCSERVER)
self.assertTrue(mock_get.called) self.assertTrue(mock_find_mapping_profiles.called)
self.assertTrue(mock_delete.called)
self.assertFalse(res) self.assertFalse(res)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json', '_find_mapping_profiles',
return_value=[]) return_value=[])
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get',
return_value=RESPONSE_200)
def test_unmap_volume_no_map_profile(self, def test_unmap_volume_no_map_profile(self,
mock_get, mock_find_mapping_profiles,
mock_get_json,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
res = self.scapi.unmap_volume(self.VOLUME, res = self.scapi.unmap_volume(self.VOLUME,
self.SCSERVER) self.SCSERVER)
self.assertTrue(mock_get.called) self.assertTrue(mock_find_mapping_profiles.called)
self.assertTrue(mock_get_json.called)
self.assertTrue(res) self.assertTrue(res)
@mock.patch.object(dell_storagecenter_api.HttpClient, @mock.patch.object(dell_storagecenter_api.HttpClient,
'delete', 'delete',
return_value=RESPONSE_204) return_value=RESPONSE_204)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json', '_find_mapping_profiles',
return_value=MAP_PROFILES) return_value=MAP_PROFILES)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get',
return_value=RESPONSE_200)
def test_unmap_volume_del_fail(self, def test_unmap_volume_del_fail(self,
mock_get, mock_find_mapping_profiles,
mock_get_json,
mock_delete, mock_delete,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
res = self.scapi.unmap_volume(self.VOLUME, res = self.scapi.unmap_volume(self.VOLUME,
self.SCSERVER) self.SCSERVER)
self.assertTrue(mock_get.called) self.assertTrue(mock_find_mapping_profiles.called)
self.assertTrue(mock_get_json.called)
self.assertTrue(mock_delete.called) self.assertTrue(mock_delete.called)
self.assertFalse(res, False) self.assertFalse(res, False)
@ -3489,14 +3886,10 @@ class DellSCSanAPITestCase(test.TestCase):
'delete', 'delete',
return_value=RESPONSE_200) return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json', '_find_mapping_profiles',
return_value=MAP_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, def test_unmap_volume_no_vol_id(self,
mock_get, mock_find_mapping_profiles,
mock_get_json,
mock_delete, mock_delete,
mock_get_id, mock_get_id,
mock_close_connection, mock_close_connection,
@ -3506,8 +3899,7 @@ class DellSCSanAPITestCase(test.TestCase):
mock_get_id.side_effect = [None, '64702.47'] mock_get_id.side_effect = [None, '64702.47']
res = self.scapi.unmap_volume(self.VOLUME, res = self.scapi.unmap_volume(self.VOLUME,
self.SCSERVER) self.SCSERVER)
self.assertFalse(mock_get.called) self.assertFalse(mock_find_mapping_profiles.called)
self.assertFalse(mock_get_json.called)
self.assertFalse(mock_delete.called) self.assertFalse(mock_delete.called)
self.assertTrue(res) self.assertTrue(res)
@ -3517,14 +3909,10 @@ class DellSCSanAPITestCase(test.TestCase):
'delete', 'delete',
return_value=RESPONSE_200) return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json', '_find_mapping_profiles',
return_value=MAP_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, def test_unmap_volume_no_server_id(self,
mock_get, mock_find_mapping_profiles,
mock_get_json,
mock_delete, mock_delete,
mock_get_id, mock_get_id,
mock_close_connection, mock_close_connection,
@ -3534,11 +3922,41 @@ class DellSCSanAPITestCase(test.TestCase):
mock_get_id.side_effect = ['64702.3494', None] mock_get_id.side_effect = ['64702.3494', None]
res = self.scapi.unmap_volume(self.VOLUME, res = self.scapi.unmap_volume(self.VOLUME,
self.SCSERVER) self.SCSERVER)
self.assertFalse(mock_get.called) self.assertFalse(mock_find_mapping_profiles.called)
self.assertFalse(mock_get_json.called)
self.assertFalse(mock_delete.called) self.assertFalse(mock_delete.called)
self.assertTrue(res) 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, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json', '_get_json',
return_value=STRG_USAGE) return_value=STRG_USAGE)
@ -5234,6 +5652,29 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
response_nc.reason = u'duplicate' response_nc.reason = u'duplicate'
RESPONSE_204 = response_nc 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): def setUp(self):
super(DellSCSanAPIConnectionTestCase, self).setUp() super(DellSCSanAPIConnectionTestCase, self).setUp()
@ -5278,7 +5719,11 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
@mock.patch.object(dell_storagecenter_api.HttpClient, @mock.patch.object(dell_storagecenter_api.HttpClient,
'post', 'post',
return_value=RESPONSE_200) return_value=RESPONSE_200)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=APIDICT)
def test_open_connection(self, def test_open_connection(self,
mock_get_json,
mock_post): mock_post):
self.scapi.open_connection() self.scapi.open_connection()
self.assertTrue(mock_post.called) self.assertTrue(mock_post.called)

View File

@ -33,16 +33,34 @@ class PayloadFilter(object):
'''PayloadFilter '''PayloadFilter
Simple class for creating filters for interacting with the Dell Simple class for creating filters for interacting with the Dell
Storage API. Storage API DropTop2 and later.
Note that this defaults to "AND" filter types. This is a pretty limited
class. It only does the trivial filters required for this driver.
''' '''
def __init__(self): def __init__(self, filtertype='AND'):
self.payload = {} self.payload = {}
self.payload['filterType'] = 'AND' self.payload['filter'] = {'filterType': filtertype,
self.payload['filters'] = [] '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'): def append(self, name, val, filtertype='Equals'):
if val is not None: if val is not None:
@ -167,9 +185,18 @@ class StorageCenterApi(object):
'''StorageCenterApi '''StorageCenterApi
Handles calls to Dell Enterprise Manager (EM) via the REST API interface. 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): def __init__(self, host, port, user, password, verify):
'''This creates a connection to Dell Enterprise Manager. '''This creates a connection to Dell Enterprise Manager.
@ -185,6 +212,8 @@ class StorageCenterApi(object):
self.ssn = None self.ssn = None
self.vfname = 'openstack' self.vfname = 'openstack'
self.sfname = 'openstack' self.sfname = 'openstack'
self.legacypayloadfilters = False
self.consisgroups = True
self.client = HttpClient(host, self.client = HttpClient(host,
port, port,
user, user,
@ -286,6 +315,12 @@ class StorageCenterApi(object):
blob) blob)
return None 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): def open_connection(self):
'''Authenticate against Dell Enterprise Manager. '''Authenticate against Dell Enterprise Manager.
@ -297,13 +332,41 @@ class StorageCenterApi(object):
payload['ApplicationVersion'] = self.APIVERSION payload['ApplicationVersion'] = self.APIVERSION
r = self.client.post('ApiConnection/Login', r = self.client.post('ApiConnection/Login',
payload) 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'), LOG.error(_LE('Login error: %(code)d %(reason)s'),
{'code': r.status_code, {'code': r.status_code,
'reason': r.reason}) '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): def close_connection(self):
'''Logout of Dell Enterprise Manager.''' '''Logout of Dell Enterprise Manager.'''
@ -427,7 +490,7 @@ class StorageCenterApi(object):
:param foldername: Full path to the folder we are looking for. :param foldername: Full path to the folder we are looking for.
:returns: Dell folder object. :returns: Dell folder object.
''' '''
pf = PayloadFilter() pf = self._get_payload_filter()
pf.append('scSerialNumber', self.ssn) pf.append('scSerialNumber', self.ssn)
basename = os.path.basename(foldername) basename = os.path.basename(foldername)
pf.append('Name', basename) pf.append('Name', basename)
@ -476,7 +539,7 @@ class StorageCenterApi(object):
Don't wig out if this fails. Don't wig out if this fails.
:param scvolume: Dell Volume object. :param scvolume: Dell Volume object.
''' '''
pf = PayloadFilter() pf = self._get_payload_filter()
pf.append('scSerialNumber', scvolume.get('scSerialNumber'), 'Equals') pf.append('scSerialNumber', scvolume.get('scSerialNumber'), 'Equals')
r = self.client.post('StorageCenter/ScServer/GetList', pf.payload) r = self.client.post('StorageCenter/ScServer/GetList', pf.payload)
if r.status_code == 200: if r.status_code == 200:
@ -521,7 +584,7 @@ class StorageCenterApi(object):
# and look through for the one we want. Never many profiles, so # and look through for the one we want. Never many profiles, so
# this doesn't cause as much overhead as it might seem. # this doesn't cause as much overhead as it might seem.
storage_profile = storage_profile.replace(' ', '').lower() storage_profile = storage_profile.replace(' ', '').lower()
pf = PayloadFilter() pf = self._get_payload_filter()
pf.append('scSerialNumber', self.ssn, 'Equals') pf.append('scSerialNumber', self.ssn, 'Equals')
r = self.client.post( r = self.client.post(
'StorageCenter/ScStorageProfile/GetList', pf.payload) 'StorageCenter/ScStorageProfile/GetList', pf.payload)
@ -617,7 +680,7 @@ class StorageCenterApi(object):
result = None result = None
# We need a name or a device ID to find a volume. # We need a name or a device ID to find a volume.
if name or deviceid: if name or deviceid:
pf = PayloadFilter() pf = self._get_payload_filter()
pf.append('scSerialNumber', self.ssn) pf.append('scSerialNumber', self.ssn)
if name is not None: if name is not None:
pf.append('Name', name) pf.append('Name', name)
@ -773,7 +836,7 @@ class StorageCenterApi(object):
:param osname: The name of the OS to look for. :param osname: The name of the OS to look for.
:returns: InstanceId of the ScServerOperatingSystem object. :returns: InstanceId of the ScServerOperatingSystem object.
''' '''
pf = PayloadFilter() pf = self._get_payload_filter()
pf.append('scSerialNumber', self.ssn) pf.append('scSerialNumber', self.ssn)
r = self.client.post('StorageCenter/ScServerOperatingSystem/GetList', r = self.client.post('StorageCenter/ScServerOperatingSystem/GetList',
pf.payload) pf.payload)
@ -888,7 +951,7 @@ class StorageCenterApi(object):
# that we found one it actually has to be attached to a # that we found one it actually has to be attached to a
# server. # server.
if hba is not None and hba.get('server') is not None: 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('scSerialNumber', self.ssn)
pf.append('instanceId', self._get_id(hba['server'])) pf.append('instanceId', self._get_id(hba['server']))
r = self.client.post('StorageCenter/ScServer/GetList', r = self.client.post('StorageCenter/ScServer/GetList',
@ -916,7 +979,7 @@ class StorageCenterApi(object):
''' '''
scserverhba = None 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 = self._get_payload_filter()
pf.append('scSerialNumber', self.ssn) pf.append('scSerialNumber', self.ssn)
pf.append('instanceName', instance_name) pf.append('instanceName', instance_name)
r = self.client.post('StorageCenter/ScServerHba/GetList', r = self.client.post('StorageCenter/ScServerHba/GetList',
@ -1006,6 +1069,28 @@ class StorageCenterApi(object):
LOG.error(_LE('_find_mappings: volume is not active')) LOG.error(_LE('_find_mappings: volume is not active'))
return mappings 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): def _find_controller_port(self, cportid):
'''Finds the SC controller port object for the specified cportid. '''Finds the SC controller port object for the specified cportid.
@ -1124,6 +1209,38 @@ class StorageCenterApi(object):
iqn = controllerport.get('iscsiName') iqn = controllerport.get('iscsiName')
return iqn 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): def find_iscsi_properties(self, scvolume, ip=None, port=None):
'''Finds target information for a given Dell scvolume object mapping. '''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('enter find_iscsi_properties')
LOG.debug('scvolume: %s', scvolume) LOG.debug('scvolume: %s', scvolume)
active = -1 # Our mutable process object.
up = -1 pdata = {'active': -1,
access_mode = 'rw' 'up': -1,
'access_mode': 'rw',
'ip': ip,
'port': port}
# Our output lists.
portals = [] portals = []
luns = [] luns = []
iqns = [] 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) mappings = self._find_mappings(scvolume)
# We should have mappings at the time of this call but do check.
if len(mappings) > 0: if len(mappings) > 0:
# In multipath (per Liberty) we will return all paths. But # In multipath (per Liberty) we will return all paths. But
# if multipath is not set (ip and port are None) then we need # if multipath is not set (ip and port are None) then we need
# to return a mapping from the controller on which the volume # to return a mapping from the controller on which the volume
# is active. So find that controller. # is active. So find that controller.
actvctrl = self._find_active_controller(scvolume) 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: for mapping in mappings:
# The lun, ro mode and status are in the mapping. # The lun, ro mode and status are in the mapping.
LOG.debug('mapping: %s', mapping) LOG.debug('mapping: %s', mapping)
lun = mapping.get('lun') lun = mapping.get('lun')
ro = mapping.get('readOnly', False) ro = mapping.get('readOnly', False)
status = mapping.get('status') status = mapping.get('status')
# Dig a bit to get our domains,IQN and controller id. # Get our IQN from our mapping.
domains = self._get_domains(mapping)
iqn = self._get_iqn(mapping) iqn = self._get_iqn(mapping)
ctrlid = self._get_controller_id(mapping) # Check if our controller ID matches our active controller ID.
if domains and iqn is not None: isactive = True if (self._get_controller_id(mapping) ==
for dom in domains: actvctrl) else False
LOG.debug('domain: %s', dom) # If we have an IQN and are in virtual port mode.
ipaddress = dom.get('targetIpv4Address', if isvpmode and iqn:
dom.get('wellKnownIpAddress')) domains = self._get_domains(mapping)
portnumber = dom.get('portNumber') if domains:
# We save our portal. for dom in domains:
portals.append(ipaddress + ':' + LOG.debug('domain: %s', dom)
six.text_type(portnumber)) ipaddress = dom.get('targetIpv4Address',
iqns.append(iqn) dom.get('wellKnownIpAddress'))
luns.append(lun) 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 # We've gone through all our mappings.
# 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
# Make sure we found something to return. # Make sure we found something to return.
if len(luns) == 0: if len(luns) == 0:
# Since we just mapped this and can't find that mapping the world # 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 # 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 # on the active controller and, preferably, up. If it isn't return
# what we have. # what we have.
if up != -1: if pdata['up'] != -1:
# We found a connection that is already up. Return that. # We found a connection that is already up. Return that.
active = up pdata['active'] = pdata['up']
elif active == -1: elif pdata['active'] == -1:
# This shouldn't be able to happen. Maybe a controller went # This shouldn't be able to happen. Maybe a controller went
# down in the middle of this so just return the first one and # 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. # hope the ports are up by the time the connection is attempted.
LOG.debug('Volume is not yet active on any controller.') LOG.debug('Volume is not yet active on any controller.')
active = 0 pdata['active'] = 0
data = {'target_discovered': False, data = {'target_discovered': False,
'target_iqn': iqns[active], 'target_iqn': iqns[pdata['active']],
'target_iqns': iqns, 'target_iqns': iqns,
'target_portal': portals[active], 'target_portal': portals[pdata['active']],
'target_portals': portals, 'target_portals': portals,
'target_lun': luns[active], 'target_lun': luns[pdata['active']],
'target_luns': luns, 'target_luns': luns,
'access_mode': access_mode 'access_mode': pdata['access_mode']
} }
LOG.debug('find_iscsi_properties return: %s', LOG.debug('find_iscsi_properties return: %s',
data) data)
@ -1232,12 +1398,18 @@ class StorageCenterApi(object):
:param scvolume: Storage Center volume object. :param scvolume: Storage Center volume object.
:param scserver: Storage Center server opbject. :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 # Make sure we have what we think we have
serverid = self._get_id(scserver) serverid = self._get_id(scserver)
volumeid = self._get_id(scvolume) volumeid = self._get_id(scvolume)
if serverid is not None and volumeid is not None: 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 = {}
payload['server'] = serverid payload['server'] = serverid
advanced = {} advanced = {}
@ -1273,35 +1445,26 @@ class StorageCenterApi(object):
serverid = self._get_id(scserver) serverid = self._get_id(scserver)
volumeid = self._get_id(scvolume) volumeid = self._get_id(scvolume)
if serverid is not None and volumeid is not None: if serverid is not None and volumeid is not None:
r = self.client.get('StorageCenter/ScVolume/%s/MappingProfileList' profiles = self._find_mapping_profiles(scvolume)
% volumeid) for profile in profiles:
if r.status_code == 200: prosrv = profile.get('server')
profiles = self._get_json(r) if prosrv is not None and self._get_id(prosrv) == serverid:
for profile in profiles: r = self.client.delete('StorageCenter/ScMappingProfile/%s'
prosrv = profile.get('server') % self._get_id(profile))
if prosrv is not None and self._get_id(prosrv) == serverid: if (r.status_code != 200 or r.ok is False):
r = self.client.delete( LOG.debug('ScMappingProfile error: '
'StorageCenter/ScMappingProfile/%s' '%(code)d %(reason)s',
% self._get_id(profile)) {'code': r.status_code,
if (r.status_code != 200 or r.ok is False): 'reason': r.reason})
LOG.debug('ScMappingProfile error: ' LOG.error(_LE('Unable to unmap Volume %s'),
'%(code)d %(reason)s', volumeid)
{'code': r.status_code, # 1 failed unmap is as good as 100.
'reason': r.reason}) # Fail it and leave
LOG.error(_LE('Unable to unmap Volume %s'), rtn = False
volumeid) break
# 1 failed unmap is as good as 100. LOG.debug('Volume %(vol)s unmapped from %(srv)s',
# Fail it and leave {'vol': volumeid,
rtn = False 'srv': serverid})
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
return rtn return rtn
def get_storage_usage(self): def get_storage_usage(self):
@ -1641,7 +1804,8 @@ class StorageCenterApi(object):
:return: Dell SC replay profile or None. :return: Dell SC replay profile or None.
:raises: VolumeBackendAPIException :raises: VolumeBackendAPIException
''' '''
pf = PayloadFilter() self.cg_except_on_no_support()
pf = self._get_payload_filter()
pf.append('ScSerialNumber', self.ssn) pf.append('ScSerialNumber', self.ssn)
pf.append('Name', name) pf.append('Name', name)
r = self.client.post('StorageCenter/ScReplayProfile/GetList', r = self.client.post('StorageCenter/ScReplayProfile/GetList',
@ -1666,6 +1830,7 @@ class StorageCenterApi(object):
the name on the Dell SC. the name on the Dell SC.
:return: SC profile or None. :return: SC profile or None.
''' '''
self.cg_except_on_no_support()
profile = self.find_replay_profile(name) profile = self.find_replay_profile(name)
if not profile: if not profile:
payload = {} payload = {}
@ -1688,6 +1853,7 @@ class StorageCenterApi(object):
:return: Nothing. :return: Nothing.
:raises: VolumeBackendAPIException :raises: VolumeBackendAPIException
''' '''
self.cg_except_on_no_support()
r = self.client.delete('StorageCenter/ScReplayProfile/%s' % r = self.client.delete('StorageCenter/ScReplayProfile/%s' %
self._get_id(profile)) self._get_id(profile))
# 200 is a good return. Log and leave. # 200 is a good return. Log and leave.
@ -1804,6 +1970,7 @@ class StorageCenterApi(object):
removing the profile from this list of volumes.) removing the profile from this list of volumes.)
:return: True/False on success/failure. :return: True/False on success/failure.
''' '''
self.cg_except_on_no_support()
ret = True ret = True
profileid = self._get_id(profile) profileid = self._get_id(profile)
if add_volumes: if add_volumes:
@ -1838,6 +2005,7 @@ class StorageCenterApi(object):
expiration. expiration.
:returns: Dell SC replay object. :returns: Dell SC replay object.
''' '''
self.cg_except_on_no_support()
if profile: if profile:
# We have to make sure these are snappable. # We have to make sure these are snappable.
self._init_cg_volumes(self._get_id(profile)) self._init_cg_volumes(self._get_id(profile))
@ -1871,6 +2039,7 @@ class StorageCenterApi(object):
GUID in the replay description. GUID in the replay description.
:returns: Dell replay object or None. :returns: Dell replay object or None.
''' '''
self.cg_except_on_no_support()
r = self.client.get('StorageCenter/ScReplayProfile/%s/ReplayList' r = self.client.get('StorageCenter/ScReplayProfile/%s/ReplayList'
% self._get_id(profile)) % self._get_id(profile))
replays = self._get_json(r) replays = self._get_json(r)
@ -1906,7 +2075,7 @@ class StorageCenterApi(object):
replay description. replay description.
:returns: Boolean for success or failure. :returns: Boolean for success or failure.
''' '''
self.cg_except_on_no_support()
LOG.debug('Expiring consistency group replay %s', replayid) LOG.debug('Expiring consistency group replay %s', replayid)
replay = self.find_replay(profile, replay = self.find_replay(profile,
replayid) replayid)
@ -1922,6 +2091,12 @@ class StorageCenterApi(object):
# We either couldn't find it or expired it. # We either couldn't find it or expired it.
return True 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): def _size_to_gb(self, spacestring):
'''Splits a SC size string into GB and a remainder. '''Splits a SC size string into GB and a remainder.