Add volume type support to Datera

This allows created volumes to have a volume type specified with them.

Implements: blueprint datera-extra-spec-support
Implements: blueprint datera-qos-support
Change-Id: I217d75deecbbf2282125703c296dc524174c4660
This commit is contained in:
Mike Perez 2015-08-14 16:19:28 -07:00
parent 4e8aa92614
commit 5a246962c3
2 changed files with 105 additions and 4 deletions

View File

@ -20,6 +20,7 @@ from cinder import exception
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers import datera
from cinder.volume import volume_types
class DateraVolumeTestCase(test.TestCase):
@ -103,6 +104,57 @@ class DateraVolumeTestCase(test.TestCase):
self.assertEqual(1, self.mock_api.retry_count)
self.assertIsNone(self.driver.create_volume(self.volume))
@mock.patch.object(volume_types, 'get_volume_type')
def test_create_volume_with_extra_specs(self, mock_get_type):
self.mock_api.return_value = {
u'status': u'available',
u'name': u'volume-00000001',
u'parent': u'00000000-0000-0000-0000-000000000000',
u'uuid': u'c20aba21-6ef6-446b-b374-45733b4883ba',
u'snapshots': {},
u'targets': {},
u'num_replicas': u'2',
u'sub_type': u'IS_ORIGINAL',
u'size': u'1073741824'
}
mock_get_type.return_value = {
'name': u'The Best',
'qos_specs_id': None,
'deleted': False,
'created_at': '2015-08-14 04:18:11',
'updated_at': None,
'extra_specs': {
u'volume_backend_name': u'datera',
u'qos:max_iops_read': u'2000',
u'qos:max_iops_write': u'4000',
u'qos:max_iops_total': u'4000'
},
'is_public': True,
'deleted_at': None,
'id': u'dffb4a83-b8fb-4c19-9f8c-713bb75db3b1',
'description': None
}
mock_volume = _stub_volume(
volume_type_id='dffb4a83-b8fb-4c19-9f8c-713bb75db3b1'
)
assert_body = {
u'max_iops_read': u'2000',
'numReplicas': '2',
'uuid': u'c20aba21-6ef6-446b-b374-45733b4883ba',
'size': '1073741824',
u'max_iops_write': u'4000',
u'max_iops_total': u'4000',
'name': u'volume-00000001'
}
self.assertIsNone(self.driver.create_volume(mock_volume))
self.mock_api.assert_called_once_with('volumes', 'post',
body=assert_body)
self.assertTrue(mock_get_type.called)
def test_create_cloned_volume_success(self):
self.mock_api.return_value = {
'status': 'available',
@ -416,6 +468,7 @@ def _stub_volume(*args, **kwargs):
volume['display_name'] = kwargs.get('display_name', name)
volume['size'] = kwargs.get('size', size)
volume['provider_location'] = kwargs.get('provider_location', None)
volume['volume_type_id'] = kwargs.get('volume_type_id', None)
return volume

View File

@ -23,10 +23,13 @@ from oslo_utils import units
import requests
import six
from cinder import context
from cinder import exception
from cinder.i18n import _, _LE, _LI, _LW
from cinder import utils
from cinder.volume.drivers.san import san
from cinder.volume import qos_specs
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
@ -133,11 +136,26 @@ class DateraDriver(san.SanISCSIDriver):
_('Resource not ready.'))
def _create_resource(self, resource, resource_type, body):
result = self._issue_api_request(resource_type, 'post', body=body)
type_id = resource.get('volume_type_id', None)
if resource_type == 'volumes':
if type_id is not None:
policies = self._get_policies_by_volume_type(type_id)
if policies:
body.update(policies)
if result['status'] == 'available':
return
self._wait_for_resource(resource['id'], resource_type)
result = None
try:
result = self._issue_api_request(resource_type, 'post', body=body)
except exception.Invalid:
if resource_type == 'volumes' and type_id:
LOG.error(_LE("Creation request failed. Please verify the "
"extra-specs set for your volume types are "
"entered correctly."))
raise
else:
if result['status'] == 'available':
return
self._wait_for_resource(resource['id'], resource_type)
def create_volume(self, volume):
"""Create a logical volume."""
@ -299,6 +317,30 @@ class DateraDriver(san.SanISCSIDriver):
'cinder.conf and start the cinder-volume'
'service again.'))
def _get_policies_by_volume_type(self, type_id):
"""Get extra_specs and qos_specs of a volume_type.
This fetches the scoped keys from the volume type. Anything set from
qos_specs will override key/values set from extra_specs.
"""
ctxt = context.get_admin_context()
volume_type = volume_types.get_volume_type(ctxt, type_id)
specs = volume_type.get('extra_specs')
policies = {}
for key, value in specs.items():
if ':' in key:
fields = key.split(':')
key = fields[1]
policies[key] = value
qos_specs_id = volume_type.get('qos_specs_id')
if qos_specs_id is not None:
qos_kvs = qos_specs.get_qos_specs(ctxt, qos_specs_id)['specs']
if qos_kvs:
policies.update(qos_kvs)
return policies
@_authenticated
def _issue_api_request(self, resource_type, method='get', resource=None,
body=None, action=None, sensitive=False):
@ -372,6 +414,12 @@ class DateraDriver(san.SanISCSIDriver):
raise exception.NotFound(data['message'])
elif response.status_code in [403, 401]:
raise exception.NotAuthorized()
elif response.status_code == 400 and 'invalidArgs' in data:
msg = _('Bad request sent to Datera cluster:'
'Invalid args: %(args)s | %(message)s') % {
'args': data['invalidArgs']['invalidAttrs'],
'message': data['message']}
raise exception.Invalid(msg)
else:
msg = _('Request to Datera cluster returned bad status:'
' %(status)s | %(reason)s') % {