Dell SC: Error attaching after LV-AFO

Fixed miscellaneous errors dealing with the state of Dell's
Live Volumes after a LV AFO event.

Was looking for the original volume and sc server object
after the primary was down. We now look for the new primary
and attach that.

Ensure export was failing to properly kick off a swap
back to the original primary if it was available.

Fixed an error with deleting replications.

Added a workaround to finding a ScLiveVolume object. Previous
method could return stale information.

Change-Id: I58889adbcf279d4b0107cbc51e1dace888a3fb15
Closes-Bug: #1617401
This commit is contained in:
Tom Swanson 2016-08-26 14:54:53 -05:00
parent 4d209fd966
commit 45bc8abc47
7 changed files with 531 additions and 251 deletions

View File

@ -262,7 +262,8 @@ class DellSCSanFCDriverTestCase(test.TestCase):
sclivevol = {'instanceId': '101.101', sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101', 'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID}, 'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102} 'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
mock_is_live_volume.return_value = True mock_is_live_volume.return_value = True
mock_find_wwns.return_value = ( mock_find_wwns.return_value = (
1, [u'5000D31000FCBE3D', u'5000D31000FCBE35'], 1, [u'5000D31000FCBE3D', u'5000D31000FCBE35'],
@ -272,7 +273,7 @@ class DellSCSanFCDriverTestCase(test.TestCase):
1, [u'5000D31000FCBE3E', u'5000D31000FCBE36'], 1, [u'5000D31000FCBE3E', u'5000D31000FCBE36'],
{u'21000024FF30441E': [u'5000D31000FCBE36'], {u'21000024FF30441E': [u'5000D31000FCBE36'],
u'21000024FF30441F': [u'5000D31000FCBE3E']}) u'21000024FF30441F': [u'5000D31000FCBE3E']})
mock_get_live_volume.return_value = (sclivevol, False) mock_get_live_volume.return_value = sclivevol
res = self.driver.initialize_connection(volume, connector) res = self.driver.initialize_connection(volume, connector)
expected = {'data': expected = {'data':
{'discard': True, {'discard': True,
@ -292,6 +293,74 @@ class DellSCSanFCDriverTestCase(test.TestCase):
mock_find_volume.assert_called_once_with(fake.VOLUME_ID, None, True) mock_find_volume.assert_called_once_with(fake.VOLUME_ID, None, True)
mock_get_volume.assert_called_once_with(self.VOLUME[u'instanceId']) mock_get_volume.assert_called_once_with(self.VOLUME[u'instanceId'])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_server',
return_value=SCSERVER)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'get_volume')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'map_volume',
return_value=MAPPING)
@mock.patch.object(dell_storagecenter_fc.DellStorageCenterFCDriver,
'_is_live_vol')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_wwns')
@mock.patch.object(dell_storagecenter_fc.DellStorageCenterFCDriver,
'initialize_secondary')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'get_live_volume')
def test_initialize_connection_live_vol_afo(self,
mock_get_live_volume,
mock_initialize_secondary,
mock_find_wwns,
mock_is_live_volume,
mock_map_volume,
mock_get_volume,
mock_find_volume,
mock_find_server,
mock_close_connection,
mock_open_connection,
mock_init):
volume = {'id': fake.VOLUME_ID, 'provider_id': '101.101'}
scvol = {'instanceId': '102.101'}
mock_find_volume.return_value = scvol
mock_get_volume.return_value = scvol
connector = self.connector
sclivevol = {'instanceId': '101.10001',
'primaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'primaryScSerialNumber': 102,
'secondaryVolume': {'instanceId': '101.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 101,
'secondaryRole': 'Activated'}
mock_is_live_volume.return_value = True
mock_find_wwns.return_value = (
1, [u'5000D31000FCBE3D', u'5000D31000FCBE35'],
{u'21000024FF30441C': [u'5000D31000FCBE35'],
u'21000024FF30441D': [u'5000D31000FCBE3D']})
mock_get_live_volume.return_value = sclivevol
res = self.driver.initialize_connection(volume, connector)
expected = {'data':
{'discard': True,
'initiator_target_map':
{u'21000024FF30441C': [u'5000D31000FCBE35'],
u'21000024FF30441D': [u'5000D31000FCBE3D']},
'target_discovered': True,
'target_lun': 1,
'target_wwn': [u'5000D31000FCBE3D', u'5000D31000FCBE35']},
'driver_volume_type': 'fibre_channel'}
self.assertEqual(expected, res, 'Unexpected return data')
# verify find_volume has been called and that is has been called twice
self.assertFalse(mock_initialize_secondary.called)
mock_find_volume.assert_called_once_with(
fake.VOLUME_ID, '101.101', True)
mock_get_volume.assert_called_once_with('102.101')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_server', 'find_server',
return_value=SCSERVER) return_value=SCSERVER)
@ -581,11 +650,7 @@ class DellSCSanFCDriverTestCase(test.TestCase):
volume = {'id': fake.VOLUME_ID} volume = {'id': fake.VOLUME_ID}
connector = self.connector connector = self.connector
mock_terminate_secondary.return_value = (None, [], {}) mock_terminate_secondary.return_value = (None, [], {})
sclivevol = {'instanceId': '101.101', mock_is_live_vol.return_value = True
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
mock_is_live_vol.return_value = sclivevol
res = self.driver.terminate_connection(volume, connector) res = self.driver.terminate_connection(volume, connector)
mock_unmap_volume.assert_called_once_with(self.VOLUME, self.SCSERVER) mock_unmap_volume.assert_called_once_with(self.VOLUME, self.SCSERVER)
expected = {'driver_volume_type': 'fibre_channel', expected = {'driver_volume_type': 'fibre_channel',

View File

@ -518,9 +518,9 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
sclivevol = {'instanceId': '101.101', sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101', 'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID}, 'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102} 'secondaryScSerialNumber': 102,
mock_api.get_live_volume = mock.MagicMock(return_value=(sclivevol, 'secondaryRole': 'Secondary'}
False)) mock_api.get_live_volume = mock.MagicMock(return_value=sclivevol)
# No replication driver data. # No replication driver data.
ret = self.driver._delete_live_volume(mock_api, vol) ret = self.driver._delete_live_volume(mock_api, vol)
self.assertFalse(ret) self.assertFalse(ret)
@ -538,7 +538,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
ret = self.driver._delete_live_volume(mock_api, vol) ret = self.driver._delete_live_volume(mock_api, vol)
self.assertFalse(ret) self.assertFalse(ret)
# No live volume found. # No live volume found.
mock_api.get_live_volume.return_value = (None, False) mock_api.get_live_volume.return_value = None
ret = self.driver._delete_live_volume(mock_api, vol) ret = self.driver._delete_live_volume(mock_api, vol)
self.assertFalse(ret) self.assertFalse(ret)
@ -786,7 +786,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
data = self.driver.initialize_connection(volume, connector) data = self.driver.initialize_connection(volume, connector)
self.assertEqual('iscsi', data['driver_volume_type']) self.assertEqual('iscsi', data['driver_volume_type'])
# verify find_volume has been called and that is has been called twice # verify find_volume has been called and that is has been called twice
mock_find_volume.assert_called_once_with(fake.VOLUME_ID, provider_id) mock_find_volume.assert_called_once_with(
fake.VOLUME_ID, provider_id, False)
mock_get_volume.assert_called_once_with(provider_id) mock_get_volume.assert_called_once_with(provider_id)
expected = {'data': self.ISCSI_PROPERTIES, expected = {'data': self.ISCSI_PROPERTIES,
'driver_volume_type': 'iscsi'} 'driver_volume_type': 'iscsi'}
@ -990,11 +991,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
mock_init): mock_init):
volume = {'id': fake.VOLUME_ID} volume = {'id': fake.VOLUME_ID}
connector = self.connector connector = self.connector
sclivevol = {'instanceId': '101.101', mock_is_live_vol.return_value = True
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
mock_is_live_vol.return_value = sclivevol
lvol_properties = {'access_mode': 'rw', lvol_properties = {'access_mode': 'rw',
'target_discovered': False, 'target_discovered': False,
'target_iqn': 'target_iqn':
@ -1018,6 +1015,75 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'driver_volume_type': 'iscsi'} 'driver_volume_type': 'iscsi'}
self.assertEqual(expected, ret) self.assertEqual(expected, ret)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_server',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'create_server',
return_value=SCSERVER)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'get_volume')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'map_volume',
return_value=MAPPINGS[0])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_iscsi_properties')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'get_live_volume')
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
'_is_live_vol')
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
'initialize_secondary')
def test_initialize_connection_live_volume_afo(self,
mock_initialize_secondary,
mock_is_live_vol,
mock_get_live_vol,
mock_find_iscsi_props,
mock_map_volume,
mock_get_volume,
mock_find_volume,
mock_create_server,
mock_find_server,
mock_close_connection,
mock_open_connection,
mock_init):
volume = {'id': fake.VOLUME_ID, 'provider_id': '101.101'}
scvol = {'instanceId': '102.101'}
mock_find_volume.return_value = scvol
mock_get_volume.return_value = scvol
connector = self.connector
sclivevol = {'instanceId': '101.10001',
'primaryVolume': {'instanceId': '101.101',
'instanceName': fake.VOLUME_ID},
'primaryScSerialNumber': 101,
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102,
'secondaryRole': 'Activated'}
mock_is_live_vol.return_value = True
mock_get_live_vol.return_value = sclivevol
props = {
'access_mode': 'rw',
'target_discovered': False,
'target_iqn': u'iqn:1',
'target_iqns': [u'iqn:1',
u'iqn:2'],
'target_lun': 1,
'target_luns': [1, 1],
'target_portal': u'192.168.1.21:3260',
'target_portals': [u'192.168.1.21:3260',
u'192.168.1.22:3260']
}
mock_find_iscsi_props.return_value = props
ret = self.driver.initialize_connection(volume, connector)
expected = {'data': props,
'driver_volume_type': 'iscsi'}
expected['data']['discard'] = True
self.assertEqual(expected, ret)
self.assertFalse(mock_initialize_secondary.called)
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver, @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
'_get_replication_specs', '_get_replication_specs',
return_value={'enabled': True, 'live': True}) return_value={'enabled': True, 'live': True})
@ -1230,9 +1296,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
sclivevol = {'instanceId': '101.101', sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101', 'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID}, 'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102} 'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
mock_is_live_vol.return_value = True mock_is_live_vol.return_value = True
mock_get_live_vol.return_value = (sclivevol, False) mock_get_live_vol.return_value = sclivevol
connector = self.connector connector = self.connector
res = self.driver.terminate_connection(volume, connector) res = self.driver.terminate_connection(volume, connector)
mock_unmap_volume.assert_called_once_with(self.VOLUME, self.SCSERVER) mock_unmap_volume.assert_called_once_with(self.VOLUME, self.SCSERVER)
@ -2661,36 +2728,36 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'instanceName': fake.VOLUME2_ID}, 'instanceName': fake.VOLUME2_ID},
'secondaryVolume': {'instanceId': '102.101', 'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID}, 'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102} 'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
postfail = {'instanceId': '101.100', postfail = {'instanceId': '101.100',
'primaryVolume': {'instanceId': '102.101', 'primaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID}, 'instanceName': fake.VOLUME_ID},
'secondaryVolume': {'instanceId': '101.101', 'secondaryVolume': {'instanceId': '101.101',
'instanceName': fake.VOLUME2_ID}, 'instanceName': fake.VOLUME2_ID},
'secondaryScSerialNumber': 102} 'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
mock_api.get_live_volume = mock.MagicMock() mock_api.get_live_volume = mock.MagicMock()
mock_api.get_live_volume.side_effect = [(sclivevol, False), mock_api.get_live_volume.side_effect = [sclivevol, postfail,
(postfail, True), sclivevol, sclivevol]
(sclivevol, False),
(sclivevol, False)
]
# Good run. # Good run.
mock_api.is_swapped = mock.MagicMock(return_value=False)
mock_api.swap_roles_live_volume = mock.MagicMock(return_value=True) mock_api.swap_roles_live_volume = mock.MagicMock(return_value=True)
model_update = {'provider_id': '102.101', model_update = {'provider_id': '102.101',
'replication_status': 'failed-over'} 'replication_status': 'failed-over'}
ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID, ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID,
'101.100') '101.101')
self.assertEqual(model_update, ret) self.assertEqual(model_update, ret)
# Swap fail # Swap fail
mock_api.swap_roles_live_volume.return_value = False mock_api.swap_roles_live_volume.return_value = False
model_update = {'status': 'error'} model_update = {'status': 'error'}
ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID, ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID,
'101.100') '101.101')
self.assertEqual(model_update, ret) self.assertEqual(model_update, ret)
# Can't find live volume. # Can't find live volume.
mock_api.get_live_volume.return_value = (None, False) mock_api.get_live_volume.return_value = None
ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID, ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID,
'101.100') '101.101')
self.assertEqual(model_update, ret) self.assertEqual(model_update, ret)
def test__failover_replication(self, def test__failover_replication(self,
@ -3206,16 +3273,16 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
{'id': fake.VOLUME2_ID, {'id': fake.VOLUME2_ID,
'replication_driver_data': '12345', 'replication_driver_data': '12345',
'provider_id': '12345.2'}] 'provider_id': '12345.2'}]
mock_get_live_volume.side_effect = [( mock_get_live_volume.side_effect = [
{'instanceId': '11111.101', {'instanceId': '11111.101',
'secondaryVolume': {'instanceId': '11111.1001', 'secondaryVolume': {'instanceId': '11111.1001',
'instanceName': fake.VOLUME_ID}, 'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 11111}, True), ( 'secondaryScSerialNumber': 11111},
{'instanceId': '11111.102', {'instanceId': '11111.102',
'secondaryVolume': {'instanceId': '11111.1002', 'secondaryVolume': {'instanceId': '11111.1002',
'instanceName': fake.VOLUME2_ID}, 'instanceName': fake.VOLUME2_ID},
'secondaryScSerialNumber': 11111}, True 'secondaryScSerialNumber': 11111}
)] ]
mock_get_replication_specs.return_value = {'enabled': True, mock_get_replication_specs.return_value = {'enabled': True,
'live': True} 'live': True}
mock_swap_roles_live_volume.side_effect = [True, True] mock_swap_roles_live_volume.side_effect = [True, True]

View File

@ -21,6 +21,7 @@ import uuid
from cinder import context from cinder import context
from cinder import exception from cinder import exception
from cinder import test from cinder import test
from cinder.tests.unit import fake_constants as fake
from cinder.volume.drivers.dell import dell_storagecenter_api from cinder.volume.drivers.dell import dell_storagecenter_api
@ -5977,7 +5978,7 @@ class DellSCSanAPITestCase(test.TestCase):
expected = 'StorageCenter/ScReplication/%s' % ( expected = 'StorageCenter/ScReplication/%s' % (
self.SCREPL[0]['instanceId']) self.SCREPL[0]['instanceId'])
expected_payload = {'DeleteDestinationVolume': True, expected_payload = {'DeleteDestinationVolume': True,
'RecycleDestinationVolume': False, 'RecycleDestinationVolume': True,
'DeleteRestorePoint': True} 'DeleteRestorePoint': True}
ret = self.scapi.delete_replication(self.VOLUME, destssn) ret = self.scapi.delete_replication(self.VOLUME, destssn)
mock_delete.assert_any_call(expected, payload=expected_payload, mock_delete.assert_any_call(expected, payload=expected_payload,
@ -6014,7 +6015,7 @@ class DellSCSanAPITestCase(test.TestCase):
expected = 'StorageCenter/ScReplication/%s' % ( expected = 'StorageCenter/ScReplication/%s' % (
self.SCREPL[0]['instanceId']) self.SCREPL[0]['instanceId'])
expected_payload = {'DeleteDestinationVolume': True, expected_payload = {'DeleteDestinationVolume': True,
'RecycleDestinationVolume': False, 'RecycleDestinationVolume': True,
'DeleteRestorePoint': True} 'DeleteRestorePoint': True}
ret = self.scapi.delete_replication(self.VOLUME, destssn) ret = self.scapi.delete_replication(self.VOLUME, destssn)
mock_delete.assert_any_call(expected, payload=expected_payload, mock_delete.assert_any_call(expected, payload=expected_payload,
@ -6399,37 +6400,60 @@ class DellSCSanAPITestCase(test.TestCase):
scvol, scvol,
'a,b') 'a,b')
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json') '_sc_live_volumes')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_live_volumes')
def test_get_live_volume(self, def test_get_live_volume(self,
mock_get_json, mock_get_live_volumes,
mock_get, mock_sc_live_volumes,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
# Basic check # Basic check
retlv, retswapped = self.scapi.get_live_volume(None) retlv = self.scapi.get_live_volume(None)
self.assertIsNone(retlv) self.assertIsNone(retlv)
self.assertFalse(retswapped)
lv1 = {'primaryVolume': {'instanceId': '12345.1'}, lv1 = {'primaryVolume': {'instanceId': '12345.1'},
'secondaryVolume': {'instanceId': '67890.1'}} 'secondaryVolume': {'instanceId': '67890.1'}}
lv2 = {'primaryVolume': {'instanceId': '12345.2'}} lv2 = {'primaryVolume': {'instanceId': '12345.2'}}
mock_get_json.return_value = [lv1, lv2] mock_sc_live_volumes.return_value = [lv1, lv2]
mock_get.return_value = self.RESPONSE_200
# Good Run # Good Run
retlv, retswapped = self.scapi.get_live_volume('12345.2') retlv = self.scapi.get_live_volume('12345.2')
self.assertEqual(lv2, retlv) self.assertEqual(lv2, retlv)
self.assertFalse(retswapped) mock_sc_live_volumes.assert_called_once_with('12345')
self.assertFalse(mock_get_live_volumes.called)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json') '_sc_live_volumes')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_live_volumes')
def test_get_live_volume_on_secondary(self,
mock_get_live_volumes,
mock_sc_live_volumes,
mock_close_connection,
mock_open_connection,
mock_init):
# Basic check
retlv = self.scapi.get_live_volume(None)
self.assertIsNone(retlv)
lv1 = {'primaryVolume': {'instanceId': '12345.1'},
'secondaryVolume': {'instanceId': '67890.1'}}
lv2 = {'primaryVolume': {'instanceId': '12345.2'}}
mock_sc_live_volumes.return_value = []
mock_get_live_volumes.return_value = [lv1, lv2]
# Good Run
retlv = self.scapi.get_live_volume('12345.2')
self.assertEqual(lv2, retlv)
mock_sc_live_volumes.assert_called_once_with('12345')
mock_get_live_volumes.assert_called_once_with()
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_sc_live_volumes')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_live_volumes')
def test_get_live_volume_not_found(self, def test_get_live_volume_not_found(self,
mock_get_json, mock_get_live_volumes,
mock_get, mock_sc_live_volumes,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
@ -6437,19 +6461,20 @@ class DellSCSanAPITestCase(test.TestCase):
'secondaryVolume': {'instanceId': '67890.1'}} 'secondaryVolume': {'instanceId': '67890.1'}}
lv2 = {'primaryVolume': {'instanceId': '12345.2'}, lv2 = {'primaryVolume': {'instanceId': '12345.2'},
'secondaryVolume': {'instanceId': '67890.2'}} 'secondaryVolume': {'instanceId': '67890.2'}}
mock_get_json.return_value = [lv1, lv2] mock_get_live_volumes.return_value = [lv1, lv2]
mock_get.return_value = self.RESPONSE_200 mock_sc_live_volumes.return_value = []
retlv, retswapped = self.scapi.get_live_volume('12345.3') retlv = self.scapi.get_live_volume('12345.3')
self.assertIsNone(retlv) self.assertIsNone(retlv)
self.assertFalse(retswapped) mock_sc_live_volumes.assert_called_once_with('12345')
mock_get_live_volumes.assert_called_once_with()
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json') '_sc_live_volumes')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_live_volumes')
def test_get_live_volume_swapped(self, def test_get_live_volume_swapped(self,
mock_get_json, mock_get_live_volumes,
mock_get, mock_sc_live_volumes,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
@ -6457,23 +6482,50 @@ class DellSCSanAPITestCase(test.TestCase):
'secondaryVolume': {'instanceId': '67890.1'}} 'secondaryVolume': {'instanceId': '67890.1'}}
lv2 = {'primaryVolume': {'instanceId': '67890.2'}, lv2 = {'primaryVolume': {'instanceId': '67890.2'},
'secondaryVolume': {'instanceId': '12345.2'}} 'secondaryVolume': {'instanceId': '12345.2'}}
mock_get_json.return_value = [lv1, lv2] mock_get_live_volumes.return_value = [lv1, lv2]
mock_get.return_value = self.RESPONSE_200 mock_sc_live_volumes.return_value = []
retlv, retswapped = self.scapi.get_live_volume('12345.2') retlv = self.scapi.get_live_volume('12345.2')
self.assertEqual(lv2, retlv) self.assertEqual(lv2, retlv)
self.assertTrue(retswapped) mock_sc_live_volumes.assert_called_once_with('12345')
mock_get_live_volumes.assert_called_once_with()
@mock.patch.object(dell_storagecenter_api.HttpClient, @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'get') '_sc_live_volumes')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_live_volumes')
def test_get_live_volume_error(self, def test_get_live_volume_error(self,
mock_get, mock_get_live_volumes,
mock_sc_live_volumes,
mock_close_connection, mock_close_connection,
mock_open_connection, mock_open_connection,
mock_init): mock_init):
mock_get.return_value = self.RESPONSE_400 mock_get_live_volumes.return_value = []
retlv, retswapped = self.scapi.get_live_volume('12345.2') mock_sc_live_volumes.return_value = []
retlv = self.scapi.get_live_volume('12345.2')
self.assertIsNone(retlv) self.assertIsNone(retlv)
self.assertFalse(retswapped)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_sc_live_volumes')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_live_volumes')
def test_get_live_volume_by_name(self,
mock_get_live_volumes,
mock_sc_live_volumes,
mock_close_connection,
mock_open_connection,
mock_init):
lv1 = {'primaryVolume': {'instanceId': '12345.1'},
'secondaryVolume': {'instanceId': '67890.1'},
'instanceName': 'Live volume of ' + fake.VOLUME2_ID}
lv2 = {'primaryVolume': {'instanceId': '67890.2'},
'secondaryVolume': {'instanceId': '12345.2'},
'instanceName': 'Live volume of ' + fake.VOLUME_ID}
mock_get_live_volumes.return_value = [lv1, lv2]
mock_sc_live_volumes.return_value = []
retlv = self.scapi.get_live_volume('12345.2', fake.VOLUME_ID)
self.assertEqual(lv2, retlv)
mock_sc_live_volumes.assert_called_once_with('12345')
mock_get_live_volumes.assert_called_once_with()
@mock.patch.object(dell_storagecenter_api.HttpClient, @mock.patch.object(dell_storagecenter_api.HttpClient,
'post') 'post')

View File

@ -1091,20 +1091,26 @@ class StorageCenterApi(object):
def _autofailback(self, lv): def _autofailback(self, lv):
# if we have a working replication state. # if we have a working replication state.
ret = False ret = False
if (lv['ReplicationState'] == 'Up' and LOG.debug('Attempting autofailback of %s', lv)
lv['failoverState'] == 'AutoFailedOver'): if (lv and lv['status'] == 'Up' and lv['replicationState'] == 'Up' and
lv['failoverState'] == 'Protected' and lv['secondaryStatus'] == 'Up'
and lv['primarySwapRoleState'] == 'NotSwapping'):
ret = self.swap_roles_live_volume(lv) ret = self.swap_roles_live_volume(lv)
return ret return ret
def _find_volume_primary(self, provider_id): def _find_volume_primary(self, provider_id, name):
# if there is no live volume then we return our provider_id. # if there is no live volume then we return our provider_id.
primary_id = provider_id primary_id = provider_id
lv, swapped = self.get_live_volume(provider_id) lv = self.get_live_volume(provider_id, name)
# if we swapped see if we can autofailback. Unless the admin LOG.info(_LI('Volume %(provider)s at primary %(primary)s.'),
# failed us over, that is. {'provider': provider_id, 'primary': primary_id})
if swapped and not self.failed_over: # If we have a live volume and are swapped and are not failed over
# at least give failback a shot.
if lv and self.is_swapped(provider_id, lv) and not self.failed_over:
if self._autofailback(lv): if self._autofailback(lv):
ls, swapped = self.get_live_volume(provider_id) lv = self.get_live_volume(provider_id)
LOG.info(_LI('After failback %s'), lv)
if lv: if lv:
primary_id = lv['primaryVolume']['instanceId'] primary_id = lv['primaryVolume']['instanceId']
return primary_id return primary_id
@ -1127,7 +1133,7 @@ class StorageCenterApi(object):
scvolume = None scvolume = None
if islivevol: if islivevol:
# Just get the primary from the sc live vol. # Just get the primary from the sc live vol.
primary_id = self._find_volume_primary(provider_id) primary_id = self._find_volume_primary(provider_id, name)
scvolume = self.get_volume(primary_id) scvolume = self.get_volume(primary_id)
elif self._use_provider_id(provider_id): elif self._use_provider_id(provider_id):
# just get our volume # just get our volume
@ -2733,7 +2739,7 @@ class StorageCenterApi(object):
if replication: if replication:
payload = {} payload = {}
payload['DeleteDestinationVolume'] = deletedestvolume payload['DeleteDestinationVolume'] = deletedestvolume
payload['RecycleDestinationVolume'] = False payload['RecycleDestinationVolume'] = deletedestvolume
payload['DeleteRestorePoint'] = True payload['DeleteRestorePoint'] = True
r = self.client.delete('StorageCenter/ScReplication/%s' % r = self.client.delete('StorageCenter/ScReplication/%s' %
self._get_id(replication), payload=payload, self._get_id(replication), payload=payload,
@ -3053,24 +3059,73 @@ class StorageCenterApi(object):
progress) progress)
return None, None return None, None
def get_live_volume(self, primaryid): def is_swapped(self, provider_id, sclivevolume):
if (sclivevolume.get('primaryVolume') and
sclivevolume['primaryVolume']['instanceId'] != provider_id):
return True
return False
def is_failed_over(self, provider_id, sclivevolume):
# either the secondary is active or the secondary is now our primary.
if (sclivevolume.get('secondaryRole') == 'Activated' or
self.is_swapped(provider_id, sclivevolume)):
return True
return False
def _sc_live_volumes(self, ssn):
if ssn:
r = self.client.get('StorageCenter/StorageCenter/%s/LiveVolumeList'
% ssn)
if self._check_result(r):
return self._get_json(r)
return []
def _get_live_volumes(self):
# Work around for a FW bug. Instead of grabbing the entire list at
# once we have to Trundle through each SC's list.
lvs = []
pf = self._get_payload_filter()
pf.append('connected', True)
r = self.client.post('StorageCenter/StorageCenter/GetList',
pf.payload)
if self._check_result(r):
# Should return [] if nothing there.
# Just in case do the or.
scs = self._get_json(r) or []
for sc in scs:
lvs += self._sc_live_volumes(self._get_id(sc))
return lvs
def get_live_volume(self, primaryid, name=None):
"""Get's the live ScLiveVolume object for the vol with primaryid. """Get's the live ScLiveVolume object for the vol with primaryid.
:param primaryid: InstanceId of the primary volume. :param primaryid: InstanceId of the primary volume.
:return: ScLiveVolume object or None, swapped True/False. :parma name: Volume name associated with this live volume.
:return: ScLiveVolume object or None
""" """
sclivevol = None
if primaryid: if primaryid:
r = self.client.get('StorageCenter/ScLiveVolume') # Try from our primary SSN. This would be the authoritay on the
if self._check_result(r): # Live Volume in question.
lvs = self._get_json(r) lvs = self._sc_live_volumes(primaryid.split('.')[0])
# No, grab them all and see if we are on the secondary.
if not lvs:
lvs = self._get_live_volumes()
if lvs:
# Look for our primaryid.
for lv in lvs: for lv in lvs:
if (lv.get('primaryVolume') and if ((lv.get('primaryVolume') and
lv['primaryVolume']['instanceId'] == primaryid): lv['primaryVolume']['instanceId'] == primaryid) or
return lv, False (lv.get('secondaryVolume') and
if (lv.get('secondaryVolume') and lv['secondaryVolume']['instanceId'] == primaryid)):
lv['secondaryVolume']['instanceId'] == primaryid): sclivevol = lv
return lv, True break
return None, False # Sometimes the lv object returns without a secondary
# volume. Make sure we find this by name if we have to.
if (name and sclivevol is None and
lv['instanceName'].endswith(name)):
sclivevol = lv
return sclivevol
def _get_hbas(self, serverid): def _get_hbas(self, serverid):
# Helper to get the hba's of a given server. # Helper to get the hba's of a given server.
@ -3170,6 +3225,7 @@ class StorageCenterApi(object):
payload['ConvertToReplication'] = False payload['ConvertToReplication'] = False
payload['DeleteSecondaryVolume'] = deletesecondaryvolume payload['DeleteSecondaryVolume'] = deletesecondaryvolume
payload['RecycleSecondaryVolume'] = deletesecondaryvolume payload['RecycleSecondaryVolume'] = deletesecondaryvolume
payload['DeleteRestorePoint'] = deletesecondaryvolume
r = self.client.delete('StorageCenter/ScLiveVolume/%s' % r = self.client.delete('StorageCenter/ScLiveVolume/%s' %
self._get_id(sclivevolume), payload, True) self._get_id(sclivevolume), payload, True)
if self._check_result(r): if self._check_result(r):

View File

@ -362,8 +362,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
ssnstrings = self._split_driver_data(replication_driver_data) ssnstrings = self._split_driver_data(replication_driver_data)
if ssnstrings: if ssnstrings:
ssn = int(ssnstrings[0]) ssn = int(ssnstrings[0])
sclivevolume, swapped = api.get_live_volume( sclivevolume = api.get_live_volume(volume.get('provider_id'))
volume.get('provider_id'))
# Have we found the live volume? # Have we found the live volume?
if (sclivevolume and if (sclivevolume and
sclivevolume.get('secondaryScSerialNumber') == ssn and sclivevolume.get('secondaryScSerialNumber') == ssn and
@ -682,12 +681,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
def _update_volume_stats(self): def _update_volume_stats(self):
"""Retrieve stats info from volume group.""" """Retrieve stats info from volume group."""
with self._client.open_connection() as api: with self._client.open_connection() as api:
storageusage = api.get_storage_usage() # Static stats.
if not storageusage:
msg = _('Unable to retrieve volume stats.')
raise exception.VolumeBackendAPIException(message=msg)
# all of this is basically static for now
data = {} data = {}
data['volume_backend_name'] = self.backend_name data['volume_backend_name'] = self.backend_name
data['vendor_name'] = 'Dell' data['vendor_name'] = 'Dell'
@ -696,12 +690,6 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
data['reserved_percentage'] = 0 data['reserved_percentage'] = 0
data['consistencygroup_support'] = True data['consistencygroup_support'] = True
data['thin_provisioning_support'] = True data['thin_provisioning_support'] = True
totalcapacity = storageusage.get('availableSpace')
totalcapacitygb = self._bytes_to_gb(totalcapacity)
data['total_capacity_gb'] = totalcapacitygb
freespace = storageusage.get('freeSpace')
freespacegb = self._bytes_to_gb(freespace)
data['free_capacity_gb'] = freespacegb
data['QoS_support'] = False data['QoS_support'] = False
data['replication_enabled'] = self.replication_enabled data['replication_enabled'] = self.replication_enabled
if self.replication_enabled: if self.replication_enabled:
@ -715,6 +703,22 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
replication_targets.append(target_device_id) replication_targets.append(target_device_id)
data['replication_targets'] = replication_targets data['replication_targets'] = replication_targets
# Get our capacity.
storageusage = api.get_storage_usage()
if storageusage:
# Get actual stats.
totalcapacity = storageusage.get('availableSpace')
totalcapacitygb = self._bytes_to_gb(totalcapacity)
data['total_capacity_gb'] = totalcapacitygb
freespace = storageusage.get('freeSpace')
freespacegb = self._bytes_to_gb(freespace)
data['free_capacity_gb'] = freespacegb
else:
# Soldier on. Just return 0 for this iteration.
LOG.error(_LE('Unable to retrieve volume stats.'))
data['total_capacity_gb'] = 0
data['free_capacity_gb'] = 0
self._stats = data self._stats = data
LOG.debug('Total cap %(total)s Free cap %(free)s', LOG.debug('Total cap %(total)s Free cap %(free)s',
{'total': data['total_capacity_gb'], {'total': data['total_capacity_gb'],
@ -1390,7 +1394,10 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
:return: model_update dict :return: model_update dict
""" """
model_update = {} model_update = {}
sclivevolume, swapped = api.get_live_volume(provider_id) # We do not search by name. Only failback if we have a complete
# LV object.
sclivevolume = api.get_live_volume(provider_id)
# TODO(tswanson): Check swapped state first.
if sclivevolume and api.swap_roles_live_volume(sclivevolume): if sclivevolume and api.swap_roles_live_volume(sclivevolume):
LOG.info(_LI('Success swapping sclivevolume roles %s'), id) LOG.info(_LI('Success swapping sclivevolume roles %s'), id)
model_update = { model_update = {
@ -1489,8 +1496,10 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
def _failover_live_volume(self, api, id, provider_id): def _failover_live_volume(self, api, id, provider_id):
model_update = {} model_update = {}
sclivevolume, swapped = api.get_live_volume(provider_id) # Search for volume by id if we have to.
sclivevolume = api.get_live_volume(provider_id, id)
if sclivevolume: if sclivevolume:
swapped = api.is_swapped(provider_id, sclivevolume)
# If we aren't swapped try it. If fail error out. # If we aren't swapped try it. If fail error out.
if not swapped and not api.swap_roles_live_volume(sclivevolume): if not swapped and not api.swap_roles_live_volume(sclivevolume):
LOG.info(_LI('Failure swapping roles %s'), id) LOG.info(_LI('Failure swapping roles %s'), id)
@ -1498,7 +1507,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
return model_update return model_update
LOG.info(_LI('Success swapping sclivevolume roles %s'), id) LOG.info(_LI('Success swapping sclivevolume roles %s'), id)
sclivevolume, swapped = api.get_live_volume(provider_id) sclivevolume = api.get_live_volume(provider_id)
model_update = { model_update = {
'replication_status': 'replication_status':
fields.ReplicationStatus.FAILED_OVER, fields.ReplicationStatus.FAILED_OVER,

View File

@ -90,52 +90,59 @@ class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver,
with self._client.open_connection() as api: with self._client.open_connection() as api:
try: try:
wwpns = connector.get('wwpns') wwpns = connector.get('wwpns')
# Find our server. # Find the volume on the storage center. Note that if this
scserver = self._find_server(api, wwpns) # is live volume and we are swapped this will be the back
# half of the live volume.
# No? Create it.
if scserver is None:
scserver = api.create_server(
wwpns, self.configuration.dell_server_os)
# Find the volume on the storage center.
scvolume = api.find_volume(volume_name, provider_id, islivevol) scvolume = api.find_volume(volume_name, provider_id, islivevol)
if scserver is not None and scvolume is not None: if scvolume:
mapping = api.map_volume(scvolume, scserver) # Get the SSN it is on.
if mapping is not None: ssn = scvolume['instanceId'].split('.')[0]
# Since we just mapped our volume we had best update # Find our server.
# our sc volume object. scserver = self._find_server(api, wwpns, ssn)
scvolume = api.get_volume(scvolume['instanceId'])
lun, targets, init_targ_map = api.find_wwns(scvolume,
scserver)
# Do we have extra live volume work? # No? Create it.
if islivevol: if scserver is None:
# Get our volume and our swap state. scserver = api.create_server(
sclivevolume, swapped = api.get_live_volume( wwpns, self.configuration.dell_server_os, ssn)
provider_id) # We have a volume and a server. Map them.
# Do not map to a failed over volume. if scserver is not None:
if sclivevolume and not swapped: mapping = api.map_volume(scvolume, scserver)
# Now map our secondary. if mapping is not None:
lvlun, lvtargets, lvinit_targ_map = ( # Since we just mapped our volume we had
self.initialize_secondary(api, # best update our sc volume object.
sclivevolume, scvolume = api.get_volume(scvolume['instanceId'])
wwpns)) lun, targets, init_targ_map = api.find_wwns(
# Unmapped. Add info to our list. scvolume, scserver)
targets += lvtargets
init_targ_map.update(lvinit_targ_map)
# Roll up our return data. # Do we have extra live volume work?
if lun is not None and len(targets) > 0: if islivevol:
data = {'driver_volume_type': 'fibre_channel', # Get our live volume.
'data': {'target_lun': lun, sclivevolume = api.get_live_volume(provider_id)
'target_discovered': True, # Do not map to a failed over volume.
'target_wwn': targets, if (sclivevolume and not
'initiator_target_map': api.is_failed_over(provider_id,
init_targ_map, sclivevolume)):
'discard': True}} # Now map our secondary.
LOG.debug('Return FC data: %s', data) lvlun, lvtargets, lvinit_targ_map = (
return data self.initialize_secondary(api,
LOG.error(_LE('Lun mapping returned null!')) sclivevolume,
wwpns))
# Unmapped. Add info to our list.
targets += lvtargets
init_targ_map.update(lvinit_targ_map)
# Roll up our return data.
if lun is not None and len(targets) > 0:
data = {'driver_volume_type': 'fibre_channel',
'data': {'target_lun': lun,
'target_discovered': True,
'target_wwn': targets,
'initiator_target_map':
init_targ_map,
'discard': True}}
LOG.debug('Return FC data: %s', data)
return data
LOG.error(_LE('Lun mapping returned null!'))
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
@ -191,46 +198,53 @@ class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver,
with self._client.open_connection() as api: with self._client.open_connection() as api:
try: try:
wwpns = connector.get('wwpns') wwpns = connector.get('wwpns')
scserver = self._find_server(api, wwpns)
# Find the volume on the storage center. # Find the volume on the storage center.
scvolume = api.find_volume(volume_name, provider_id, islivevol) scvolume = api.find_volume(volume_name, provider_id, islivevol)
# Get our target map so we can return it to free up a zone. if scvolume:
lun, targets, init_targ_map = api.find_wwns(scvolume, scserver) # Get the SSN it is on.
ssn = scvolume['instanceId'].split('.')[0]
# Do we have extra live volume work? scserver = self._find_server(api, wwpns, ssn)
if islivevol:
# Get our volume and our swap state.
sclivevolume, swapped = api.get_live_volume(
provider_id)
# Do not map to a failed over volume.
if sclivevolume and not swapped:
lvlun, lvtargets, lvinit_targ_map = (
self.terminate_secondary(api, sclivevolume, wwpns))
# Add to our return.
if lvlun:
targets += lvtargets
init_targ_map.update(lvinit_targ_map)
# If we have a server and a volume lets unmap them. # Get our target map so we can return it to free up a zone.
if (scserver is not None and lun, targets, init_targ_map = api.find_wwns(scvolume,
scvolume is not None and scserver)
api.unmap_volume(scvolume, scserver) is True):
LOG.debug('Connection terminated')
else:
raise exception.VolumeBackendAPIException(
_('Terminate connection failed'))
# basic return info... # Do we have extra live volume work?
info = {'driver_volume_type': 'fibre_channel', if islivevol:
'data': {}} # Get our live volume.
sclivevolume = api.get_live_volume(provider_id)
# Do not map to a failed over volume.
if (sclivevolume and not
api.is_failed_over(provider_id,
sclivevolume)):
lvlun, lvtargets, lvinit_targ_map = (
self.terminate_secondary(
api, sclivevolume, wwpns))
# Add to our return.
if lvlun:
targets += lvtargets
init_targ_map.update(lvinit_targ_map)
# if not then we return the target map so that # If we have a server and a volume lets unmap them.
# the zone can be freed up. if (scserver is not None and
if api.get_volume_count(scserver) == 0: scvolume is not None and
info['data'] = {'target_wwn': targets, api.unmap_volume(scvolume, scserver) is True):
'initiator_target_map': init_targ_map} LOG.debug('Connection terminated')
return info else:
raise exception.VolumeBackendAPIException(
_('Terminate connection failed'))
# basic return info...
info = {'driver_volume_type': 'fibre_channel',
'data': {}}
# if not then we return the target map so that
# the zone can be freed up.
if api.get_volume_count(scserver) == 0:
info['data'] = {'target_wwn': targets,
'initiator_target_map': init_targ_map}
return info
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():

View File

@ -96,59 +96,68 @@ class DellStorageCenterISCSIDriver(dell_storagecenter_common.DellCommonDriver,
with self._client.open_connection() as api: with self._client.open_connection() as api:
try: try:
# Find our server. # Find the volume on the storage center. Note that if this
scserver = api.find_server(initiator_name) # is live volume and we are swapped this will be the back
# No? Create it. # half of the live volume.
if scserver is None: scvolume = api.find_volume(volume_name, provider_id, islivevol)
scserver = api.create_server( if scvolume:
[initiator_name], self.configuration.dell_server_os) # Get the SSN it is on.
# Find the volume on the storage center. ssn = scvolume['instanceId'].split('.')[0]
scvolume = api.find_volume(volume_name, provider_id) # Find our server.
scserver = api.find_server(initiator_name, ssn)
# No? Create it.
if scserver is None:
scserver = api.create_server(
[initiator_name],
self.configuration.dell_server_os, ssn)
# if we have a server and a volume lets bring them together. # if we have a server and a volume lets bring them
if scserver is not None and scvolume is not None: # together.
mapping = api.map_volume(scvolume, scserver) if scserver is not None:
if mapping is not None: mapping = api.map_volume(scvolume, scserver)
# Since we just mapped our volume we had best update if mapping is not None:
# our sc volume object. # Since we just mapped our volume we had best
scvolume = api.get_volume(provider_id) # update our sc volume object.
# Our return. scvolume = api.get_volume(scvolume['instanceId'])
iscsiprops = {} # Our return.
iscsiprops = {}
# Three cases that should all be satisfied with the # Three cases that should all be satisfied with the
# same return of Target_Portal and Target_Portals. # same return of Target_Portal and Target_Portals.
# 1. Nova is calling us so we need to return the # 1. Nova is calling us so we need to return the
# Target_Portal stuff. It should ignore the # Target_Portal stuff. It should ignore the
# Target_Portals stuff. # Target_Portals stuff.
# 2. OS brick is calling us in multipath mode so we # 2. OS brick is calling us in multipath mode so we
# want to return Target_Portals. It will ignore # want to return Target_Portals. It will ignore
# the Target_Portal stuff. # the Target_Portal stuff.
# 3. OS brick is calling us in single path mode so # 3. OS brick is calling us in single path mode so
# we want to return Target_Portal and # we want to return Target_Portal and
# Target_Portals as alternates. # Target_Portals as alternates.
iscsiprops = api.find_iscsi_properties(scvolume) iscsiprops = api.find_iscsi_properties(scvolume)
# If this is a live volume we need to map up our # If this is a live volume we need to map up our
# secondary volume. # secondary volume. Note that if we have failed
if islivevol: # over we do not wish to do this.
sclivevolume, swapped = api.get_live_volume( if islivevol:
provider_id) sclivevolume = api.get_live_volume(provider_id)
# Only map if we are not swapped. # Only map if we are not failed over.
if sclivevolume and not swapped: if (sclivevolume and not
secondaryprops = self.initialize_secondary( api.is_failed_over(provider_id,
api, sclivevolume, initiator_name) sclivevolume)):
# Combine with iscsiprops secondaryprops = self.initialize_secondary(
iscsiprops['target_iqns'] += ( api, sclivevolume, initiator_name)
secondaryprops['target_iqns']) # Combine with iscsiprops
iscsiprops['target_portals'] += ( iscsiprops['target_iqns'] += (
secondaryprops['target_portals']) secondaryprops['target_iqns'])
iscsiprops['target_luns'] += ( iscsiprops['target_portals'] += (
secondaryprops['target_luns']) secondaryprops['target_portals'])
iscsiprops['target_luns'] += (
secondaryprops['target_luns'])
# Return our iscsi properties. # Return our iscsi properties.
iscsiprops['discard'] = True iscsiprops['discard'] = True
return {'driver_volume_type': 'iscsi', return {'driver_volume_type': 'iscsi',
'data': iscsiprops} 'data': iscsiprops}
# Re-raise any backend exception. # Re-raise any backend exception.
except exception.VolumeBackendAPIException: except exception.VolumeBackendAPIException:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
@ -214,23 +223,31 @@ class DellStorageCenterISCSIDriver(dell_storagecenter_common.DellCommonDriver,
'initiator': initiator_name}) 'initiator': initiator_name})
with self._client.open_connection() as api: with self._client.open_connection() as api:
try: try:
scserver = api.find_server(initiator_name) # Find the volume on the storage center. Note that if this
# Find the volume on the storage center. # is live volume and we are swapped this will be the back
scvolume = api.find_volume(volume_name, provider_id) # half of the live volume.
scvolume = api.find_volume(volume_name, provider_id, islivevol)
if scvolume:
# Get the SSN it is on.
ssn = scvolume['instanceId'].split('.')[0]
# Find our server.
scserver = api.find_server(initiator_name, ssn)
# Unmap our secondary if it isn't swapped. # Unmap our secondary if not failed over..
if islivevol: if islivevol:
sclivevolume, swapped = api.get_live_volume(provider_id) sclivevolume = api.get_live_volume(provider_id)
if sclivevolume and not swapped: if (sclivevolume and not
self.terminate_secondary(api, sclivevolume, api.is_failed_over(provider_id,
initiator_name) sclivevolume)):
self.terminate_secondary(api, sclivevolume,
initiator_name)
# If we have a server and a volume lets pull them apart. # If we have a server and a volume lets pull them apart.
if (scserver is not None and if (scserver is not None and
scvolume is not None and scvolume is not None and
api.unmap_volume(scvolume, scserver) is True): api.unmap_volume(scvolume, scserver) is True):
LOG.debug('Connection terminated') LOG.debug('Connection terminated')
return return
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.error(_LE('Failed to terminate connection ' LOG.error(_LE('Failed to terminate connection '