diff options
| author | mdietz <mdietz@openstack> | 2010-12-18 00:18:36 +0000 |
|---|---|---|
| committer | mdietz <mdietz@openstack> | 2010-12-18 00:18:36 +0000 |
| commit | f3f5b58f395794b3933cc3489ff37ce08002de89 (patch) | |
| tree | 7726a499904308a2e5e88b5191711d647a57c782 | |
| parent | 5b8362d0f56bdbeba7ee8292222863a501bad6af (diff) | |
| parent | 800ecbd713c55d7410d6eb860a439cb87468e7ad (diff) | |
All API tests finally pass
| -rw-r--r-- | Authors | 2 | ||||
| -rw-r--r-- | nova/api/openstack/__init__.py | 2 | ||||
| -rw-r--r-- | nova/api/openstack/ratelimiting/__init__.py | 17 | ||||
| -rw-r--r-- | nova/auth/fakeldap.py | 99 | ||||
| -rw-r--r-- | nova/tests/api/openstack/__init__.py | 16 | ||||
| -rw-r--r-- | nova/tests/auth_unittest.py | 10 |
6 files changed, 90 insertions, 56 deletions
@@ -6,6 +6,7 @@ Chris Behrens <cbehrens@codestud.com> Chmouel Boudjnah <chmouel@chmouel.com> Dean Troyer <dtroyer@gmail.com> Devin Carlen <devin.carlen@gmail.com> +Ed Leafe <ed@leafe.com> Eldar Nugaev <enugaev@griddynamics.com> Eric Day <eday@oddments.org> Ewan Mellor <ewan.mellor@citrix.com> @@ -14,6 +15,7 @@ Jay Pipes <jaypipes@gmail.com> Jesse Andrews <anotherjesse@gmail.com> Joe Heck <heckj@mac.com> Joel Moore <joelbm24@gmail.com> +Jonathan Bryce <jbryce@jbryce.com> Josh Kearney <josh.kearney@rackspace.com> Joshua McKenty <jmckenty@gmail.com> Justin Santa Barbara <justin@fathomdb.com> diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index e78080012..20336d885 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -113,7 +113,7 @@ class RateLimitingMiddleware(wsgi.Middleware): @webob.dec.wsgify def __call__(self, req): - return self._limiting_driver.limited_request(req) + return self._limiting_driver.limited_request(req, self.application) class APIRouter(wsgi.Router): diff --git a/nova/api/openstack/ratelimiting/__init__.py b/nova/api/openstack/ratelimiting/__init__.py index 2dc5ec32e..3c1e6e1eb 100644 --- a/nova/api/openstack/ratelimiting/__init__.py +++ b/nova/api/openstack/ratelimiting/__init__.py @@ -6,6 +6,7 @@ import urllib import webob.dec import webob.exc +from nova.api.openstack import faults # Convenience constants for the limits dictionary passed to Limiter(). PER_SECOND = 1 @@ -22,16 +23,16 @@ class BasicRateLimiting(object): #TODO(gundlach): These limits were based on limitations of Cloud #Servers. We should revisit them in Nova. self.limiter = Limiter(limits={ - 'DELETE': (100, ratelimiting.PER_MINUTE), - 'PUT': (10, ratelimiting.PER_MINUTE), - 'POST': (10, ratelimiting.PER_MINUTE), - 'POST servers': (50, ratelimiting.PER_DAY), - 'GET changes-since': (3, ratelimiting.PER_MINUTE), + 'DELETE': (100, PER_MINUTE), + 'PUT': (10, PER_MINUTE), + 'POST': (10, PER_MINUTE), + 'POST servers': (50, PER_DAY), + 'GET changes-since': (3, PER_MINUTE), }) else: self.limiter = WSGIAppProxy(service_host) - def limited_request(self, req): + def limited_request(self, req, application): """Rate limit the request. If the request should be rate limited, return a 413 status with a @@ -40,7 +41,7 @@ class BasicRateLimiting(object): action_name = self.get_action_name(req) if not action_name: # Not rate limited - return self.application + return application delay = self.get_delay(action_name, req.environ['nova.context'].user_id) if delay: @@ -49,7 +50,7 @@ class BasicRateLimiting(object): explanation='Too many requests.', headers={'Retry-After': time.time() + delay}) raise faults.Fault(exc) - return self.application + return application def get_delay(self, action_name, username): """Return the delay for the given action and username, or None if diff --git a/nova/auth/fakeldap.py b/nova/auth/fakeldap.py index 46e0135b4..1ac579dbd 100644 --- a/nova/auth/fakeldap.py +++ b/nova/auth/fakeldap.py @@ -15,7 +15,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -"""Fake LDAP server for test harness, backs to ReDIS. +"""Fake LDAP server for test harness. This class does very little error checking, and knows nothing about ldap class definitions. It implements the minimum emulation of the python ldap @@ -23,20 +23,11 @@ library to work with nova. """ +import fnmatch import json -import redis -from nova import flags -FLAGS = flags.FLAGS -flags.DEFINE_string('redis_host', '127.0.0.1', - 'Host that redis is running on.') -flags.DEFINE_integer('redis_port', 6379, - 'Port that redis is running on.') -flags.DEFINE_integer('redis_db', 0, 'Multiple DB keeps tests away') - - -class Redis(object): +class Store(object): def __init__(self): if hasattr(self.__class__, '_instance'): raise Exception('Attempted to instantiate singleton') @@ -44,13 +35,53 @@ class Redis(object): @classmethod def instance(cls): if not hasattr(cls, '_instance'): - inst = redis.Redis(host=FLAGS.redis_host, - port=FLAGS.redis_port, - db=FLAGS.redis_db) - cls._instance = inst + cls._instance = _StorageDict() return cls._instance +class _StorageDict(dict): + def keys(self, pat=None): + ret = super(_StorageDict, self).keys() + if pat is not None: + ret = fnmatch.filter(ret, pat) + return ret + + def delete(self, key): + try: + del self[key] + except KeyError: + pass + + def flushdb(self): + self.clear() + + def hgetall(self, key): + """Returns the hash for the given key; creates + the hash if the key doesn't exist.""" + try: + return self[key] + except KeyError: + self[key] = {} + return self[key] + + def hget(self, key, field): + hashdict = self.hgetall(key) + try: + return hashdict[field] + except KeyError: + hashdict[field] = {} + return hashdict[field] + + def hset(self, key, field, val): + hashdict = self.hgetall(key) + hashdict[field] = val + + def hmset(self, key, value_dict): + hashdict = self.hgetall(key) + for field, val in value_dict.items(): + hashdict[field] = val + + SCOPE_BASE = 0 SCOPE_ONELEVEL = 1 # Not implemented SCOPE_SUBTREE = 2 @@ -169,8 +200,6 @@ def _to_json(unencoded): class FakeLDAP(object): - #TODO(vish): refactor this class to use a wrapper instead of accessing - # redis directly """Fake LDAP connection.""" def simple_bind_s(self, dn, password): @@ -183,14 +212,13 @@ class FakeLDAP(object): def add_s(self, dn, attr): """Add an object with the specified attributes at dn.""" - key = "%s%s" % (self.__redis_prefix, dn) - + key = "%s%s" % (self.__prefix, dn) value_dict = dict([(k, _to_json(v)) for k, v in attr]) - Redis.instance().hmset(key, value_dict) + Store.instance().hmset(key, value_dict) def delete_s(self, dn): """Remove the ldap object at specified dn.""" - Redis.instance().delete("%s%s" % (self.__redis_prefix, dn)) + Store.instance().delete("%s%s" % (self.__prefix, dn)) def modify_s(self, dn, attrs): """Modify the object at dn using the attribute list. @@ -201,18 +229,18 @@ class FakeLDAP(object): ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value) """ - redis = Redis.instance() - key = "%s%s" % (self.__redis_prefix, dn) + store = Store.instance() + key = "%s%s" % (self.__prefix, dn) for cmd, k, v in attrs: - values = _from_json(redis.hget(key, k)) + values = _from_json(store.hget(key, k)) if cmd == MOD_ADD: values.append(v) elif cmd == MOD_REPLACE: values = [v] else: values.remove(v) - values = redis.hset(key, k, _to_json(values)) + values = store.hset(key, k, _to_json(values)) def search_s(self, dn, scope, query=None, fields=None): """Search for all matching objects under dn using the query. @@ -226,16 +254,17 @@ class FakeLDAP(object): """ if scope != SCOPE_BASE and scope != SCOPE_SUBTREE: raise NotImplementedError(str(scope)) - redis = Redis.instance() + store = Store.instance() if scope == SCOPE_BASE: - keys = ["%s%s" % (self.__redis_prefix, dn)] + keys = ["%s%s" % (self.__prefix, dn)] else: - keys = redis.keys("%s*%s" % (self.__redis_prefix, dn)) + keys = store.keys("%s*%s" % (self.__prefix, dn)) + objects = [] for key in keys: - # get the attributes from redis - attrs = redis.hgetall(key) - # turn the values from redis into lists + # get the attributes from the store + attrs = store.hgetall(key) + # turn the values from the store into lists # pylint: disable-msg=E1103 attrs = dict([(k, _from_json(v)) for k, v in attrs.iteritems()]) @@ -244,13 +273,13 @@ class FakeLDAP(object): # filter the attributes by fields attrs = dict([(k, v) for k, v in attrs.iteritems() if not fields or k in fields]) - objects.append((key[len(self.__redis_prefix):], attrs)) + objects.append((key[len(self.__prefix):], attrs)) # pylint: enable-msg=E1103 if objects == []: raise NO_SUCH_OBJECT() return objects @property - def __redis_prefix(self): # pylint: disable-msg=R0201 - """Get the prefix to use for all redis keys.""" + def __prefix(self): # pylint: disable-msg=R0201 + """Get the prefix to use for all keys.""" return 'ldap:' diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 4e4dfe4fc..fffc57e67 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -17,11 +17,15 @@ import unittest +from nova import context +from nova import flags from nova.api.openstack.common import limited from nova.api.openstack import RateLimitingMiddleware from nova.tests.api.fakes import APIStub +from nova import utils from webob import Request +FLAGS = flags.FLAGS class RateLimitingMiddlewareTest(unittest.TestCase): @@ -31,7 +35,7 @@ class RateLimitingMiddlewareTest(unittest.TestCase): def verify(method, url, action_name): req = Request.blank(url) req.method = method - action = middleware.get_action_name(req) + action = middleware._limiting_driver.get_action_name(req) self.assertEqual(action, action_name) verify('PUT', '/servers/4', 'PUT') @@ -46,6 +50,8 @@ class RateLimitingMiddlewareTest(unittest.TestCase): def exhaust(self, middleware, method, url, username, times): req = Request.blank(url, dict(REQUEST_METHOD=method), headers={'X-Auth-User': username}) + req.environ['nova.context'] = context.RequestContext(username, + username) for i in range(times): resp = req.get_response(middleware) self.assertEqual(resp.status_int, 200) @@ -62,7 +68,7 @@ class RateLimitingMiddlewareTest(unittest.TestCase): middleware = RateLimitingMiddleware(APIStub()) self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) self.exhaust(middleware, 'POST', '/images/4', 'usr2', 10) - self.assertTrue(set(middleware.limiter._levels) == + self.assertTrue(set(middleware._limiting_driver.limiter._levels) == \ set(['usr1:POST', 'usr1:POST servers', 'usr2:POST'])) def test_POST_servers_action_correctly_ratelimited(self): @@ -71,15 +77,15 @@ class RateLimitingMiddlewareTest(unittest.TestCase): for i in range(5): self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) # Reset the 'POST' action counter. - del middleware.limiter._levels['usr1:POST'] + del middleware._limiting_driver.limiter._levels['usr1:POST'] # All 50 daily "POST servers" actions should be all used up self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 0) def test_proxy_ctor_works(self): middleware = RateLimitingMiddleware(APIStub()) - self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") + self.assertEqual(middleware._limiting_driver.limiter.__class__.__name__, "Limiter") middleware = RateLimitingMiddleware(APIStub(), service_host='foobar') - self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") + self.assertEqual(middleware._limiting_driver.limiter.__class__.__name__, "WSGIAppProxy") class LimiterTest(unittest.TestCase): diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 4508d6721..61ae43fb1 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -333,14 +333,10 @@ class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase): AuthManagerTestCase.__init__(self) test.TestCase.__init__(self, *args, **kwargs) import nova.auth.fakeldap as fakeldap - FLAGS.redis_db = 8 if FLAGS.flush_db: - logging.info("Flushing redis datastore") - try: - r = fakeldap.Redis.instance() - r.flushdb() - except: - self.skip = True + logging.info("Flushing datastore") + r = fakeldap.Store.instance() + r.flushdb() class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase): |
