diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-08-06 22:56:24 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-08-06 22:56:24 +0000 |
commit | 1f1550822ed1ca47ed9f99270690d1b23c52c161 (patch) | |
tree | b2162a4e2a723df5aba8ec849137159147feafde | |
parent | 69c4a34d97ba26adf62b54489d7c23228fc19a6b (diff) | |
parent | bd9fd598e6c2ff11f8c31098cd25c7a42a97d761 (diff) | |
download | django_openstack_auth-1f1550822ed1ca47ed9f99270690d1b23c52c161.tar.gz |
Merge "Cache the User's Project by Token ID"
-rw-r--r-- | openstack_auth/tests/tests.py | 92 | ||||
-rw-r--r-- | openstack_auth/utils.py | 38 | ||||
-rw-r--r-- | openstack_auth/views.py | 1 |
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( |