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.openstack import wsgi
from cinder.api.views import qos_specs as view_qos_specs
from cinder.api import xmlutil
from cinder import db
from cinder import exception
from cinder.openstack.common import log as logging
from cinder.openstack.common.notifier import api as notifier_api
from cinder.volume import qos_specs
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
@@ -66,6 +65,8 @@ def _check_specs(context, specs_id):
class QoSSpecsController(wsgi.Controller):
"""The volume type extra specs API controller for the OpenStack API."""
_view_builder_class = view_qos_specs.ViewBuilder
@staticmethod
def _notify_qos_specs_error(context, method, payload):
notifier_api.notify(context,
@@ -80,7 +81,7 @@ class QoSSpecsController(wsgi.Controller):
context = req.environ['cinder.context']
authorize(context)
specs = qos_specs.get_all_specs(context)
return specs
return self._view_builder.summary_list(req, specs)
@wsgi.serializers(xml=QoSSpecsTemplate)
def create(self, req, body=None):
@@ -97,8 +98,8 @@ class QoSSpecsController(wsgi.Controller):
raise webob.exc.HTTPBadRequest(explanation=msg)
try:
specs_ref = qos_specs.create(context, name, specs)
qos_specs.get_qos_specs_by_name(context, name)
qos_specs.create(context, name, specs)
spec = qos_specs.get_qos_specs_by_name(context, name)
notifier_info = dict(name=name, specs=specs)
notifier_api.notify(context, 'QoSSpecs',
'QoSSpecs.create',
@@ -122,7 +123,7 @@ class QoSSpecsController(wsgi.Controller):
notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=str(err))
return body
return self._view_builder.detail(req, spec)
@wsgi.serializers(xml=QoSSpecsTemplate)
def update(self, req, id, body=None):
@@ -166,10 +167,10 @@ class QoSSpecsController(wsgi.Controller):
try:
spec = qos_specs.get_qos_specs(context, id)
except exception.NotFound:
raise webob.exc.HTTPNotFound()
except exception.QoSSpecsNotFound as err:
raise webob.exc.HTTPNotFound(explanation=str(err))
return spec
return self._view_builder.detail(req, spec)
def delete(self, req, id):
"""Deletes an existing qos specs."""
@@ -178,7 +179,8 @@ class QoSSpecsController(wsgi.Controller):
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:
qos_specs.get_qos_specs(context, id)
@@ -187,12 +189,12 @@ class QoSSpecsController(wsgi.Controller):
notifier_api.notify(context, 'QoSSpecs',
'qos_specs.delete',
notifier_api.INFO, notifier_info)
except exception.NotFound as err:
except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
'qos_specs.delete',
notifier_err)
raise webob.exc.HTTPNotFound()
raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsInUse as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
@@ -212,7 +214,7 @@ class QoSSpecsController(wsgi.Controller):
context = req.environ['cinder.context']
authorize(context)
LOG.debug("assocications(): id: %s" % id)
LOG.debug("Get associations for qos_spec id: %s" % id)
try:
associates = qos_specs.get_associations(context, id)
@@ -225,15 +227,15 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context,
'qos_specs.associations',
notifier_err)
raise webob.exc.HTTPNotFound(explanation=err)
raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.CinderException as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
'qos_specs.associations',
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):
"""Associate a qos specs with a volume type."""
@@ -249,7 +251,8 @@ class QoSSpecsController(wsgi.Controller):
'qos_specs.delete',
notifier_err)
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:
qos_specs.get_qos_specs(context, id)
@@ -263,19 +266,19 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context,
'qos_specs.associate',
notifier_err)
raise webob.exc.HTTPNotFound(explanation=err)
raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
'qos_specs.associate',
notifier_err)
raise webob.exc.HTTPNotFound(explanation=err)
raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsAssociateFailed as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
'qos_specs.associate',
notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=err)
raise webob.exc.HTTPInternalServerError(explanation=str(err))
return webob.Response(status_int=202)
@@ -293,7 +296,8 @@ class QoSSpecsController(wsgi.Controller):
'qos_specs.delete',
notifier_err)
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:
qos_specs.get_qos_specs(context, id)
@@ -307,19 +311,19 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context,
'qos_specs.disassociate',
notifier_err)
raise webob.exc.HTTPNotFound(explanation=err)
raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsNotFound as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
'qos_specs.disassociate',
notifier_err)
raise webob.exc.HTTPNotFound(explanation=err)
raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsDisassociateFailed as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
'qos_specs.disassociate',
notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=err)
raise webob.exc.HTTPInternalServerError(explanation=str(err))
return webob.Response(status_int=202)
@@ -328,7 +332,7 @@ class QoSSpecsController(wsgi.Controller):
context = req.environ['cinder.context']
authorize(context)
LOG.debug("disassocicate_all(): id: %s" % id)
LOG.debug("Disassociate qos_spec: %s from all." % id)
try:
qos_specs.get_qos_specs(context, id)
@@ -342,13 +346,13 @@ class QoSSpecsController(wsgi.Controller):
self._notify_qos_specs_error(context,
'qos_specs.disassociate_all',
notifier_err)
raise webob.exc.HTTPNotFound(explanation=err)
raise webob.exc.HTTPNotFound(explanation=str(err))
except exception.QoSSpecsDisassociateFailed as err:
notifier_err = dict(id=id, error_message=str(err))
self._notify_qos_specs_error(context,
'qos_specs.disassociate_all',
notifier_err)
raise webob.exc.HTTPInternalServerError(explanation=err)
raise webob.exc.HTTPInternalServerError(explanation=str(err))
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
root['key'] = 'QoS_Specs_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.save(session=session)
@@ -2036,7 +2036,7 @@ def qos_specs_create(context, values):
except Exception as e:
raise db_exc.DBError(e)
return specs_root
return dict(id=specs_root.id, name=specs_root.value)
@require_admin_context
@@ -2078,21 +2078,25 @@ def _dict_with_children_specs(specs):
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,
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
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:
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:
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
@@ -2100,25 +2104,35 @@ def _dict_with_qos_specs(rows):
def qos_specs_get(context, qos_specs_id, inactive=False):
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
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:
{'qos-spec-1': {'id': SPECS-UUID,
[{
'id': SPECS-UUID,
'name': 'qos_spec-1',
'consumer': 'back-end',
'specs': {
'key1': 'value1',
'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 {}
#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):
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
@@ -2148,9 +2162,7 @@ def qos_specs_associations_get(context, qos_specs_id):
extend qos specs association to other entities, such as volumes,
sometime in future.
"""
rows = _qos_specs_get_ref(context, qos_specs_id, None)
if not rows:
raise exception.QoSSpecsNotFound(specs_id=qos_specs_id)
_qos_specs_get_ref(context, qos_specs_id, None)
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):
res = dict(name='qos_specs_' + str(id))
res.update(dict(consumer='back-end'))
res.update(dict(id=str(id)))
specs = {"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
"key5": "value5"}
specs.update(dict(id=str(id)))
return specs
res.update(dict(specs=specs))
return res
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):
return dict(
qos_specs_1=stub_qos_specs(1),
qos_specs_2=stub_qos_specs(2),
qos_specs_3=stub_qos_specs(3)
)
return [
stub_qos_specs(1),
stub_qos_specs(2),
stub_qos_specs(3),
]
def return_qos_specs_get_qos_specs(context, id):
if id == "777":
raise exception.QoSSpecsNotFound(specs_id=id)
name = 'qos_specs_%s' % id
return {name: stub_qos_specs(int(id))}
return stub_qos_specs(int(id))
def return_qos_specs_delete(context, id, force):
@@ -142,14 +147,16 @@ class QoSSpecManageApiTest(test.TestCase):
return_qos_specs_get_all)
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']
self.assertEqual(set(res_dict.keys()), set(expected_names))
for key in res_dict.keys():
self.assertEqual('value1', res_dict[key]['key1'])
self.assertEqual(names, set(expected_names))
def test_qos_specs_delete(self):
self.stubs.Set(qos_specs, 'get_qos_specs',
@@ -212,7 +219,6 @@ class QoSSpecManageApiTest(test.TestCase):
res_dict = self.controller.create(req, body)
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
self.assertEqual(1, len(res_dict))
self.assertEqual('qos_specs_1', res_dict['qos_specs']['name'])
def test_create_conflict(self):
@@ -318,8 +324,8 @@ class QoSSpecManageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v2/fake/qos-specs/1')
res_dict = self.controller.show(req, '1')
self.assertEqual(1, len(res_dict))
self.assertEqual('1', res_dict['qos_specs_1']['id'])
self.assertEqual('1', res_dict['qos_specs']['id'])
self.assertEqual('qos_specs_1', res_dict['qos_specs']['name'])
def test_get_associations(self):
self.stubs.Set(qos_specs, 'get_associations',
@@ -329,10 +335,10 @@ class QoSSpecManageApiTest(test.TestCase):
'/v2/fake/qos-specs/1/associations')
res = self.controller.associations(req, '1')
self.assertEqual('1', res.keys()[0])
self.assertEqual('FakeVolTypeName', res['1'].keys()[0])
self.assertEqual('FakeVolTypeName',
res['qos_associations'][0]['name'])
self.assertEqual('FakeVolTypeID',
res['1']['FakeVolTypeName'])
res['qos_associations'][0]['id'])
def test_get_associations_not_found(self):
self.stubs.Set(qos_specs, 'get_associations',

View File

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

View File

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