Merge "Dell SC: Enable use of Storage Profiles"

This commit is contained in:
Jenkins 2015-06-25 18:42:07 +00:00 committed by Gerrit Code Review
commit 27b37f9034
6 changed files with 272 additions and 7 deletions

View File

@ -19,6 +19,7 @@ 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_iscsi
from cinder.volume import volume_types
import mock
@ -273,7 +274,31 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
volume = {'id': self.volume_name, 'size': 1}
self.driver.create_volume(volume)
mock_create_volume.assert_called_once_with(self.volume_name,
1)
1,
None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'create_volume',
return_value=VOLUME)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_sc',
return_value=12345)
@mock.patch.object(
volume_types,
'get_volume_type_extra_specs',
return_value={'storagetype:storageprofile': 'HighPriority'})
def test_create_volume_storage_profile(self,
mock_extra,
mock_find_sc,
mock_create_volume,
mock_close_connection,
mock_open_connection,
mock_init):
volume = {'id': self.volume_name, 'size': 1, 'volume_type_id': 'abc'}
self.driver.create_volume(volume)
mock_create_volume.assert_called_once_with(self.volume_name,
1,
"HighPriority")
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'create_volume',

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from oslo_log import log as logging
from cinder import context
@ -30,6 +31,7 @@ LOG = logging.getLogger(__name__)
# from trying to contact a Dell Storage Center.
@ddt.ddt
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'__init__',
return_value=None)
@ -1389,6 +1391,80 @@ class DellSCSanAPITestCase(test.TestCase):
u'storageAlertThreshold': 10,
u'objectType': u'StorageCenterStorageUsage'}
STORAGE_PROFILE_LIST = [
{u'allowedForFlashOptimized': False,
u'allowedForNonFlashOptimized': True,
u'index': 1,
u'instanceId': u'64158.1',
u'instanceName': u'Recommended',
u'name': u'Recommended',
u'notes': u'',
u'objectType': u'ScStorageProfile',
u'raidTypeDescription': u'RAID 10 Active, RAID 5 or RAID 6 Replay',
u'raidTypeUsed': u'Mixed',
u'scName': u'Storage Center 64158',
u'scSerialNumber': 64158,
u'tiersUsedDescription': u'Tier 1, Tier 2, Tier 3',
u'useTier1Storage': True,
u'useTier2Storage': True,
u'useTier3Storage': True,
u'userCreated': False,
u'volumeCount': 125},
{u'allowedForFlashOptimized': False,
u'allowedForNonFlashOptimized': True,
u'index': 2,
u'instanceId': u'64158.2',
u'instanceName': u'High Priority',
u'name': u'High Priority',
u'notes': u'',
u'objectType': u'ScStorageProfile',
u'raidTypeDescription': u'RAID 10 Active, RAID 5 or RAID 6 Replay',
u'raidTypeUsed': u'Mixed',
u'scName': u'Storage Center 64158',
u'scSerialNumber': 64158,
u'tiersUsedDescription': u'Tier 1',
u'useTier1Storage': True,
u'useTier2Storage': False,
u'useTier3Storage': False,
u'userCreated': False,
u'volumeCount': 0},
{u'allowedForFlashOptimized': False,
u'allowedForNonFlashOptimized': True,
u'index': 3,
u'instanceId': u'64158.3',
u'instanceName': u'Medium Priority',
u'name': u'Medium Priority',
u'notes': u'',
u'objectType': u'ScStorageProfile',
u'raidTypeDescription': u'RAID 10 Active, RAID 5 or RAID 6 Replay',
u'raidTypeUsed': u'Mixed',
u'scName': u'Storage Center 64158',
u'scSerialNumber': 64158,
u'tiersUsedDescription': u'Tier 2',
u'useTier1Storage': False,
u'useTier2Storage': True,
u'useTier3Storage': False,
u'userCreated': False,
u'volumeCount': 0},
{u'allowedForFlashOptimized': True,
u'allowedForNonFlashOptimized': True,
u'index': 4,
u'instanceId': u'64158.4',
u'instanceName': u'Low Priority',
u'name': u'Low Priority',
u'notes': u'',
u'objectType': u'ScStorageProfile',
u'raidTypeDescription': u'RAID 10 Active, RAID 5 or RAID 6 Replay',
u'raidTypeUsed': u'Mixed',
u'scName': u'Storage Center 64158',
u'scSerialNumber': 64158,
u'tiersUsedDescription': u'Tier 3',
u'useTier1Storage': False,
u'useTier2Storage': False,
u'useTier3Storage': True,
u'userCreated': False,
u'volumeCount': 0}]
IQN = 'iqn.2002-03.com.compellent:5000D31000000001'
WWN = u'21000024FF30441C'
@ -1712,6 +1788,57 @@ class DellSCSanAPITestCase(test.TestCase):
self.configuration.dell_sc_volume_folder)
self.assertEqual(self.FLDR, res, 'Unexpected Folder')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=STORAGE_PROFILE_LIST)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'post',
return_value=RESPONSE_200)
def test_find_storage_profile_fail(self,
mock_json,
mock_find_folder,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where _find_volume_folder returns none
res = self.scapi._find_storage_profile("Blah")
self.assertIsNone(res)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=STORAGE_PROFILE_LIST)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'post',
return_value=RESPONSE_200)
def test_find_storage_profile_none(self,
mock_json,
mock_find_folder,
mock_close_connection,
mock_open_connection,
mock_init):
# Test case where _find_storage_profile returns none
res = self.scapi._find_storage_profile(None)
self.assertIsNone(res)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=STORAGE_PROFILE_LIST)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'post',
return_value=RESPONSE_200)
@ddt.data('HighPriority', 'highpriority', 'High Priority')
def test_find_storage_profile(self,
value,
mock_json,
mock_find_folder,
mock_close_connection,
mock_open_connection,
mock_init):
res = self.scapi._find_storage_profile(value)
self.assertIsNotNone(res, 'Expected matching storage profile!')
self.assertEqual(self.STORAGE_PROFILE_LIST[1]['instanceId'],
res.get('instanceId'))
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_create_folder_path',
return_value=FLDR)
@ -1819,6 +1946,52 @@ class DellSCSanAPITestCase(test.TestCase):
mock_find_volume_folder.assert_called_once_with(True)
self.assertEqual(self.VOLUME, res, 'Unexpected ScVolume')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_storage_profile',
return_value=None)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_volume_folder',
return_value=FLDR)
def test_create_volume_storage_profile_missing(self,
mock_find_volume_folder,
mock_find_storage_profile,
mock_close_connection,
mock_open_connection,
mock_init):
self.assertRaises(exception.VolumeBackendAPIException,
self.scapi.create_volume,
self.volume_name,
1,
'Blah')
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_get_json',
return_value=VOLUME)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_storage_profile',
return_value=STORAGE_PROFILE_LIST[0])
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'_find_volume_folder',
return_value=FLDR)
@mock.patch.object(dell_storagecenter_api.HttpClient,
'post',
return_value=RESPONSE_201)
def test_create_volume_storage_profile(self,
mock_post,
mock_find_volume_folder,
mock_find_storage_profile,
mock_get_json,
mock_close_connection,
mock_open_connection,
mock_init):
self.scapi.create_volume(
self.volume_name,
1,
'Recommended')
actual = mock_post.call_args[0][1]['StorageProfile']
expected = self.STORAGE_PROFILE_LIST[0]['instanceId']
self.assertEqual(expected, actual)
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
'find_volume',
return_value=VOLUME)

View File

@ -502,7 +502,38 @@ class StorageCenterApi(object):
LOG.warning(_LW('Volume initialization failure. (%s)'),
self._get_id(scvolume))
def create_volume(self, name, size):
def _find_storage_profile(self, storage_profile):
'''Looks for a Storage Profile on the array.
Storage Profiles determine tiering settings. If not specified a volume
will use the Default storage profile.
:param storage_profile: The Storage Profile name to find with any
spaces stripped.
:returns: The Storage Profile object or None.
'''
if not storage_profile:
return None
# Since we are stripping out spaces for convenience we are not
# able to just filter on name. Need to get all Storage Profiles
# and look through for the one we want. Never many profiles, so
# this doesn't cause as much overhead as it might seem.
storage_profile = storage_profile.replace(' ', '').lower()
pf = PayloadFilter()
pf.append('scSerialNumber', self.ssn, 'Equals')
r = self.client.post(
'StorageCenter/ScStorageProfile/GetList', pf.payload)
if r.status_code == 200:
profiles = self._get_json(r)
for profile in profiles:
# Look for the stripped, case insensitive match
name = profile.get('name', '').replace(' ', '').lower()
if name == storage_profile:
return profile
return None
def create_volume(self, name, size, storage_profile=None):
'''Creates a new volume on the Storage Center.
It will create it in a folder called self.vfname. If self.vfname
@ -511,12 +542,16 @@ class StorageCenterApi(object):
:param name: Name of the volume to be created on the Dell SC backend.
This is the cinder volume ID.
:param size: The size of the volume to be created in GB.
:param storage_profile: Optional storage profile to set for the volume.
:returns: Dell Volume object or None.
'''
LOG.debug('Create Volume %(name)s %(ssn)s %(folder)s',
LOG.debug('Create Volume %(name)s %(ssn)s %(folder)s %(profile)s',
{'name': name,
'ssn': self.ssn,
'folder': self.vfname})
'folder': self.vfname,
'profile': storage_profile,
})
# Find our folder
folder = self._find_volume_folder(True)
@ -526,6 +561,13 @@ class StorageCenterApi(object):
LOG.warning(_LW('Unable to create folder %s'),
self.vfname)
# See if we need a storage profile
profile = self._find_storage_profile(storage_profile)
if storage_profile and profile is None:
msg = _('Storage Profile %s not found.') % storage_profile
raise exception.VolumeBackendAPIException(
data=msg)
# Init our return.
scvolume = None
@ -537,6 +579,8 @@ class StorageCenterApi(object):
payload['StorageCenter'] = self.ssn
if folder is not None:
payload['VolumeFolder'] = self._get_id(folder)
if profile:
payload['StorageProfile'] = self._get_id(profile)
r = self.client.post('StorageCenter/ScVolume',
payload)
if r.status_code == 201:

View File

@ -20,6 +20,7 @@ from cinder import exception
from cinder.i18n import _, _LE, _LW
from cinder.volume.drivers.dell import dell_storagecenter_api
from cinder.volume.drivers.san import san
from cinder.volume import volume_types
common_opts = [
@ -86,12 +87,25 @@ class DellCommonDriver(san.SanDriver):
with self._client.open_connection() as api:
api.find_sc()
def _get_volume_extra_specs(self, volume):
'''Gets extra specs for the given volume.'''
type_id = volume.get('volume_type_id')
if type_id:
return volume_types.get_volume_type_extra_specs(type_id)
return {}
def create_volume(self, volume):
'''Create a volume.'''
# We use id as our name as it is unique.
volume_name = volume.get('id')
volume_size = volume.get('size')
# See if we have any extra specs.
specs = self._get_volume_extra_specs(volume)
storage_profile = specs.get('storagetype:storageprofile')
LOG.debug('Creating volume %(name)s of size %(size)s',
{'name': volume_name,
'size': volume_size})
@ -100,7 +114,8 @@ class DellCommonDriver(san.SanDriver):
try:
if api.find_sc():
scvolume = api.create_volume(volume_name,
volume_size)
volume_size,
storage_profile)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Failed to create volume %s'),

View File

@ -33,9 +33,13 @@ class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver,
To enable the driver add the following line to the cinder configuration:
volume_driver=cinder.volume.drivers.dell.DellStorageCenterFCDriver
Version history:
1.0.0 - Initial driver
1.1.0 - Added extra spec support for Storage Profile selection
'''
VERSION = '1.0.2'
VERSION = '1.1.0'
def __init__(self, *args, **kwargs):
super(DellStorageCenterFCDriver, self).__init__(*args, **kwargs)

View File

@ -32,9 +32,13 @@ class DellStorageCenterISCSIDriver(san.SanISCSIDriver,
To enable the driver add the following line to the cinder configuration:
volume_driver=cinder.volume.drivers.dell.DellStorageCenterISCSIDriver
Version history:
1.0.0 - Initial driver
1.1.0 - Added extra spec support for Storage Profile selection
'''
VERSION = '1.0.2'
VERSION = '1.1.0'
def __init__(self, *args, **kwargs):
super(DellStorageCenterISCSIDriver, self).__init__(*args, **kwargs)