summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-08-06 22:56:24 +0000
committerGerrit Code Review <review@openstack.org>2014-08-06 22:56:24 +0000
commit1f1550822ed1ca47ed9f99270690d1b23c52c161 (patch)
treeb2162a4e2a723df5aba8ec849137159147feafde
parent69c4a34d97ba26adf62b54489d7c23228fc19a6b (diff)
parentbd9fd598e6c2ff11f8c31098cd25c7a42a97d761 (diff)
downloaddjango_openstack_auth-1f1550822ed1ca47ed9f99270690d1b23c52c161.tar.gz
Merge "Cache the User's Project by Token ID"
-rw-r--r--openstack_auth/tests/tests.py92
-rw-r--r--openstack_auth/utils.py38
-rw-r--r--openstack_auth/views.py1
3 files changed, 131 insertions, 0 deletions
diff --git a/openstack_auth/tests/tests.py b/openstack_auth/tests/tests.py
index 4e596a3..b39936a 100644
--- a/openstack_auth/tests/tests.py
+++ b/openstack_auth/tests/tests.py
@@ -489,6 +489,51 @@ class OpenStackAuthTestsV2(test.TestCase):
debug=False)
self.assertEqual(tenant_list, expected_tenants)
+ def test_tenant_list_caching(self):
+ tenants = [self.data.tenant_two, self.data.tenant_one]
+ expected_tenants = [self.data.tenant_one, self.data.tenant_two]
+ user = self.data.user
+ unscoped = self.data.unscoped_access_info
+
+ self.mox.StubOutWithMock(self.ks_client_module, "Client")
+ self.mox.StubOutWithMock(self.keystone_client_unscoped.tenants, "list")
+
+ self.ks_client_module.Client(user_id=user.id,
+ auth_url=settings.OPENSTACK_KEYSTONE_URL,
+ token=unscoped.auth_token,
+ insecure=False,
+ cacert=None,
+ debug=False)\
+ .AndReturn(self.keystone_client_unscoped)
+ self.keystone_client_unscoped.tenants.list().AndReturn(tenants)
+
+ self.mox.ReplayAll()
+
+ tenant_list = utils.get_project_list(
+ user_id=user.id,
+ auth_url=settings.OPENSTACK_KEYSTONE_URL,
+ token=unscoped.auth_token,
+ insecure=False,
+ cacert=None,
+ debug=False)
+ self.assertEqual(tenant_list, expected_tenants)
+
+ # Test to validate that requesting the project list again results
+ # to using the cache and will not make a Keystone call.
+ self.assertEqual(utils._PROJECT_CACHE.get(unscoped.auth_token),
+ expected_tenants)
+ tenant_list = utils.get_project_list(
+ user_id=user.id,
+ auth_url=settings.OPENSTACK_KEYSTONE_URL,
+ token=unscoped.auth_token,
+ insecure=False,
+ cacert=None,
+ debug=False)
+ self.assertEqual(tenant_list, expected_tenants)
+
+ utils.remove_project_cache(unscoped.auth_token)
+ self.assertIsNone(utils._PROJECT_CACHE.get(unscoped.auth_token))
+
def EndpointMetaFactory(endpoint_type):
def endpoint_wrapper(func):
@@ -979,6 +1024,53 @@ class OpenStackAuthTestsV3(test.TestCase):
debug=False)
self.assertEqual(project_list, expected_projects)
+ def test_tenant_list_caching(self):
+ projects = [self.data.project_two, self.data.project_one]
+ expected_projects = [self.data.project_one, self.data.project_two]
+ user = self.data.user
+ unscoped = self.data.unscoped_access_info
+
+ self.mox.StubOutWithMock(self.ks_client_module, "Client")
+ self.mox.StubOutWithMock(self.keystone_client_unscoped.projects,
+ "list")
+
+ self.ks_client_module.Client(user_id=user.id,
+ auth_url=settings.OPENSTACK_KEYSTONE_URL,
+ token=unscoped.auth_token,
+ insecure=False,
+ cacert=None,
+ debug=False)\
+ .AndReturn(self.keystone_client_unscoped)
+ self.keystone_client_unscoped.projects.list(user=user.id) \
+ .AndReturn(projects)
+
+ self.mox.ReplayAll()
+
+ project_list = utils.get_project_list(
+ user_id=user.id,
+ auth_url=settings.OPENSTACK_KEYSTONE_URL,
+ token=unscoped.auth_token,
+ insecure=False,
+ cacert=None,
+ debug=False)
+ self.assertEqual(project_list, expected_projects)
+
+ # Test to validate that requesting the project list again results
+ # to using the cache and will not make a Keystone call.
+ self.assertEqual(utils._PROJECT_CACHE.get(unscoped.auth_token),
+ expected_projects)
+ project_list = utils.get_project_list(
+ user_id=user.id,
+ auth_url=settings.OPENSTACK_KEYSTONE_URL,
+ token=unscoped.auth_token,
+ insecure=False,
+ cacert=None,
+ debug=False)
+ self.assertEqual(project_list, expected_projects)
+
+ utils.remove_project_cache(unscoped.auth_token)
+ self.assertIsNone(utils._PROJECT_CACHE.get(unscoped.auth_token))
+
class OpenStackAuthTestsV3WithPublicURL(OpenStackAuthTestsV3):
"""Test V3 with settings.OPENSTACK_ENDPOINT_TYPE = 'publicURL'."""
diff --git a/openstack_auth/utils.py b/openstack_auth/utils.py
index 482b7a4..f6da16d 100644
--- a/openstack_auth/utils.py
+++ b/openstack_auth/utils.py
@@ -11,18 +11,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import functools
+
from six.moves.urllib import parse as urlparse
from django.conf import settings
from django.contrib import auth
from django.contrib.auth import middleware
from django.contrib.auth import models
+from django.utils import decorators
from django.utils import timezone
from keystoneclient.v2_0 import client as client_v2
from keystoneclient.v3 import client as client_v3
+_PROJECT_CACHE = {}
+
+
"""
We need the request object to get the user, so we'll slightly modify the
existing django.contrib.auth.get_user method. To do so we update the
@@ -91,6 +97,37 @@ def is_safe_url(url, host=None):
return not netloc or netloc == host
+def memoize_by_keyword_arg(cache, kw_keys):
+ """Memoize a function using the list of keyword argument name as its key.
+
+ Wrap a function so that results for any keyword argument tuple are stored
+ in 'cache'. Note that the keyword args to the function must be usable as
+ dictionary keys.
+
+ :param cache: Dictionary object to store the results.
+ :param kw_keys: List of keyword arguments names. The values are used
+ for generating the key in the cache.
+ """
+ def _decorator(func):
+ @functools.wraps(func, assigned=decorators.available_attrs(func))
+ def wrapper(*args, **kwargs):
+ mem_args = [kwargs[key] for key in kw_keys if key in kwargs]
+ mem_args = '__'.join(str(mem_arg) for mem_arg in mem_args)
+ if not mem_args:
+ return func(*args, **kwargs)
+ if mem_args in cache:
+ return cache[mem_args]
+ result = func(*args, **kwargs)
+ cache[mem_args] = result
+ return result
+ return wrapper
+ return _decorator
+
+
+def remove_project_cache(token):
+ _PROJECT_CACHE.pop(token, None)
+
+
# Helper for figuring out keystone version
# Implementation will change when API version discovery is available
def get_keystone_version():
@@ -104,6 +141,7 @@ def get_keystone_client():
return client_v3
+@memoize_by_keyword_arg(_PROJECT_CACHE, ('token', ))
def get_project_list(*args, **kwargs):
if get_keystone_version() < 3:
auth_url = kwargs.get('auth_url', '').replace('v3', 'v2.0')
diff --git a/openstack_auth/views.py b/openstack_auth/views.py
index 380223c..4ce09b7 100644
--- a/openstack_auth/views.py
+++ b/openstack_auth/views.py
@@ -119,6 +119,7 @@ def delete_token(endpoint, token_id):
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None)
+ utils.remove_project_cache(token_id)
try:
if utils.get_keystone_version() < 3:
client = keystone_client_v2.Client(