Merge "Dell SC: Add support for driver retype"
This commit is contained in:
commit
92d9c96551
|
@ -12,14 +12,14 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
import uuid
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder import test
|
||||
from cinder.volume.drivers.dell import dell_storagecenter_api
|
||||
from cinder.volume.drivers.dell import dell_storagecenter_common
|
||||
from cinder.volume.drivers.dell import dell_storagecenter_iscsi
|
||||
from cinder.volume import volume_types
|
||||
|
||||
|
@ -1636,6 +1636,37 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||
volume,
|
||||
existing_ref)
|
||||
|
||||
def test_retype_not_extra_specs(self,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.driver.retype(
|
||||
None, None, None, {'extra_specs': None}, None)
|
||||
self.assertFalse(res)
|
||||
|
||||
def test_retype_not_storage_profile(self,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.driver.retype(
|
||||
None, None, None, {'extra_specs': {'something': 'else'}}, None)
|
||||
self.assertFalse(res)
|
||||
|
||||
def test_retype_malformed(self,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
LOG = self.mock_object(dell_storagecenter_common, "LOG")
|
||||
res = self.driver.retype(
|
||||
None, None, None,
|
||||
{'extra_specs': {
|
||||
'storagetype:storageprofile': ['something',
|
||||
'not',
|
||||
'right']}},
|
||||
None)
|
||||
self.assertFalse(res)
|
||||
self.assertEqual(1, LOG.warning.call_count)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_volume',
|
||||
return_value=VOLUME)
|
||||
|
@ -1667,3 +1698,26 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||
self.driver.unmanage(volume)
|
||||
mock_find_volume.assert_called_once_with(volume['id'])
|
||||
self.assertFalse(mock_unmanage.called)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'update_storage_profile')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_volume',
|
||||
return_value=VOLUME)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_sc',
|
||||
return_value=12345)
|
||||
def test_retype(self,
|
||||
mock_find_sc,
|
||||
mock_find_volume,
|
||||
mock_update_storage_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.driver.retype(
|
||||
None, {'id': 'volid'}, None,
|
||||
{'extra_specs': {'storagetype:storageprofile': ['A', 'B']}},
|
||||
None)
|
||||
mock_update_storage_profile.ssert_called_once_with(
|
||||
self.VOLUME, 'B')
|
||||
self.assertTrue(res)
|
||||
|
|
|
@ -4009,6 +4009,113 @@ class DellSCSanAPITestCase(test.TestCase):
|
|||
self.assertFalse(mock_delete.called)
|
||||
self.assertIsNone(res, 'Expected None')
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
return_value={'test': 'test'})
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_200)
|
||||
def test_get_user_preferences(self,
|
||||
mock_get,
|
||||
mock_get_json,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
# Not really testing anything other than the ability to mock, but
|
||||
# including for completeness.
|
||||
res = self.scapi._get_user_preferences()
|
||||
self.assertEqual({'test': 'test'}, res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'get',
|
||||
return_value=RESPONSE_400)
|
||||
def test_get_user_preferences_failure(self,
|
||||
mock_get,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
LOG = self.mock_object(dell_storagecenter_api, "LOG")
|
||||
res = self.scapi._get_user_preferences()
|
||||
self.assertEqual({}, res)
|
||||
self.assertTrue(LOG.error.call_count > 0)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_user_preferences',
|
||||
return_value=None)
|
||||
def test_update_storage_profile_noprefs(self,
|
||||
mock_prefs,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
res = self.scapi.update_storage_profile(None, None)
|
||||
self.assertFalse(res)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_user_preferences',
|
||||
return_value={'allowStorageProfileSelection': False})
|
||||
def test_update_storage_profile_not_allowed(self,
|
||||
mock_prefs,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
LOG = self.mock_object(dell_storagecenter_api, "LOG")
|
||||
res = self.scapi.update_storage_profile(None, None)
|
||||
self.assertFalse(res)
|
||||
self.assertEqual(1, LOG.error.call_count)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_find_storage_profile',
|
||||
return_value=None)
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_user_preferences',
|
||||
return_value={'allowStorageProfileSelection': True})
|
||||
def test_update_storage_profile_prefs_not_found(self,
|
||||
mock_profile,
|
||||
mock_prefs,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
LOG = self.mock_object(dell_storagecenter_api, "LOG")
|
||||
res = self.scapi.update_storage_profile(None, 'Fake')
|
||||
self.assertFalse(res)
|
||||
self.assertEqual(1, LOG.error.call_count)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_user_preferences',
|
||||
return_value={'allowStorageProfileSelection': True,
|
||||
'storageProfile': None})
|
||||
def test_update_storage_profile_default_not_found(self,
|
||||
mock_prefs,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
LOG = self.mock_object(dell_storagecenter_api, "LOG")
|
||||
res = self.scapi.update_storage_profile(None, None)
|
||||
self.assertFalse(res)
|
||||
self.assertEqual(1, LOG.error.call_count)
|
||||
|
||||
@mock.patch.object(
|
||||
dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_user_preferences',
|
||||
return_value={'allowStorageProfileSelection': True,
|
||||
'storageProfile': {'name': 'Fake',
|
||||
'instanceId': 'fakeId'}})
|
||||
@mock.patch.object(dell_storagecenter_api.HttpClient,
|
||||
'post',
|
||||
return_value=RESPONSE_200)
|
||||
def test_update_storage_profile(self,
|
||||
mock_post,
|
||||
mock_prefs,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
LOG = self.mock_object(dell_storagecenter_api, "LOG")
|
||||
fake_scvolume = {'name': 'name', 'instanceId': 'id'}
|
||||
res = self.scapi.update_storage_profile(fake_scvolume, None)
|
||||
self.assertTrue(res)
|
||||
self.assertTrue('fakeId' in repr(mock_post.call_args_list[0]))
|
||||
self.assertEqual(1, LOG.info.call_count)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'_get_json',
|
||||
return_value=[RPLAY_PROFILE])
|
||||
|
|
|
@ -1538,6 +1538,78 @@ class StorageCenterApi(object):
|
|||
return False
|
||||
return True
|
||||
|
||||
def update_storage_profile(self, scvolume, storage_profile):
|
||||
"""Update a volume's Storage Profile.
|
||||
|
||||
Changes the volume setting to use a different Storage Profile. If
|
||||
storage_profile is None, will reset to the default profile for the
|
||||
cinder user account.
|
||||
|
||||
:param scvolume: The Storage Center volume to be updated.
|
||||
:param storage_profile: The requested Storage Profile name.
|
||||
:returns: True if successful, False otherwise.
|
||||
"""
|
||||
prefs = self._get_user_preferences()
|
||||
if not prefs:
|
||||
return False
|
||||
|
||||
if not prefs.get('allowStorageProfileSelection'):
|
||||
LOG.error(_LE('User does not have permission to change '
|
||||
'Storage Profile selection.'))
|
||||
return False
|
||||
|
||||
profile = self._find_storage_profile(storage_profile)
|
||||
if storage_profile:
|
||||
if not profile:
|
||||
LOG.error(_LE('Storage Profile %s was not found.'),
|
||||
storage_profile)
|
||||
return False
|
||||
else:
|
||||
# Going from specific profile to the user default
|
||||
profile = prefs.get('storageProfile')
|
||||
if not profile:
|
||||
LOG.error(_LE('Default Storage Profile was not found.'))
|
||||
return False
|
||||
|
||||
LOG.info(_LI('Switching volume %(vol)s to profile %(prof)s.'),
|
||||
{'vol': scvolume['name'],
|
||||
'prof': profile.get('name')})
|
||||
payload = {}
|
||||
payload['StorageProfile'] = self._get_id(profile)
|
||||
r = self.client.post('StorageCenter/ScVolumeConfiguration'
|
||||
'/%s/Modify'
|
||||
% self._get_id(scvolume),
|
||||
payload)
|
||||
if r.status_code != 200:
|
||||
LOG.error(_LE('Error changing Storage Profile for volume '
|
||||
'%(original)s to %(name)s: %(code)d %(reason)s '
|
||||
'%(text)s'),
|
||||
{'original': scvolume['name'],
|
||||
'name': storage_profile,
|
||||
'code': r.status_code,
|
||||
'reason': r.reason,
|
||||
'text': r.text})
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_user_preferences(self):
|
||||
"""Gets the preferences and defaults for this user.
|
||||
|
||||
There are a set of preferences and defaults for each user on the
|
||||
Storage Center. This retrieves all settings for the current account
|
||||
used by Cinder.
|
||||
"""
|
||||
r = self.client.get('StorageCenter/StorageCenter/%s/UserPreferences' %
|
||||
self.ssn)
|
||||
if r.status_code != 200:
|
||||
LOG.error(_LE('Error getting user preferences: '
|
||||
'%(code)d %(reason)s %(text)s'),
|
||||
{'code': r.status_code,
|
||||
'reason': r.reason,
|
||||
'text': r.text})
|
||||
return {}
|
||||
return self._get_json(r)
|
||||
|
||||
def _delete_server(self, scserver):
|
||||
'''Deletes scserver from the backend.
|
||||
|
||||
|
|
|
@ -636,3 +636,43 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
|
|||
scvolume = api.find_volume(volume['id'])
|
||||
if scvolume:
|
||||
api.unmanage(scvolume)
|
||||
|
||||
def retype(self, ctxt, volume, new_type, diff, host):
|
||||
"""Convert the volume to be of the new type.
|
||||
|
||||
Returns a boolean indicating whether the retype occurred.
|
||||
|
||||
:param ctxt: Context
|
||||
:param volume: A dictionary describing the volume to migrate
|
||||
:param new_type: A dictionary describing the volume type to convert to
|
||||
:param diff: A dictionary with the difference between the two types
|
||||
:param host: A dictionary describing the host to migrate to, where
|
||||
host['host'] is its name, and host['capabilities'] is a
|
||||
dictionary of its reported capabilities (Not Used).
|
||||
"""
|
||||
# We currently only support retyping for the Storage Profile extra spec
|
||||
if diff['extra_specs']:
|
||||
storage_profiles = diff['extra_specs'].get(
|
||||
'storagetype:storageprofile')
|
||||
if storage_profiles:
|
||||
if len(storage_profiles) != 2:
|
||||
LOG.warning(_LW('Unable to retype Storage Profile, '
|
||||
'expected to receive current and '
|
||||
'requested storagetype:storageprofile '
|
||||
'values. Value received: %s'),
|
||||
storage_profiles)
|
||||
return False
|
||||
|
||||
requested = storage_profiles[1]
|
||||
volume_name = volume.get('id')
|
||||
LOG.debug('Retyping volume %(vol)s to use storage '
|
||||
'profile %(profile)s',
|
||||
{'vol': volume_name,
|
||||
'profile': requested})
|
||||
with self._client.open_connection() as api:
|
||||
if api.find_sc():
|
||||
scvolume = api.find_volume(volume_name)
|
||||
return api.update_storage_profile(
|
||||
scvolume, requested)
|
||||
|
||||
return False
|
||||
|
|
|
@ -41,9 +41,10 @@ class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver,
|
|||
2.0.0 - Switched to inheriting functional objects rather than volume
|
||||
driver.
|
||||
2.1.0 - Added support for ManageableVD.
|
||||
2.2.0 - Driver retype support for switching volume's Storage Profile
|
||||
'''
|
||||
|
||||
VERSION = '2.1.0'
|
||||
VERSION = '2.2.0'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DellStorageCenterFCDriver, self).__init__(*args, **kwargs)
|
||||
|
|
|
@ -39,9 +39,10 @@ class DellStorageCenterISCSIDriver(dell_storagecenter_common.DellCommonDriver,
|
|||
2.0.0 - Switched to inheriting functional objects rather than volume
|
||||
driver.
|
||||
2.1.0 - Added support for ManageableVD.
|
||||
2.2.0 - Driver retype support for switching volume's Storage Profile
|
||||
'''
|
||||
|
||||
VERSION = '2.1.0'
|
||||
VERSION = '2.2.0'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DellStorageCenterISCSIDriver, self).__init__(*args, **kwargs)
|
||||
|
|
Loading…
Reference in New Issue