Cache the User's Project by Token ID
Project list fetched for each request. The patches caches the project list and uses the token as the key in the cache. When the user logout or switch project, the project list is removed from the cache. Change-Id: I2386d7a342cf02a0252e97cc48c5349ccab8a9eb Closes-bug: 1241838
This commit is contained in:
committed by
lin-hua-cheng
parent
58da8b38a9
commit
bd9fd598e6
@@ -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'."""
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user