Merge lp:~ed-leafe/nova/remove-redis into lp:~hudson-openstack/nova/trunk

Proposed by Ed Leafe
Status: Merged
Approved by: Soren Hansen
Approved revision: 469
Merged at revision: 468
Proposed branch: lp:~ed-leafe/nova/remove-redis
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 204 lines (+68/-40)
2 files modified
nova/auth/fakeldap.py (+66/-37)
nova/tests/auth_unittest.py (+2/-3)
To merge this branch: bzr merge lp:~ed-leafe/nova/remove-redis
Reviewer Review Type Date Requested Status
Soren Hansen (community) Approve
Vish Ishaya (community) Approve
Review via email: mp+44074@code.launchpad.net

Description of the change

Replaced the use of redis in fakeldap with a customized dict class. Auth unittests should now run fine without a redis server running, or without python-redis installed.

To post a comment you must log in.
Revision history for this message
Vish Ishaya (vishvananda) wrote :

Die Redis Die!

review: Approve
Revision history for this message
Jonathan Bryce (jbryce) wrote :

On lines 338-342 of /nova/tests/auth_unittest.py the exception handling is now superfluous and the self.skip is unnecessary as well. 'skip' was a property available from the TrialTestCase class that isn't used with PyUnit TestCase. This is minor but it will keep future code readers from wondering what that block is supposed to be doing.

Revision history for this message
Soren Hansen (soren) wrote :

Excellent, thanks a lot Ed!

jbryce, I've filed https://bugs.edge.launchpad.net/nova/+bug/691704 about that issue. I don't see any reason why it should block this merge, so tracking it separately.

review: Approve
Revision history for this message
Soren Hansen (soren) wrote :

Whoops, Ed, you need to add yourself to Authors.

Revision history for this message
Soren Hansen (soren) wrote :

No worries, jbryce took care of it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'nova/auth/fakeldap.py'
--- nova/auth/fakeldap.py 2010-10-29 15:58:57 +0000
+++ nova/auth/fakeldap.py 2010-12-17 17:31:34 +0000
@@ -15,7 +15,7 @@
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations16# License for the specific language governing permissions and limitations
17# under the License.17# under the License.
18"""Fake LDAP server for test harness, backs to ReDIS.18"""Fake LDAP server for test harness.
1919
20This class does very little error checking, and knows nothing about ldap20This class does very little error checking, and knows nothing about ldap
21class definitions. It implements the minimum emulation of the python ldap21class definitions. It implements the minimum emulation of the python ldap
@@ -23,20 +23,11 @@
2323
24"""24"""
2525
26import fnmatch
26import json27import json
27import redis28
2829
29from nova import flags30class Store(object):
30
31FLAGS = flags.FLAGS
32flags.DEFINE_string('redis_host', '127.0.0.1',
33 'Host that redis is running on.')
34flags.DEFINE_integer('redis_port', 6379,
35 'Port that redis is running on.')
36flags.DEFINE_integer('redis_db', 0, 'Multiple DB keeps tests away')
37
38
39class Redis(object):
40 def __init__(self):31 def __init__(self):
41 if hasattr(self.__class__, '_instance'):32 if hasattr(self.__class__, '_instance'):
42 raise Exception('Attempted to instantiate singleton')33 raise Exception('Attempted to instantiate singleton')
@@ -44,13 +35,53 @@
44 @classmethod35 @classmethod
45 def instance(cls):36 def instance(cls):
46 if not hasattr(cls, '_instance'):37 if not hasattr(cls, '_instance'):
47 inst = redis.Redis(host=FLAGS.redis_host,38 cls._instance = _StorageDict()
48 port=FLAGS.redis_port,
49 db=FLAGS.redis_db)
50 cls._instance = inst
51 return cls._instance39 return cls._instance
5240
5341
42class _StorageDict(dict):
43 def keys(self, pat=None):
44 ret = super(_StorageDict, self).keys()
45 if pat is not None:
46 ret = fnmatch.filter(ret, pat)
47 return ret
48
49 def delete(self, key):
50 try:
51 del self[key]
52 except KeyError:
53 pass
54
55 def flushdb(self):
56 self.clear()
57
58 def hgetall(self, key):
59 """Returns the hash for the given key; creates
60 the hash if the key doesn't exist."""
61 try:
62 return self[key]
63 except KeyError:
64 self[key] = {}
65 return self[key]
66
67 def hget(self, key, field):
68 hashdict = self.hgetall(key)
69 try:
70 return hashdict[field]
71 except KeyError:
72 hashdict[field] = {}
73 return hashdict[field]
74
75 def hset(self, key, field, val):
76 hashdict = self.hgetall(key)
77 hashdict[field] = val
78
79 def hmset(self, key, value_dict):
80 hashdict = self.hgetall(key)
81 for field, val in value_dict.items():
82 hashdict[field] = val
83
84
54SCOPE_BASE = 085SCOPE_BASE = 0
55SCOPE_ONELEVEL = 1 # Not implemented86SCOPE_ONELEVEL = 1 # Not implemented
56SCOPE_SUBTREE = 287SCOPE_SUBTREE = 2
@@ -169,8 +200,6 @@
169200
170201
171class FakeLDAP(object):202class FakeLDAP(object):
172 #TODO(vish): refactor this class to use a wrapper instead of accessing
173 # redis directly
174 """Fake LDAP connection."""203 """Fake LDAP connection."""
175204
176 def simple_bind_s(self, dn, password):205 def simple_bind_s(self, dn, password):
@@ -183,14 +212,13 @@
183212
184 def add_s(self, dn, attr):213 def add_s(self, dn, attr):
185 """Add an object with the specified attributes at dn."""214 """Add an object with the specified attributes at dn."""
186 key = "%s%s" % (self.__redis_prefix, dn)215 key = "%s%s" % (self.__prefix, dn)
187
188 value_dict = dict([(k, _to_json(v)) for k, v in attr])216 value_dict = dict([(k, _to_json(v)) for k, v in attr])
189 Redis.instance().hmset(key, value_dict)217 Store.instance().hmset(key, value_dict)
190218
191 def delete_s(self, dn):219 def delete_s(self, dn):
192 """Remove the ldap object at specified dn."""220 """Remove the ldap object at specified dn."""
193 Redis.instance().delete("%s%s" % (self.__redis_prefix, dn))221 Store.instance().delete("%s%s" % (self.__prefix, dn))
194222
195 def modify_s(self, dn, attrs):223 def modify_s(self, dn, attrs):
196 """Modify the object at dn using the attribute list.224 """Modify the object at dn using the attribute list.
@@ -201,18 +229,18 @@
201 ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)229 ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
202230
203 """231 """
204 redis = Redis.instance()232 store = Store.instance()
205 key = "%s%s" % (self.__redis_prefix, dn)233 key = "%s%s" % (self.__prefix, dn)
206234
207 for cmd, k, v in attrs:235 for cmd, k, v in attrs:
208 values = _from_json(redis.hget(key, k))236 values = _from_json(store.hget(key, k))
209 if cmd == MOD_ADD:237 if cmd == MOD_ADD:
210 values.append(v)238 values.append(v)
211 elif cmd == MOD_REPLACE:239 elif cmd == MOD_REPLACE:
212 values = [v]240 values = [v]
213 else:241 else:
214 values.remove(v)242 values.remove(v)
215 values = redis.hset(key, k, _to_json(values))243 values = store.hset(key, k, _to_json(values))
216244
217 def search_s(self, dn, scope, query=None, fields=None):245 def search_s(self, dn, scope, query=None, fields=None):
218 """Search for all matching objects under dn using the query.246 """Search for all matching objects under dn using the query.
@@ -226,16 +254,17 @@
226 """254 """
227 if scope != SCOPE_BASE and scope != SCOPE_SUBTREE:255 if scope != SCOPE_BASE and scope != SCOPE_SUBTREE:
228 raise NotImplementedError(str(scope))256 raise NotImplementedError(str(scope))
229 redis = Redis.instance()257 store = Store.instance()
230 if scope == SCOPE_BASE:258 if scope == SCOPE_BASE:
231 keys = ["%s%s" % (self.__redis_prefix, dn)]259 keys = ["%s%s" % (self.__prefix, dn)]
232 else:260 else:
233 keys = redis.keys("%s*%s" % (self.__redis_prefix, dn))261 keys = store.keys("%s*%s" % (self.__prefix, dn))
262
234 objects = []263 objects = []
235 for key in keys:264 for key in keys:
236 # get the attributes from redis265 # get the attributes from the store
237 attrs = redis.hgetall(key)266 attrs = store.hgetall(key)
238 # turn the values from redis into lists267 # turn the values from the store into lists
239 # pylint: disable-msg=E1103268 # pylint: disable-msg=E1103
240 attrs = dict([(k, _from_json(v))269 attrs = dict([(k, _from_json(v))
241 for k, v in attrs.iteritems()])270 for k, v in attrs.iteritems()])
@@ -244,13 +273,13 @@
244 # filter the attributes by fields273 # filter the attributes by fields
245 attrs = dict([(k, v) for k, v in attrs.iteritems()274 attrs = dict([(k, v) for k, v in attrs.iteritems()
246 if not fields or k in fields])275 if not fields or k in fields])
247 objects.append((key[len(self.__redis_prefix):], attrs))276 objects.append((key[len(self.__prefix):], attrs))
248 # pylint: enable-msg=E1103277 # pylint: enable-msg=E1103
249 if objects == []:278 if objects == []:
250 raise NO_SUCH_OBJECT()279 raise NO_SUCH_OBJECT()
251 return objects280 return objects
252281
253 @property282 @property
254 def __redis_prefix(self): # pylint: disable-msg=R0201283 def __prefix(self): # pylint: disable-msg=R0201
255 """Get the prefix to use for all redis keys."""284 """Get the prefix to use for all keys."""
256 return 'ldap:'285 return 'ldap:'
257286
=== modified file 'nova/tests/auth_unittest.py'
--- nova/tests/auth_unittest.py 2010-12-10 00:05:13 +0000
+++ nova/tests/auth_unittest.py 2010-12-17 17:31:34 +0000
@@ -333,11 +333,10 @@
333 AuthManagerTestCase.__init__(self)333 AuthManagerTestCase.__init__(self)
334 test.TestCase.__init__(self, *args, **kwargs)334 test.TestCase.__init__(self, *args, **kwargs)
335 import nova.auth.fakeldap as fakeldap335 import nova.auth.fakeldap as fakeldap
336 FLAGS.redis_db = 8
337 if FLAGS.flush_db:336 if FLAGS.flush_db:
338 logging.info("Flushing redis datastore")337 logging.info("Flushing datastore")
339 try:338 try:
340 r = fakeldap.Redis.instance()339 r = fakeldap.Store.instance()
341 r.flushdb()340 r.flushdb()
342 except:341 except:
343 self.skip = True342 self.skip = True