Add view builder to QoS specs API extension

Add view builder for qos_specs.create(), index(), show() and associations()
to in order to make the response more easier to be consumed by client.

This patch also:
  fixed circular reference error when raising HTTP exception.
  fixed some typo in debug message and removed unused imports.

fix bug: # 1219016

Change-Id: I107888e6b4dac8eb5f1b45a87721a7b5efc45632
This commit is contained in:
Zhiteng Huang
2013-08-30 22:28:52 +08:00
parent e5b6d4d82c
commit 4a028e3f33
7 changed files with 237 additions and 123 deletions

View File

@@ -21,13 +21,12 @@ import webob
from cinder.api import extensions from cinder.api import extensions
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.views import qos_specs as view_qos_specs
from cinder.api import xmlutil from cinder.api import xmlutil
from cinder import db
from cinder import exception from cinder import exception
from cinder.openstack.common import log as logging from cinder.openstack.common import log as logging
from cinder.openstack.common.notifier import api as notifier_api from cinder.openstack.common.notifier import api as notifier_api
from cinder.volume import qos_specs from cinder.volume import qos_specs
from cinder.volume import volume_types
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@@ -66,6 +65,8 @@ def _check_specs(context, specs_id):
class QoSSpecsController(wsgi.Controller): class QoSSpecsController(wsgi.Controller):
"""The volume type extra specs API controller for the OpenStack API.""" """The volume type extra specs API controller for the OpenStack API."""
_view_builder_class = view_qos_specs.ViewBuilder
@staticmethod @staticmethod
def _notify_qos_specs_error(context, method, payload): def _notify_qos_specs_error(context, method, payload):
notifier_api.notify(context, notifier_api.notify(context,
@@ -80,7 +81,7 @@ class QoSSpecsController(wsgi.Controller):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
authorize(context) authorize(context)
specs = qos_specs.get_all_specs(context) specs = qos_specs.get_all_specs(context)
return specs return self._view_builder.summary_list(req, specs)
@wsgi.serializers(xml=QoSSpecsTemplate) @wsgi.serializers(xml=QoSSpecsTemplate)
def create(self, req, body=None): def create(self, req, body=None):
@@ -97,8 +98,8 @@ class QoSSpecsController(wsgi.Controller):
raise webob.exc.HTTPBadRequest(explanation=msg) raise webob.exc.HTTPBadRequest(explanation=msg)
try: try:
specs_ref = qos_specs.create(context, name, specs) qos_specs.create(context, name, specs)
qos_specs.get_qos_specs_by_name(context, name) spec = qos_specs.get_qos_specs_by_name(context, name)
notifier_info = dict(name=name, specs=specs) notifier_info = dict(name=name, specs=specs)
notifier_api.notify(context, 'QoSSpecs', notifier_api.notify(context, 'QoSSpecs',
'QoSSpecs.create', 'QoSSpecs.create',
@@ -122,7 +123,7 @@ class QoSSpecsController(wsgi.Controller):
notifier_err) notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=str(err)) raise webob.exc.HTTPInternalServerError(explanation=str(err))
return body return self._view_builder.detail(req, spec)
@wsgi.serializers(xml=QoSSpecsTemplate) @wsgi.serializers(xml=QoSSpecsTemplate)
def update(self, req, id, body=None): def update(self, req, id, body=None):
@@ -166,10 +167,10 @@ class QoSSpecsController(wsgi.Controller):
try: try:
spec = qos_specs.get_qos_specs(context, id) spec = qos_specs.get_qos_specs(context, id)
except exception.NotFound: except exception.QoSSpecsNotFound as err:
raise webob.exc.HTTPNotFound() raise webob.exc.HTTPNotFound(explanation=str(err))
return spec return self._view_builder.detail(req, spec)
def delete(self, req, id): def delete(self, req, id):
"""Deletes an existing qos specs.""" """Deletes an existing qos specs."""
@@ -178,7 +179,8 @@ class QoSSpecsController(wsgi.Controller):
force = req.params.get('force', None) force = req.params.get('force', None)
LOG.debug("qos_specs_manage.delete(): id: %s, force: %s" % (id, force)) LOG.debug("Delete qos_spec: %(id)s, force: %(force)s" %
{'id': id, 'force': force})
try: try:
qos_specs.get_qos_specs(context, id) qos_specs.get_qos_specs(context, id)
@@ -187,12 +189,12 @@ class QoSSpecsController(wsgi.Controller):
notifier_api.notify(context, 'QoSSpecs', notifier_api.notify(context, 'QoSSpecs',
'qos_specs.delete', 'qos_specs.delete',
notifier_api.INFO, notifier_info) notifier_api.INFO, notifier_info)
except exception.NotFound as err: except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.delete', 'qos_specs.delete',
notifier_err) notifier_err)
raise webob.exc.HTTPNotFound() raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsInUse as err: except exception.QoSSpecsInUse as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
@@ -212,7 +214,7 @@ class QoSSpecsController(wsgi.Controller):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
authorize(context) authorize(context)
LOG.debug("assocications(): id: %s" % id) LOG.debug("Get associations for qos_spec id: %s" % id)
try: try:
associates = qos_specs.get_associations(context, id) associates = qos_specs.get_associations(context, id)
@@ -225,15 +227,15 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.associations', 'qos_specs.associations',
notifier_err) notifier_err)
raise webob.exc.HTTPNotFound(explanation=err) raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.CinderException as err: except exception.CinderException as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.associations', 'qos_specs.associations',
notifier_err) notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=err) raise webob.exc.HTTPInternalServerError(explanation=str(err))
return associates return self._view_builder.associations(req, associates)
def associate(self, req, id): def associate(self, req, id):
"""Associate a qos specs with a volume type.""" """Associate a qos specs with a volume type."""
@@ -249,7 +251,8 @@ class QoSSpecsController(wsgi.Controller):
'qos_specs.delete', 'qos_specs.delete',
notifier_err) notifier_err)
raise webob.exc.HTTPBadRequest(explanation=msg) raise webob.exc.HTTPBadRequest(explanation=msg)
LOG.debug("associcate(): id: %s, type_id: %s" % (id, type_id)) LOG.debug("Associate qos_spec: %(id)s with type: %(type_id)s" %
{'id': id, 'type_id': type_id})
try: try:
qos_specs.get_qos_specs(context, id) qos_specs.get_qos_specs(context, id)
@@ -263,19 +266,19 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.associate', 'qos_specs.associate',
notifier_err) notifier_err)
raise webob.exc.HTTPNotFound(explanation=err) raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsNotFound as err: except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.associate', 'qos_specs.associate',
notifier_err) notifier_err)
raise webob.exc.HTTPNotFound(explanation=err) raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsAssociateFailed as err: except exception.QoSSpecsAssociateFailed as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.associate', 'qos_specs.associate',
notifier_err) notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=err) raise webob.exc.HTTPInternalServerError(explanation=str(err))
return webob.Response(status_int=202) return webob.Response(status_int=202)
@@ -293,7 +296,8 @@ class QoSSpecsController(wsgi.Controller):
'qos_specs.delete', 'qos_specs.delete',
notifier_err) notifier_err)
raise webob.exc.HTTPBadRequest(explanation=msg) raise webob.exc.HTTPBadRequest(explanation=msg)
LOG.debug("disassocicate(): id: %s, type_id: %s" % (id, type_id)) LOG.debug("Disassociate qos_spec: %(id)s from type: %(type_id)s" %
{'id': id, 'type_id': type_id})
try: try:
qos_specs.get_qos_specs(context, id) qos_specs.get_qos_specs(context, id)
@@ -307,19 +311,19 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.disassociate', 'qos_specs.disassociate',
notifier_err) notifier_err)
raise webob.exc.HTTPNotFound(explanation=err) raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsNotFound as err: except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.disassociate', 'qos_specs.disassociate',
notifier_err) notifier_err)
raise webob.exc.HTTPNotFound(explanation=err) raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsDisassociateFailed as err: except exception.QoSSpecsDisassociateFailed as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.disassociate', 'qos_specs.disassociate',
notifier_err) notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=err) raise webob.exc.HTTPInternalServerError(explanation=str(err))
return webob.Response(status_int=202) return webob.Response(status_int=202)
@@ -328,7 +332,7 @@ class QoSSpecsController(wsgi.Controller):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
authorize(context) authorize(context)
LOG.debug("disassocicate_all(): id: %s" % id) LOG.debug("Disassociate qos_spec: %s from all." % id)
try: try:
qos_specs.get_qos_specs(context, id) qos_specs.get_qos_specs(context, id)
@@ -342,13 +346,13 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.disassociate_all', 'qos_specs.disassociate_all',
notifier_err) notifier_err)
raise webob.exc.HTTPNotFound(explanation=err) raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsDisassociateFailed as err: except exception.QoSSpecsDisassociateFailed as err:
notifier_err = dict(id=id, error_message=str(err)) notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context, self._notify_qos_specs_error(context,
'qos_specs.disassociate_all', 'qos_specs.disassociate_all',
notifier_err) notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=err) raise webob.exc.HTTPInternalServerError(explanation=str(err))
return webob.Response(status_int=202) return webob.Response(status_int=202)

View File

@@ -0,0 +1,64 @@
# Copyright (C) 2013 eBay Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cinder.api import common
from cinder.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class ViewBuilder(common.ViewBuilder):
"""Model QoS specs API responses as a python dictionary."""
_collection_name = "qos_specs"
def __init__(self):
"""Initialize view builder."""
super(ViewBuilder, self).__init__()
def summary_list(self, request, qos_specs):
"""Show a list of qos_specs without many details."""
return self._list_view(self.detail, request, qos_specs)
def summary(self, request, qos_spec):
"""Generic, non-detailed view of a qos_specs."""
return {
'qos_specs': qos_spec,
'links': self._get_links(request,
qos_spec['id']),
}
def detail(self, request, qos_spec):
"""Detailed view of a single qos_spec."""
#TODO(zhiteng) Add associations to detailed view
return {
'qos_specs': qos_spec,
'links': self._get_links(request,
qos_spec['id']),
}
def associations(self, request, associates):
"""View of qos specs associations."""
return {
'qos_associations': associates
}
def _list_view(self, func, request, qos_specs):
"""Provide a view for a list of qos_specs."""
specs_list = [func(request, specs)['qos_specs'] for specs in qos_specs]
specs_dict = dict(qos_specs=specs_list)
return specs_dict

View File

@@ -2022,7 +2022,7 @@ def qos_specs_create(context, values):
# the name of QoS specs # the name of QoS specs
root['key'] = 'QoS_Specs_Name' root['key'] = 'QoS_Specs_Name'
root['value'] = values['name'] root['value'] = values['name']
LOG.debug("qos_specs_create(): root %s", root) LOG.debug("DB qos_specs_create(): root %s", root)
specs_root.update(root) specs_root.update(root)
specs_root.save(session=session) specs_root.save(session=session)
@@ -2036,7 +2036,7 @@ def qos_specs_create(context, values):
except Exception as e: except Exception as e:
raise db_exc.DBError(e) raise db_exc.DBError(e)
return specs_root return dict(id=specs_root.id, name=specs_root.value)
@require_admin_context @require_admin_context
@@ -2078,21 +2078,25 @@ def _dict_with_children_specs(specs):
def _dict_with_qos_specs(rows): def _dict_with_qos_specs(rows):
"""Convert qos specs query results to dict with name as key. """Convert qos specs query results to list.
Qos specs query results are a list of quality_of_service_specs refs, Qos specs query results are a list of quality_of_service_specs refs,
some are root entry of a qos specs (key == 'QoS_Specs_Name') and the some are root entry of a qos specs (key == 'QoS_Specs_Name') and the
rest are children entry, a.k.a detailed specs for a qos specs. This rest are children entry, a.k.a detailed specs for a qos specs. This
funtion converts query results to a dict using spec name as key. function converts query results to a dict using spec name as key.
""" """
result = {} result = []
for row in rows: for row in rows:
if row['key'] == 'QoS_Specs_Name': if row['key'] == 'QoS_Specs_Name':
result[row['value']] = dict(id=row['id']) member = {}
member['name'] = row['value']
member.update(dict(id=row['id']))
if row.specs: if row.specs:
spec_dict = _dict_with_children_specs(row.specs) spec_dict = _dict_with_children_specs(row.specs)
result[row['value']].update(spec_dict) member.update(dict(consumer=spec_dict['consumer']))
del spec_dict['consumer']
member.update(dict(specs=spec_dict))
result.append(member)
return result return result
@@ -2100,25 +2104,35 @@ def _dict_with_qos_specs(rows):
def qos_specs_get(context, qos_specs_id, inactive=False): def qos_specs_get(context, qos_specs_id, inactive=False):
rows = _qos_specs_get_ref(context, qos_specs_id, None, inactive) rows = _qos_specs_get_ref(context, qos_specs_id, None, inactive)
return _dict_with_qos_specs(rows) return _dict_with_qos_specs(rows)[0]
@require_admin_context @require_admin_context
def qos_specs_get_all(context, inactive=False, filters=None): def qos_specs_get_all(context, inactive=False, filters=None):
"""Returns dicts describing all qos_specs. """Returns a list of all qos_specs.
Results is like: Results is like:
{'qos-spec-1': {'id': SPECS-UUID, [{
'id': SPECS-UUID,
'name': 'qos_spec-1',
'consumer': 'back-end',
'specs': {
'key1': 'value1', 'key1': 'value1',
'key2': 'value2', 'key2': 'value2',
... ...
'consumer': 'back-end'}
'qos-spec-2': {'id': SPECS-UUID,
'key1': 'value1',
'key2': 'value2',
...
'consumer': 'back-end'}
} }
},
{
'id': SPECS-UUID,
'name': 'qos_spec-2',
'consumer': 'front-end',
'specs': {
'key1': 'value1',
'key2': 'value2',
...
}
},
]
""" """
filters = filters or {} filters = filters or {}
#TODO(zhiteng) Add filters for 'consumer' #TODO(zhiteng) Add filters for 'consumer'
@@ -2135,7 +2149,7 @@ def qos_specs_get_all(context, inactive=False, filters=None):
def qos_specs_get_by_name(context, name, inactive=False): def qos_specs_get_by_name(context, name, inactive=False):
rows = _qos_specs_get_by_name(context, name, None, inactive) rows = _qos_specs_get_by_name(context, name, None, inactive)
return _dict_with_qos_specs(rows) return _dict_with_qos_specs(rows)[0]
@require_admin_context @require_admin_context
@@ -2148,9 +2162,7 @@ def qos_specs_associations_get(context, qos_specs_id):
extend qos specs association to other entities, such as volumes, extend qos specs association to other entities, such as volumes,
sometime in future. sometime in future.
""" """
rows = _qos_specs_get_ref(context, qos_specs_id, None) _qos_specs_get_ref(context, qos_specs_id, None)
if not rows:
raise exception.QoSSpecsNotFound(specs_id=qos_specs_id)
return volume_type_qos_associations_get(context, qos_specs_id) return volume_type_qos_associations_get(context, qos_specs_id)

View File

@@ -26,32 +26,37 @@ from cinder.volume import qos_specs
def stub_qos_specs(id): def stub_qos_specs(id):
res = dict(name='qos_specs_' + str(id))
res.update(dict(consumer='back-end'))
res.update(dict(id=str(id)))
specs = {"key1": "value1", specs = {"key1": "value1",
"key2": "value2", "key2": "value2",
"key3": "value3", "key3": "value3",
"key4": "value4", "key4": "value4",
"key5": "value5"} "key5": "value5"}
specs.update(dict(id=str(id))) res.update(dict(specs=specs))
return specs return res
def stub_qos_associates(id): def stub_qos_associates(id):
return {str(id): {'FakeVolTypeName': 'FakeVolTypeID'}} return [{
'association_type': 'volume_type',
'name': 'FakeVolTypeName',
'id': 'FakeVolTypeID'}]
def return_qos_specs_get_all(context): def return_qos_specs_get_all(context):
return dict( return [
qos_specs_1=stub_qos_specs(1), stub_qos_specs(1),
qos_specs_2=stub_qos_specs(2), stub_qos_specs(2),
qos_specs_3=stub_qos_specs(3) stub_qos_specs(3),
) ]
def return_qos_specs_get_qos_specs(context, id): def return_qos_specs_get_qos_specs(context, id):
if id == "777": if id == "777":
raise exception.QoSSpecsNotFound(specs_id=id) raise exception.QoSSpecsNotFound(specs_id=id)
name = 'qos_specs_%s' % id return stub_qos_specs(int(id))
return {name: stub_qos_specs(int(id))}
def return_qos_specs_delete(context, id, force): def return_qos_specs_delete(context, id, force):
@@ -142,14 +147,16 @@ class QoSSpecManageApiTest(test.TestCase):
return_qos_specs_get_all) return_qos_specs_get_all)
req = fakes.HTTPRequest.blank('/v2/fake/qos-specs') req = fakes.HTTPRequest.blank('/v2/fake/qos-specs')
res_dict = self.controller.index(req) res = self.controller.index(req)
self.assertEqual(3, len(res_dict.keys())) self.assertEqual(3, len(res['qos_specs']))
names = set()
for item in res['qos_specs']:
self.assertEqual('value1', item['specs']['key1'])
names.add(item['name'])
expected_names = ['qos_specs_1', 'qos_specs_2', 'qos_specs_3'] expected_names = ['qos_specs_1', 'qos_specs_2', 'qos_specs_3']
self.assertEqual(set(res_dict.keys()), set(expected_names)) self.assertEqual(names, set(expected_names))
for key in res_dict.keys():
self.assertEqual('value1', res_dict[key]['key1'])
def test_qos_specs_delete(self): def test_qos_specs_delete(self):
self.stubs.Set(qos_specs, 'get_qos_specs', self.stubs.Set(qos_specs, 'get_qos_specs',
@@ -212,7 +219,6 @@ class QoSSpecManageApiTest(test.TestCase):
res_dict = self.controller.create(req, body) res_dict = self.controller.create(req, body)
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1) self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
self.assertEqual(1, len(res_dict))
self.assertEqual('qos_specs_1', res_dict['qos_specs']['name']) self.assertEqual('qos_specs_1', res_dict['qos_specs']['name'])
def test_create_conflict(self): def test_create_conflict(self):
@@ -318,8 +324,8 @@ class QoSSpecManageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v2/fake/qos-specs/1') req = fakes.HTTPRequest.blank('/v2/fake/qos-specs/1')
res_dict = self.controller.show(req, '1') res_dict = self.controller.show(req, '1')
self.assertEqual(1, len(res_dict)) self.assertEqual('1', res_dict['qos_specs']['id'])
self.assertEqual('1', res_dict['qos_specs_1']['id']) self.assertEqual('qos_specs_1', res_dict['qos_specs']['name'])
def test_get_associations(self): def test_get_associations(self):
self.stubs.Set(qos_specs, 'get_associations', self.stubs.Set(qos_specs, 'get_associations',
@@ -329,10 +335,10 @@ class QoSSpecManageApiTest(test.TestCase):
'/v2/fake/qos-specs/1/associations') '/v2/fake/qos-specs/1/associations')
res = self.controller.associations(req, '1') res = self.controller.associations(req, '1')
self.assertEqual('1', res.keys()[0]) self.assertEqual('FakeVolTypeName',
self.assertEqual('FakeVolTypeName', res['1'].keys()[0]) res['qos_associations'][0]['name'])
self.assertEqual('FakeVolTypeID', self.assertEqual('FakeVolTypeID',
res['1']['FakeVolTypeName']) res['qos_associations'][0]['id'])
def test_get_associations_not_found(self): def test_get_associations_not_found(self):
self.stubs.Set(qos_specs, 'get_associations', self.stubs.Set(qos_specs, 'get_associations',

View File

@@ -68,7 +68,7 @@ class QualityOfServiceSpecsTableTestCase(test.TestCase):
specs_id = self._create_qos_specs('NewName') specs_id = self._create_qos_specs('NewName')
query_id = db.qos_specs_get_by_name( query_id = db.qos_specs_get_by_name(
self.ctxt, 'NewName')['NewName']['id'] self.ctxt, 'NewName')['id']
self.assertEquals(specs_id, query_id) self.assertEquals(specs_id, query_id)
def test_qos_specs_get(self): def test_qos_specs_get(self):
@@ -81,8 +81,9 @@ class QualityOfServiceSpecsTableTestCase(test.TestCase):
db.qos_specs_get, self.ctxt, fake_id) db.qos_specs_get, self.ctxt, fake_id)
specs = db.qos_specs_get(self.ctxt, specs_id) specs = db.qos_specs_get(self.ctxt, specs_id)
value.update(dict(id=specs_id)) expected = dict(name='Name1', id=specs_id, consumer='front-end')
expected = dict(Name1=value) del value['consumer']
expected.update(dict(specs=value))
self.assertDictMatch(specs, expected) self.assertDictMatch(specs, expected)
def test_qos_specs_get_all(self): def test_qos_specs_get_all(self):
@@ -101,12 +102,18 @@ class QualityOfServiceSpecsTableTestCase(test.TestCase):
self.assertEquals(len(specs), 3, self.assertEquals(len(specs), 3,
"Unexpected number of qos specs records") "Unexpected number of qos specs records")
value1.update({'id': spec_id1}) expected1 = dict(name='Name1', id=spec_id1, consumer='front-end')
value2.update({'id': spec_id2}) expected2 = dict(name='Name2', id=spec_id2, consumer='back-end')
value3.update({'id': spec_id3}) expected3 = dict(name='Name3', id=spec_id3, consumer='back-end')
self.assertDictMatch(specs['Name1'], value1) del value1['consumer']
self.assertDictMatch(specs['Name2'], value2) del value2['consumer']
self.assertDictMatch(specs['Name3'], value3) del value3['consumer']
expected1.update(dict(specs=value1))
expected2.update(dict(specs=value2))
expected3.update(dict(specs=value3))
self.assertIn(expected1, specs)
self.assertIn(expected2, specs)
self.assertIn(expected3, specs)
def test_qos_specs_get_by_name(self): def test_qos_specs_get_by_name(self):
name = str(int(time.time())) name = str(int(time.time()))
@@ -114,8 +121,11 @@ class QualityOfServiceSpecsTableTestCase(test.TestCase):
foo='Foo', bar='Bar') foo='Foo', bar='Bar')
specs_id = self._create_qos_specs(name, value) specs_id = self._create_qos_specs(name, value)
specs = db.qos_specs_get_by_name(self.ctxt, name) specs = db.qos_specs_get_by_name(self.ctxt, name)
value.update(dict(id=specs_id)) del value['consumer']
expected = {name: value} expected = {'name': name,
'id': specs_id,
'consumer': 'front-end',
'specs': value}
self.assertDictMatch(specs, expected) self.assertDictMatch(specs, expected)
def test_qos_specs_delete(self): def test_qos_specs_delete(self):
@@ -200,5 +210,5 @@ class QualityOfServiceSpecsTableTestCase(test.TestCase):
self.ctxt, 'Fake-UUID', value) self.ctxt, 'Fake-UUID', value)
db.qos_specs_update(self.ctxt, specs_id, value) db.qos_specs_update(self.ctxt, specs_id, value)
specs = db.qos_specs_get(self.ctxt, specs_id) specs = db.qos_specs_get(self.ctxt, specs_id)
self.assertEqual(specs[name]['key2'], 'new_value2') self.assertEqual(specs['specs']['key2'], 'new_value2')
self.assertEqual(specs[name]['key3'], 'value3') self.assertEqual(specs['specs']['key3'], 'value3')

View File

@@ -66,17 +66,16 @@ class QoSSpecsTestCase(test.TestCase):
'key3': 'value3'} 'key3': 'value3'}
ref = qos_specs.create(self.ctxt, 'FakeName', input) ref = qos_specs.create(self.ctxt, 'FakeName', input)
specs = qos_specs.get_qos_specs(self.ctxt, ref['id']) specs = qos_specs.get_qos_specs(self.ctxt, ref['id'])
input.update(dict(consumer='back-end')) expected = (dict(consumer='back-end'))
input.update(dict(id=ref['id'])) expected.update(dict(id=ref['id']))
expected = {'FakeName': input} expected.update(dict(name='FakeName'))
del input['consumer']
expected.update(dict(specs=input))
self.assertDictMatch(specs, expected) self.assertDictMatch(specs, expected)
self.stubs.Set(db, 'qos_specs_create', self.stubs.Set(db, 'qos_specs_create',
fake_db_qos_specs_create) fake_db_qos_specs_create)
# Restore input back to original state
del input['id']
del input['consumer']
# qos specs must have unique name # qos specs must have unique name
self.assertRaises(exception.QoSSpecsExists, self.assertRaises(exception.QoSSpecsExists,
qos_specs.create, self.ctxt, 'DupQoSName', input) qos_specs.create, self.ctxt, 'DupQoSName', input)
@@ -101,7 +100,7 @@ class QoSSpecsTestCase(test.TestCase):
self.assertRaises(exception.InvalidQoSSpecs, self.assertRaises(exception.InvalidQoSSpecs,
qos_specs.update, self.ctxt, 'fake_id', input) qos_specs.update, self.ctxt, 'fake_id', input)
del input['consumer'] input['consumer'] = 'front-end'
# qos specs must exists # qos specs must exists
self.assertRaises(exception.QoSSpecsNotFound, self.assertRaises(exception.QoSSpecsNotFound,
qos_specs.update, self.ctxt, 'fake_id', input) qos_specs.update, self.ctxt, 'fake_id', input)
@@ -111,8 +110,8 @@ class QoSSpecsTestCase(test.TestCase):
{'key1': 'newvalue1', {'key1': 'newvalue1',
'key2': 'value2'}) 'key2': 'value2'})
specs = qos_specs.get_qos_specs(self.ctxt, specs_id) specs = qos_specs.get_qos_specs(self.ctxt, specs_id)
self.assertEqual(specs['Name']['key1'], 'newvalue1') self.assertEqual(specs['specs']['key1'], 'newvalue1')
self.assertEqual(specs['Name']['key2'], 'value2') self.assertEqual(specs['specs']['key2'], 'value2')
self.stubs.Set(db, 'qos_specs_update', fake_db_update) self.stubs.Set(db, 'qos_specs_update', fake_db_update)
self.assertRaises(exception.QoSSpecsUpdateFailed, self.assertRaises(exception.QoSSpecsUpdateFailed,
@@ -155,10 +154,15 @@ class QoSSpecsTestCase(test.TestCase):
self.stubs.Set(db, 'qos_specs_associations_get', self.stubs.Set(db, 'qos_specs_associations_get',
fake_db_associate_get) fake_db_associate_get)
expected = {'specs-id': {'type-1': 'id-1', expected1 = {'association_type': 'volume_type',
'type-2': 'id-2'}} 'name': 'type-1',
'id': 'id-1'}
expected2 = {'association_type': 'volume_type',
'name': 'type-2',
'id': 'id-2'}
res = qos_specs.get_associations(self.ctxt, 'specs-id') res = qos_specs.get_associations(self.ctxt, 'specs-id')
self.assertDictMatch(res, expected) self.assertIn(expected1, res)
self.assertIn(expected2, res)
self.assertRaises(exception.CinderException, self.assertRaises(exception.CinderException,
qos_specs.get_associations, self.ctxt, qos_specs.get_associations, self.ctxt,
@@ -178,9 +182,9 @@ class QoSSpecsTestCase(test.TestCase):
qos_specs.associate_qos_with_type(self.ctxt, specs_id, qos_specs.associate_qos_with_type(self.ctxt, specs_id,
type_ref['id']) type_ref['id'])
res = qos_specs.get_associations(self.ctxt, specs_id) res = qos_specs.get_associations(self.ctxt, specs_id)
self.assertEquals(len(res[specs_id].keys()), 1) self.assertEquals(len(res), 1)
self.assertIn('TypeName', res[specs_id].keys()) self.assertEquals('TypeName', res[0]['name'])
self.assertIn(type_ref['id'], res[specs_id].values()) self.assertEquals(type_ref['id'], res[0]['id'])
self.stubs.Set(db, 'qos_specs_associate', self.stubs.Set(db, 'qos_specs_associate',
fake_db_associate) fake_db_associate)
@@ -205,11 +209,11 @@ class QoSSpecsTestCase(test.TestCase):
qos_specs.associate_qos_with_type(self.ctxt, specs_id, qos_specs.associate_qos_with_type(self.ctxt, specs_id,
type_ref['id']) type_ref['id'])
res = qos_specs.get_associations(self.ctxt, specs_id) res = qos_specs.get_associations(self.ctxt, specs_id)
self.assertEquals(len(res[specs_id].keys()), 1) self.assertEquals(len(res), 1)
qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id']) qos_specs.disassociate_qos_specs(self.ctxt, specs_id, type_ref['id'])
res = qos_specs.get_associations(self.ctxt, specs_id) res = qos_specs.get_associations(self.ctxt, specs_id)
self.assertEquals(len(res[specs_id].keys()), 0) self.assertEquals(len(res), 0)
self.stubs.Set(db, 'qos_specs_disassociate', self.stubs.Set(db, 'qos_specs_disassociate',
fake_db_disassociate) fake_db_disassociate)
@@ -235,11 +239,11 @@ class QoSSpecsTestCase(test.TestCase):
qos_specs.associate_qos_with_type(self.ctxt, specs_id, qos_specs.associate_qos_with_type(self.ctxt, specs_id,
type2_ref['id']) type2_ref['id'])
res = qos_specs.get_associations(self.ctxt, specs_id) res = qos_specs.get_associations(self.ctxt, specs_id)
self.assertEquals(len(res[specs_id].keys()), 2) self.assertEquals(len(res), 2)
qos_specs.disassociate_all(self.ctxt, specs_id) qos_specs.disassociate_all(self.ctxt, specs_id)
res = qos_specs.get_associations(self.ctxt, specs_id) res = qos_specs.get_associations(self.ctxt, specs_id)
self.assertEquals(len(res[specs_id].keys()), 0) self.assertEquals(len(res), 0)
self.stubs.Set(db, 'qos_specs_disassociate_all', self.stubs.Set(db, 'qos_specs_disassociate_all',
fake_db_disassociate_all) fake_db_disassociate_all)
@@ -250,31 +254,41 @@ class QoSSpecsTestCase(test.TestCase):
def test_get_all_specs(self): def test_get_all_specs(self):
input = {'key1': 'value1', input = {'key1': 'value1',
'key2': 'value2', 'key2': 'value2',
'key3': 'value3'} 'key3': 'value3',
'consumer': 'both'}
specs_id1 = self._create_qos_specs('Specs1', input) specs_id1 = self._create_qos_specs('Specs1', input)
input.update({'key4': 'value4'}) input.update({'key4': 'value4'})
specs_id2 = self._create_qos_specs('Specs2', input) specs_id2 = self._create_qos_specs('Specs2', input)
expected = {'Specs1': {'key1': 'value1', expected1 = {
'id': specs_id1, 'id': specs_id1,
'name': 'Specs1',
'consumer': 'both',
'specs': {'key1': 'value1',
'key2': 'value2', 'key2': 'value2',
'key3': 'value3'}, 'key3': 'value3'}}
'Specs2': {'key1': 'value1', expected2 = {
'id': specs_id2, 'id': specs_id2,
'name': 'Specs2',
'consumer': 'both',
'specs': {'key1': 'value1',
'key2': 'value2', 'key2': 'value2',
'key3': 'value3', 'key3': 'value3',
'key4': 'value4'}} 'key4': 'value4'}}
res = qos_specs.get_all_specs(self.ctxt) res = qos_specs.get_all_specs(self.ctxt)
self.assertDictMatch(expected, res) self.assertEqual(len(res), 2)
self.assertIn(expected1, res)
self.assertIn(expected2, res)
def test_get_qos_specs(self): def test_get_qos_specs(self):
one_time_value = str(int(time.time())) one_time_value = str(int(time.time()))
input = {'key1': one_time_value, input = {'key1': one_time_value,
'key2': 'value2', 'key2': 'value2',
'key3': 'value3'} 'key3': 'value3',
'consumer': 'both'}
id = self._create_qos_specs('Specs1', input) id = self._create_qos_specs('Specs1', input)
specs = qos_specs.get_qos_specs(self.ctxt, id) specs = qos_specs.get_qos_specs(self.ctxt, id)
self.assertEquals(specs['Specs1']['key1'], one_time_value) self.assertEquals(specs['specs']['key1'], one_time_value)
self.assertRaises(exception.InvalidQoSSpecs, self.assertRaises(exception.InvalidQoSSpecs,
qos_specs.get_qos_specs, self.ctxt, None) qos_specs.get_qos_specs, self.ctxt, None)
@@ -283,11 +297,12 @@ class QoSSpecsTestCase(test.TestCase):
one_time_value = str(int(time.time())) one_time_value = str(int(time.time()))
input = {'key1': one_time_value, input = {'key1': one_time_value,
'key2': 'value2', 'key2': 'value2',
'key3': 'value3'} 'key3': 'value3',
'consumer': 'back-end'}
id = self._create_qos_specs(one_time_value, input) id = self._create_qos_specs(one_time_value, input)
specs = qos_specs.get_qos_specs_by_name(self.ctxt, specs = qos_specs.get_qos_specs_by_name(self.ctxt,
one_time_value) one_time_value)
self.assertEquals(specs[one_time_value]['key1'], one_time_value) self.assertEquals(specs['specs']['key1'], one_time_value)
self.assertRaises(exception.InvalidQoSSpecs, self.assertRaises(exception.InvalidQoSSpecs,
qos_specs.get_qos_specs_by_name, self.ctxt, None) qos_specs.get_qos_specs_by_name, self.ctxt, None)

View File

@@ -147,11 +147,14 @@ def get_associations(context, specs_id):
LOG.warn(msg) LOG.warn(msg)
raise exception.CinderException(message=msg) raise exception.CinderException(message=msg)
result = {} result = []
for vol_type in associates: for vol_type in associates:
result[vol_type['name']] = vol_type['id'] member = dict(association_type='volume_type')
member.update(dict(name=vol_type['name']))
member.update(dict(id=vol_type['id']))
result.append(member)
return {specs_id: result} return result
def associate_qos_with_type(context, specs_id, type_id): def associate_qos_with_type(context, specs_id, type_id):