Merge "Dell SC: Error attaching after LV-AFO"

This commit is contained in:
Jenkins 2016-09-22 19:52:10 +00:00 committed by Gerrit Code Review
commit 860c183e82
7 changed files with 531 additions and 251 deletions

View File

@ -262,7 +262,8 @@ class DellSCSanFCDriverTestCase(test.TestCase):
sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
mock_is_live_volume.return_value = True
mock_find_wwns.return_value = (
1, [u'5000D31000FCBE3D', u'5000D31000FCBE35'],
@ -272,7 +273,7 @@ class DellSCSanFCDriverTestCase(test.TestCase):
1, [u'5000D31000FCBE3E', u'5000D31000FCBE36'],
{u'21000024FF30441E': [u'5000D31000FCBE36'],
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)
expected = {'data':
{'discard': True,
@ -292,6 +293,74 @@ class DellSCSanFCDriverTestCase(test.TestCase):
mock_find_volume.assert_called_once_with(fake.VOLUME_ID, None, True)
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,
'find_server',
return_value=SCSERVER)
@ -581,11 +650,7 @@ class DellSCSanFCDriverTestCase(test.TestCase):
volume = {'id': fake.VOLUME_ID}
connector = self.connector
mock_terminate_secondary.return_value = (None, [], {})
sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
mock_is_live_vol.return_value = sclivevol
mock_is_live_vol.return_value = True
res = self.driver.terminate_connection(volume, connector)
mock_unmap_volume.assert_called_once_with(self.VOLUME, self.SCSERVER)
expected = {'driver_volume_type': 'fibre_channel',

View File

@ -518,9 +518,9 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
mock_api.get_live_volume = mock.MagicMock(return_value=(sclivevol,
False))
'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
mock_api.get_live_volume = mock.MagicMock(return_value=sclivevol)
# No replication driver data.
ret = self.driver._delete_live_volume(mock_api, vol)
self.assertFalse(ret)
@ -538,7 +538,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
ret = self.driver._delete_live_volume(mock_api, vol)
self.assertFalse(ret)
# 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)
self.assertFalse(ret)
@ -786,7 +786,8 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
data = self.driver.initialize_connection(volume, connector)
self.assertEqual('iscsi', data['driver_volume_type'])
# 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)
expected = {'data': self.ISCSI_PROPERTIES,
'driver_volume_type': 'iscsi'}
@ -990,11 +991,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
mock_init):
volume = {'id': fake.VOLUME_ID}
connector = self.connector
sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
mock_is_live_vol.return_value = sclivevol
mock_is_live_vol.return_value = True
lvol_properties = {'access_mode': 'rw',
'target_discovered': False,
'target_iqn':
@ -1018,6 +1015,75 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'driver_volume_type': 'iscsi'}
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,
'_get_replication_specs',
return_value={'enabled': True, 'live': True})
@ -1230,9 +1296,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
sclivevol = {'instanceId': '101.101',
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
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
res = self.driver.terminate_connection(volume, connector)
mock_unmap_volume.assert_called_once_with(self.VOLUME, self.SCSERVER)
@ -2661,36 +2728,36 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
'instanceName': fake.VOLUME2_ID},
'secondaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 102}
'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
postfail = {'instanceId': '101.100',
'primaryVolume': {'instanceId': '102.101',
'instanceName': fake.VOLUME_ID},
'secondaryVolume': {'instanceId': '101.101',
'instanceName': fake.VOLUME2_ID},
'secondaryScSerialNumber': 102}
'secondaryScSerialNumber': 102,
'secondaryRole': 'Secondary'}
mock_api.get_live_volume = mock.MagicMock()
mock_api.get_live_volume.side_effect = [(sclivevol, False),
(postfail, True),
(sclivevol, False),
(sclivevol, False)
]
mock_api.get_live_volume.side_effect = [sclivevol, postfail,
sclivevol, sclivevol]
# Good run.
mock_api.is_swapped = mock.MagicMock(return_value=False)
mock_api.swap_roles_live_volume = mock.MagicMock(return_value=True)
model_update = {'provider_id': '102.101',
'replication_status': 'failed-over'}
ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID,
'101.100')
'101.101')
self.assertEqual(model_update, ret)
# Swap fail
mock_api.swap_roles_live_volume.return_value = False
model_update = {'status': 'error'}
ret = self.driver._failover_live_volume(mock_api, fake.VOLUME_ID,
'101.100')
'101.101')
self.assertEqual(model_update, ret)
# 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,
'101.100')
'101.101')
self.assertEqual(model_update, ret)
def test__failover_replication(self,
@ -3206,16 +3273,16 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
{'id': fake.VOLUME2_ID,
'replication_driver_data': '12345',
'provider_id': '12345.2'}]
mock_get_live_volume.side_effect = [(
mock_get_live_volume.side_effect = [
{'instanceId': '11111.101',
'secondaryVolume': {'instanceId': '11111.1001',
'instanceName': fake.VOLUME_ID},
'secondaryScSerialNumber': 11111}, True), (
'secondaryScSerialNumber': 11111},
{'instanceId': '11111.102',
'secondaryVolume': {'instanceId': '11111.1002',
'instanceName': fake.VOLUME2_ID},
'secondaryScSerialNumber': 11111}, True
)]
'secondaryScSerialNumber': 11111}
]
mock_get_replication_specs.return_value = {'enabled': True,
'live': 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 exception
from cinder import test
from cinder.tests.unit import fake_constants as fake
from cinder.volume.drivers.dell import dell_storagecenter_api
@ -6027,7 +6028,7 @@ class DellSCSanAPITestCase(test.TestCase):
expected = 'StorageCenter/ScReplication/%s' % (
self.SCREPL[0]['instanceId'])
expected_payload = {'DeleteDestinationVolume': True,
'RecycleDestinationVolume': False,
'RecycleDestinationVolume': True,
'DeleteRestorePoint': True}
ret = self.scapi.delete_replication(self.VOLUME, destssn)
mock_delete.assert_any_call(expected, payload=expected_payload,
@ -6064,7 +6065,7 @@ class DellSCSanAPITestCase(test.TestCase):
expected = 'StorageCenter/ScReplication/%s' % (
self.SCREPL[0]['instanceId'])
expected_payload = {'DeleteDestinationVolume': True,
'RecycleDestinationVolume': False,
'RecycleDestinationVolume': True,
'DeleteRestorePoint': True}
ret = self.scapi.delete_replication(self.VOLUME, destssn)
mock_delete.assert_any_call(expected, payload=expected_payload,
@ -6449,37 +6450,60 @@ class DellSCSanAPITestCase(test.TestCase):
scvol,
'a,b')
@mock.patch.object(dell_storagecenter_api.HttpClient,
'get')
@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,
mock_get_json,
mock_get,
mock_get_live_volumes,
mock_sc_live_volumes,
mock_close_connection,
mock_open_connection,
mock_init):
# Basic check
retlv, retswapped = self.scapi.get_live_volume(None)
retlv = self.scapi.get_live_volume(None)
self.assertIsNone(retlv)
self.assertFalse(retswapped)
lv1 = {'primaryVolume': {'instanceId': '12345.1'},
'secondaryVolume': {'instanceId': '67890.1'}}
lv2 = {'primaryVolume': {'instanceId': '12345.2'}}
mock_get_json.return_value = [lv1, lv2]
mock_get.return_value = self.RESPONSE_200
mock_sc_live_volumes.return_value = [lv1, lv2]
# Good Run
retlv, retswapped = self.scapi.get_live_volume('12345.2')
retlv = self.scapi.get_live_volume('12345.2')
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,
'_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,
mock_get_json,
mock_get,
mock_get_live_volumes,
mock_sc_live_volumes,
mock_close_connection,
mock_open_connection,
mock_init):
@ -6487,19 +6511,20 @@ class DellSCSanAPITestCase(test.TestCase):
'secondaryVolume': {'instanceId': '67890.1'}}
lv2 = {'primaryVolume': {'instanceId': '12345.2'},
'secondaryVolume': {'instanceId': '67890.2'}}
mock_get_json.return_value = [lv1, lv2]
mock_get.return_value = self.RESPONSE_200
retlv, retswapped = self.scapi.get_live_volume('12345.3')
mock_get_live_volumes.return_value = [lv1, lv2]
mock_sc_live_volumes.return_value = []
retlv = self.scapi.get_live_volume('12345.3')
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,
'_get_json')
'_sc_live_volumes')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_live_volumes')
def test_get_live_volume_swapped(self,
mock_get_json,
mock_get,
mock_get_live_volumes,
mock_sc_live_volumes,
mock_close_connection,
mock_open_connection,
mock_init):
@ -6507,23 +6532,50 @@ class DellSCSanAPITestCase(test.TestCase):
'secondaryVolume': {'instanceId': '67890.1'}}
lv2 = {'primaryVolume': {'instanceId': '67890.2'},
'secondaryVolume': {'instanceId': '12345.2'}}
mock_get_json.return_value = [lv1, lv2]
mock_get.return_value = self.RESPONSE_200
retlv, retswapped = self.scapi.get_live_volume('12345.2')
mock_get_live_volumes.return_value = [lv1, lv2]
mock_sc_live_volumes.return_value = []
retlv = self.scapi.get_live_volume('12345.2')
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,
'get')
@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_error(self,
mock_get,
mock_get_live_volumes,
mock_sc_live_volumes,
mock_close_connection,
mock_open_connection,
mock_init):
mock_get.return_value = self.RESPONSE_400
retlv, retswapped = self.scapi.get_live_volume('12345.2')
mock_get_live_volumes.return_value = []
mock_sc_live_volumes.return_value = []
retlv = self.scapi.get_live_volume('12345.2')
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,
'post')

View File

@ -1091,20 +1091,26 @@ class StorageCenterApi(object):
def _autofailback(self, lv):
# if we have a working replication state.
ret = False
if (lv['ReplicationState'] == 'Up' and
lv['failoverState'] == 'AutoFailedOver'):
LOG.debug('Attempting autofailback of %s', lv)
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)
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.
primary_id = provider_id
lv, swapped = self.get_live_volume(provider_id)
# if we swapped see if we can autofailback. Unless the admin
# failed us over, that is.
if swapped and not self.failed_over:
lv = self.get_live_volume(provider_id, name)
LOG.info(_LI('Volume %(provider)s at primary %(primary)s.'),
{'provider': provider_id, 'primary': primary_id})
# 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):
ls, swapped = self.get_live_volume(provider_id)
lv = self.get_live_volume(provider_id)
LOG.info(_LI('After failback %s'), lv)
if lv:
primary_id = lv['primaryVolume']['instanceId']
return primary_id
@ -1127,7 +1133,7 @@ class StorageCenterApi(object):
scvolume = None
if islivevol:
# 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)
elif self._use_provider_id(provider_id):
# just get our volume
@ -2747,7 +2753,7 @@ class StorageCenterApi(object):
if replication:
payload = {}
payload['DeleteDestinationVolume'] = deletedestvolume
payload['RecycleDestinationVolume'] = False
payload['RecycleDestinationVolume'] = deletedestvolume
payload['DeleteRestorePoint'] = True
r = self.client.delete('StorageCenter/ScReplication/%s' %
self._get_id(replication), payload=payload,
@ -3067,24 +3073,73 @@ class StorageCenterApi(object):
progress)
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.
: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:
r = self.client.get('StorageCenter/ScLiveVolume')
if self._check_result(r):
lvs = self._get_json(r)
# Try from our primary SSN. This would be the authoritay on the
# Live Volume in question.
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:
if (lv.get('primaryVolume') and
lv['primaryVolume']['instanceId'] == primaryid):
return lv, False
if (lv.get('secondaryVolume') and
lv['secondaryVolume']['instanceId'] == primaryid):
return lv, True
return None, False
if ((lv.get('primaryVolume') and
lv['primaryVolume']['instanceId'] == primaryid) or
(lv.get('secondaryVolume') and
lv['secondaryVolume']['instanceId'] == primaryid)):
sclivevol = lv
break
# 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):
# Helper to get the hba's of a given server.
@ -3184,6 +3239,7 @@ class StorageCenterApi(object):
payload['ConvertToReplication'] = False
payload['DeleteSecondaryVolume'] = deletesecondaryvolume
payload['RecycleSecondaryVolume'] = deletesecondaryvolume
payload['DeleteRestorePoint'] = deletesecondaryvolume
r = self.client.delete('StorageCenter/ScLiveVolume/%s' %
self._get_id(sclivevolume), payload, True)
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)
if ssnstrings:
ssn = int(ssnstrings[0])
sclivevolume, swapped = api.get_live_volume(
volume.get('provider_id'))
sclivevolume = api.get_live_volume(volume.get('provider_id'))
# Have we found the live volume?
if (sclivevolume and
sclivevolume.get('secondaryScSerialNumber') == ssn and
@ -682,12 +681,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
def _update_volume_stats(self):
"""Retrieve stats info from volume group."""
with self._client.open_connection() as api:
storageusage = api.get_storage_usage()
if not storageusage:
msg = _('Unable to retrieve volume stats.')
raise exception.VolumeBackendAPIException(message=msg)
# all of this is basically static for now
# Static stats.
data = {}
data['volume_backend_name'] = self.backend_name
data['vendor_name'] = 'Dell'
@ -696,12 +690,6 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
data['reserved_percentage'] = 0
data['consistencygroup_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['replication_enabled'] = self.replication_enabled
if self.replication_enabled:
@ -715,6 +703,22 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
replication_targets.append(target_device_id)
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
LOG.debug('Total cap %(total)s Free cap %(free)s',
{'total': data['total_capacity_gb'],
@ -1390,7 +1394,10 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
:return: model_update dict
"""
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):
LOG.info(_LI('Success swapping sclivevolume roles %s'), id)
model_update = {
@ -1489,8 +1496,10 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
def _failover_live_volume(self, api, id, provider_id):
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:
swapped = api.is_swapped(provider_id, sclivevolume)
# If we aren't swapped try it. If fail error out.
if not swapped and not api.swap_roles_live_volume(sclivevolume):
LOG.info(_LI('Failure swapping roles %s'), id)
@ -1498,7 +1507,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
return model_update
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 = {
'replication_status':
fields.ReplicationStatus.FAILED_OVER,

View File

@ -90,52 +90,59 @@ class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver,
with self._client.open_connection() as api:
try:
wwpns = connector.get('wwpns')
# Find our server.
scserver = self._find_server(api, wwpns)
# No? Create it.
if scserver is None:
scserver = api.create_server(
wwpns, self.configuration.dell_server_os)
# Find the volume on the storage center.
# Find the volume on the storage center. Note that if this
# is live volume and we are swapped this will be the back
# half of the live volume.
scvolume = api.find_volume(volume_name, provider_id, islivevol)
if scserver is not None and scvolume is not None:
mapping = api.map_volume(scvolume, scserver)
if mapping is not None:
# Since we just mapped our volume we had best update
# our sc volume object.
scvolume = api.get_volume(scvolume['instanceId'])
lun, targets, init_targ_map = api.find_wwns(scvolume,
scserver)
if scvolume:
# Get the SSN it is on.
ssn = scvolume['instanceId'].split('.')[0]
# Find our server.
scserver = self._find_server(api, wwpns, ssn)
# Do we have extra live volume work?
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:
# Now map our secondary.
lvlun, lvtargets, lvinit_targ_map = (
self.initialize_secondary(api,
sclivevolume,
wwpns))
# Unmapped. Add info to our list.
targets += lvtargets
init_targ_map.update(lvinit_targ_map)
# No? Create it.
if scserver is None:
scserver = api.create_server(
wwpns, self.configuration.dell_server_os, ssn)
# We have a volume and a server. Map them.
if scserver is not None:
mapping = api.map_volume(scvolume, scserver)
if mapping is not None:
# Since we just mapped our volume we had
# best update our sc volume object.
scvolume = api.get_volume(scvolume['instanceId'])
lun, targets, init_targ_map = api.find_wwns(
scvolume, scserver)
# 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!'))
# Do we have extra live volume work?
if islivevol:
# 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)):
# Now map our secondary.
lvlun, lvtargets, lvinit_targ_map = (
self.initialize_secondary(api,
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:
with excutils.save_and_reraise_exception():
@ -191,46 +198,53 @@ class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver,
with self._client.open_connection() as api:
try:
wwpns = connector.get('wwpns')
scserver = self._find_server(api, wwpns)
# Find the volume on the storage center.
scvolume = api.find_volume(volume_name, provider_id, islivevol)
# Get our target map so we can return it to free up a zone.
lun, targets, init_targ_map = api.find_wwns(scvolume, scserver)
if scvolume:
# Get the SSN it is on.
ssn = scvolume['instanceId'].split('.')[0]
# Do we have extra live volume work?
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)
scserver = self._find_server(api, wwpns, ssn)
# If we have a server and a volume lets unmap them.
if (scserver is not None and
scvolume is not None and
api.unmap_volume(scvolume, scserver) is True):
LOG.debug('Connection terminated')
else:
raise exception.VolumeBackendAPIException(
_('Terminate connection failed'))
# Get our target map so we can return it to free up a zone.
lun, targets, init_targ_map = api.find_wwns(scvolume,
scserver)
# basic return info...
info = {'driver_volume_type': 'fibre_channel',
'data': {}}
# Do we have extra live volume work?
if islivevol:
# 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
# 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
# If we have a server and a volume lets unmap them.
if (scserver is not None and
scvolume is not None and
api.unmap_volume(scvolume, scserver) is True):
LOG.debug('Connection terminated')
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:
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:
try:
# Find our server.
scserver = api.find_server(initiator_name)
# No? Create it.
if scserver is None:
scserver = api.create_server(
[initiator_name], self.configuration.dell_server_os)
# Find the volume on the storage center.
scvolume = api.find_volume(volume_name, provider_id)
# Find the volume on the storage center. Note that if this
# is live volume and we are swapped this will be the back
# 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)
# 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 scserver is not None and scvolume is not None:
mapping = api.map_volume(scvolume, scserver)
if mapping is not None:
# Since we just mapped our volume we had best update
# our sc volume object.
scvolume = api.get_volume(provider_id)
# Our return.
iscsiprops = {}
# if we have a server and a volume lets bring them
# together.
if scserver is not None:
mapping = api.map_volume(scvolume, scserver)
if mapping is not None:
# Since we just mapped our volume we had best
# update our sc volume object.
scvolume = api.get_volume(scvolume['instanceId'])
# Our return.
iscsiprops = {}
# Three cases that should all be satisfied with the
# same return of Target_Portal and Target_Portals.
# 1. Nova is calling us so we need to return the
# Target_Portal stuff. It should ignore the
# Target_Portals stuff.
# 2. OS brick is calling us in multipath mode so we
# want to return Target_Portals. It will ignore
# the Target_Portal stuff.
# 3. OS brick is calling us in single path mode so
# we want to return Target_Portal and
# Target_Portals as alternates.
iscsiprops = api.find_iscsi_properties(scvolume)
# Three cases that should all be satisfied with the
# same return of Target_Portal and Target_Portals.
# 1. Nova is calling us so we need to return the
# Target_Portal stuff. It should ignore the
# Target_Portals stuff.
# 2. OS brick is calling us in multipath mode so we
# want to return Target_Portals. It will ignore
# the Target_Portal stuff.
# 3. OS brick is calling us in single path mode so
# we want to return Target_Portal and
# Target_Portals as alternates.
iscsiprops = api.find_iscsi_properties(scvolume)
# If this is a live volume we need to map up our
# secondary volume.
if islivevol:
sclivevolume, swapped = api.get_live_volume(
provider_id)
# Only map if we are not swapped.
if sclivevolume and not swapped:
secondaryprops = self.initialize_secondary(
api, sclivevolume, initiator_name)
# Combine with iscsiprops
iscsiprops['target_iqns'] += (
secondaryprops['target_iqns'])
iscsiprops['target_portals'] += (
secondaryprops['target_portals'])
iscsiprops['target_luns'] += (
secondaryprops['target_luns'])
# If this is a live volume we need to map up our
# secondary volume. Note that if we have failed
# over we do not wish to do this.
if islivevol:
sclivevolume = api.get_live_volume(provider_id)
# Only map if we are not failed over.
if (sclivevolume and not
api.is_failed_over(provider_id,
sclivevolume)):
secondaryprops = self.initialize_secondary(
api, sclivevolume, initiator_name)
# Combine with iscsiprops
iscsiprops['target_iqns'] += (
secondaryprops['target_iqns'])
iscsiprops['target_portals'] += (
secondaryprops['target_portals'])
iscsiprops['target_luns'] += (
secondaryprops['target_luns'])
# Return our iscsi properties.
iscsiprops['discard'] = True
return {'driver_volume_type': 'iscsi',
'data': iscsiprops}
# Return our iscsi properties.
iscsiprops['discard'] = True
return {'driver_volume_type': 'iscsi',
'data': iscsiprops}
# Re-raise any backend exception.
except exception.VolumeBackendAPIException:
with excutils.save_and_reraise_exception():
@ -214,23 +223,31 @@ class DellStorageCenterISCSIDriver(dell_storagecenter_common.DellCommonDriver,
'initiator': initiator_name})
with self._client.open_connection() as api:
try:
scserver = api.find_server(initiator_name)
# Find the volume on the storage center.
scvolume = api.find_volume(volume_name, provider_id)
# Find the volume on the storage center. Note that if this
# is live volume and we are swapped this will be the back
# 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.
if islivevol:
sclivevolume, swapped = api.get_live_volume(provider_id)
if sclivevolume and not swapped:
self.terminate_secondary(api, sclivevolume,
initiator_name)
# Unmap our secondary if not failed over..
if islivevol:
sclivevolume = api.get_live_volume(provider_id)
if (sclivevolume and not
api.is_failed_over(provider_id,
sclivevolume)):
self.terminate_secondary(api, sclivevolume,
initiator_name)
# If we have a server and a volume lets pull them apart.
if (scserver is not None and
scvolume is not None and
api.unmap_volume(scvolume, scserver) is True):
LOG.debug('Connection terminated')
return
# If we have a server and a volume lets pull them apart.
if (scserver is not None and
scvolume is not None and
api.unmap_volume(scvolume, scserver) is True):
LOG.debug('Connection terminated')
return
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Failed to terminate connection '