summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormdietz <mdietz@openstack>2010-12-18 00:18:36 +0000
committermdietz <mdietz@openstack>2010-12-18 00:18:36 +0000
commitf3f5b58f395794b3933cc3489ff37ce08002de89 (patch)
tree7726a499904308a2e5e88b5191711d647a57c782
parent5b8362d0f56bdbeba7ee8292222863a501bad6af (diff)
parent800ecbd713c55d7410d6eb860a439cb87468e7ad (diff)
All API tests finally pass
-rw-r--r--Authors2
-rw-r--r--nova/api/openstack/__init__.py2
-rw-r--r--nova/api/openstack/ratelimiting/__init__.py17
-rw-r--r--nova/auth/fakeldap.py99
-rw-r--r--nova/tests/api/openstack/__init__.py16
-rw-r--r--nova/tests/auth_unittest.py10
6 files changed, 90 insertions, 56 deletions
diff --git a/Authors b/Authors
index 565444ee1..fa38ef0b1 100644
--- a/Authors
+++ b/Authors
@@ -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):