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):
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',
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
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__)
_('Resource not ready.'))
def _create_resource(self, resource, resource_type, body):
- result = self._issue_api_request(resource_type, 'post', body=body)
-
- if result['status'] == 'available':
- return
- self._wait_for_resource(resource['id'], resource_type)
+ 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)
+
+ 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."""
'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):
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') % {