]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add view builder to QoS specs API extension
authorZhiteng Huang <zhithuang@ebaysf.com>
Fri, 30 Aug 2013 14:28:52 +0000 (22:28 +0800)
committerZhiteng Huang <zhithuang@ebaysf.com>
Wed, 4 Sep 2013 02:47:12 +0000 (10:47 +0800)
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

cinder/api/contrib/qos_specs_manage.py
cinder/api/views/qos_specs.py [new file with mode: 0644]
cinder/db/sqlalchemy/api.py
cinder/tests/api/contrib/test_qos_specs_manage.py
cinder/tests/db/test_qos_specs.py
cinder/tests/test_qos_specs.py
cinder/volume/qos_specs.py

index 425b46d66bcc1e327167b2d2146657c0d4c5ef35..3a45d39c0a8bc8c5738132058270e306713b0cc0 100644 (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)
 
diff --git a/cinder/api/views/qos_specs.py b/cinder/api/views/qos_specs.py
new file mode 100644 (file)
index 0000000..cd83862
--- /dev/null
@@ -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
index d8a40eb5d99abea392b56a8cf9dd467c5c991eab..79f5579eea1485bdaf8f14c94a29c163a78b3165 100644 (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,
-                        '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-1',
+            'consumer': 'back-end',
+            'specs': {
+                'key1': 'value1',
+                'key2': 'value2',
+                ...
+            }
+         },
+         {
+            '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)
 
index 0bf8e588d91c9a25c8e8cf19653bad5f11de9315..a283a1682027c392df5fbad824f3787e1b1f440a 100644 (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',
index 7d8f1d43fd6a0c7980ac9e16a76a85c7b435a920..3b1be4f075f72c536a3a0403dc7bac37fbef0837 100644 (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')
index 067fa319367b097694a6f998d16580a1bd265f02..dbd4b9c287282809aa265dfdc7cb88abd57d6a47 100644 (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',
-                               'id': specs_id1,
-                               'key2': 'value2',
-                               'key3': 'value3'},
-                    'Specs2': {'key1': 'value1',
-                               'id': specs_id2,
-                               'key2': 'value2',
-                               'key3': 'value3',
-                               'key4': 'value4'}}
+        expected1 = {
+            'id': specs_id1,
+            'name': 'Specs1',
+            'consumer': 'both',
+            'specs': {'key1': 'value1',
+                      'key2': 'value2',
+                      '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)
index 2b933cc4092ea31734128fd314c7e50a8c05c783..436c482058fbd68ddcb99ad8e5ef5fd5db751224 100644 (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):