Merge "Dell SC: Error attaching after LV-AFO"
This commit is contained in:
commit
860c183e82
|
@ -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',
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -6027,7 +6028,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,
|
||||||
|
@ -6064,7 +6065,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,
|
||||||
|
@ -6449,37 +6450,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):
|
||||||
|
@ -6487,19 +6511,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):
|
||||||
|
@ -6507,23 +6532,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')
|
||||||
|
|
|
@ -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
|
||||||
|
@ -2747,7 +2753,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,
|
||||||
|
@ -3067,24 +3073,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.
|
||||||
|
@ -3184,6 +3239,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):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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 '
|
||||||
|
|
Loading…
Reference in New Issue