]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Quota API is now compatible with keystone API v2
authorSzymon Borkowski <szymon.borkowski@intel.com>
Thu, 17 Dec 2015 12:18:31 +0000 (13:18 +0100)
committerSzymon Borkowski <szymon.borkowski@intel.com>
Wed, 23 Dec 2015 10:33:44 +0000 (11:33 +0100)
Before, the quota API used to fail to perform calls with specified
keystone API v2, because the keystone client used in code was set
to v3 and all quota operations used to assume that the user would
use only keystone API v3.
Now, we use the generic client, which discovers the version of the
keystone API, so depending on that, the quota API can perform
appropriate operations.

Change-Id: I32595a37a9fe74ede77c92f76e0865f4c9371f65
Closes-Bug: 1517043

cinder/api/contrib/quotas.py
cinder/tests/unit/api/contrib/test_quotas.py

index 4dfc53f52554630e8006e17f3cc9758b923d173d..b5e0bb954f8509c570fe73c9678583b34d908fe0 100644 (file)
@@ -57,6 +57,17 @@ class QuotaTemplate(xmlutil.TemplateBuilder):
 
 class QuotaSetsController(wsgi.Controller):
 
+    class GenericProjectInfo(object):
+
+        """Abstraction layer for Keystone V2 and V3 project objects"""
+
+        def __init__(self, project_id, project_keystone_api_version,
+                     project_parent_id=None, project_subtree=None):
+            self.id = project_id
+            self.keystone_api_version = project_keystone_api_version
+            self.parent_id = project_parent_id
+            self.subtree = project_subtree
+
     def _format_quota_set(self, project_id, quota_set):
         """Convert the quota object to a result dict."""
 
@@ -64,6 +75,20 @@ class QuotaSetsController(wsgi.Controller):
 
         return dict(quota_set=quota_set)
 
+    def _keystone_client(self, context):
+        """Creates and returns an instance of a generic keystone client.
+
+        :param context: The request context
+        :return: keystoneclient.client.Client object
+        """
+        auth_plugin = token.Token(
+            auth_url=CONF.keystone_authtoken.auth_uri,
+            token=context.auth_token,
+            project_id=context.project_id)
+        client_session = session.Session(auth=auth_plugin)
+        return client.Client(auth_url=CONF.keystone_authtoken.auth_uri,
+                             session=client_session)
+
     def _validate_existing_resource(self, key, value, quota_values):
         if key == 'per_volume_gigabytes':
             return
@@ -177,23 +202,23 @@ class QuotaSetsController(wsgi.Controller):
     def _get_project(self, context, id, subtree_as_ids=False):
         """A Helper method to get the project hierarchy.
 
-        Along with Hierachical Multitenancy, projects can be hierarchically
-        organized. Therefore, we need to know the project hierarchy, if any, in
-        order to do quota operations properly.
+        Along with Hierachical Multitenancy in keystone API v3, projects can be
+        hierarchically organized. Therefore, we need to know the project
+        hierarchy, if any, in order to do quota operations properly.
         """
         try:
-            auth_plugin = token.Token(
-                auth_url=CONF.keystone_authtoken.auth_uri,
-                token=context.auth_token,
-                project_id=context.project_id)
-            client_session = session.Session(auth=auth_plugin)
-            keystone = client.Client(auth_url=CONF.keystone_authtoken.auth_uri,
-                                     session=client_session)
-            project = keystone.projects.get(id, subtree_as_ids=subtree_as_ids)
+            keystone = self._keystone_client(context)
+            generic_project = self.GenericProjectInfo(id, keystone.version)
+            if keystone.version == 'v3':
+                project = keystone.projects.get(id,
+                                                subtree_as_ids=subtree_as_ids)
+                generic_project.parent_id = project.parent_id
+                generic_project.subtree = (
+                    project.subtree if subtree_as_ids else None)
         except exceptions.NotFound:
             msg = (_("Tenant ID: %s does not exist.") % id)
             raise webob.exc.HTTPNotFound(explanation=msg)
-        return project
+        return generic_project
 
     @wsgi.serializers(xml=QuotaTemplate)
     def show(self, req, id):
index 741fa0295db8d84346e77dbeb22d21c658fd2f72..3fbe369c4e6bcd04e0167006839b0a37d44e4f63 100644 (file)
@@ -134,17 +134,49 @@ class QuotaSetsControllerTest(test.TestCase):
     def test_keystone_client_instantiation(self, ksclient_session,
                                            ksclient_class):
         context = self.req.environ['cinder.context']
-        self.controller._get_project(context, context.project_id)
+        self.controller._keystone_client(context)
         ksclient_class.assert_called_once_with(auth_url=self.auth_url,
                                                session=ksclient_session())
 
     @mock.patch('keystoneclient.client.Client')
-    def test_get_project(self, ksclient_class):
+    def test_get_project_keystoneclient_v2(self, ksclient_class):
         context = self.req.environ['cinder.context']
         keystoneclient = ksclient_class.return_value
-        self.controller._get_project(context, context.project_id)
+        keystoneclient.version = 'v2.0'
+        expected_project = self.controller.GenericProjectInfo(
+            context.project_id, 'v2.0')
+        project = self.controller._get_project(context, context.project_id)
+        self.assertEqual(expected_project.__dict__, project.__dict__)
+
+    @mock.patch('keystoneclient.client.Client')
+    def test_get_project_keystoneclient_v3(self, ksclient_class):
+        context = self.req.environ['cinder.context']
+        keystoneclient = ksclient_class.return_value
+        keystoneclient.version = 'v3'
+        returned_project = self.FakeProject(context.project_id, 'bar')
+        del returned_project.subtree
+        keystoneclient.projects.get.return_value = returned_project
+        expected_project = self.controller.GenericProjectInfo(
+            context.project_id, 'v3', 'bar')
+        project = self.controller._get_project(context, context.project_id)
+        self.assertEqual(expected_project.__dict__, project.__dict__)
+
+    @mock.patch('keystoneclient.client.Client')
+    def test_get_project_keystoneclient_v3_with_subtree(self, ksclient_class):
+        context = self.req.environ['cinder.context']
+        keystoneclient = ksclient_class.return_value
+        keystoneclient.version = 'v3'
+        returned_project = self.FakeProject(context.project_id, 'bar')
+        subtree_dict = {'baz': {'quux': None}}
+        returned_project.subtree = subtree_dict
+        keystoneclient.projects.get.return_value = returned_project
+        expected_project = self.controller.GenericProjectInfo(
+            context.project_id, 'v3', 'bar', subtree_dict)
+        project = self.controller._get_project(context, context.project_id,
+                                               subtree_as_ids=True)
         keystoneclient.projects.get.assert_called_once_with(
-            context.project_id, subtree_as_ids=False)
+            context.project_id, subtree_as_ids=True)
+        self.assertEqual(expected_project.__dict__, project.__dict__)
 
     def test_defaults(self):
         self.controller._get_project = mock.Mock()