# 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
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)
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)
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])
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.
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
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)
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)