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
1=== modified file 'nova/auth/fakeldap.py'
2--- nova/auth/fakeldap.py 2010-10-29 15:58:57 +0000
3+++ nova/auth/fakeldap.py 2010-12-17 17:31:34 +0000
4@@ -15,7 +15,7 @@
5 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
6 # License for the specific language governing permissions and limitations
7 # under the License.
8-"""Fake LDAP server for test harness, backs to ReDIS.
9+"""Fake LDAP server for test harness.
10
11 This class does very little error checking, and knows nothing about ldap
12 class definitions. It implements the minimum emulation of the python ldap
13@@ -23,20 +23,11 @@
14
15 """
16
17+import fnmatch
18 import json
19-import redis
20-
21-from nova import flags
22-
23-FLAGS = flags.FLAGS
24-flags.DEFINE_string('redis_host', '127.0.0.1',
25- 'Host that redis is running on.')
26-flags.DEFINE_integer('redis_port', 6379,
27- 'Port that redis is running on.')
28-flags.DEFINE_integer('redis_db', 0, 'Multiple DB keeps tests away')
29-
30-
31-class Redis(object):
32+
33+
34+class Store(object):
35 def __init__(self):
36 if hasattr(self.__class__, '_instance'):
37 raise Exception('Attempted to instantiate singleton')
38@@ -44,13 +35,53 @@
39 @classmethod
40 def instance(cls):
41 if not hasattr(cls, '_instance'):
42- inst = redis.Redis(host=FLAGS.redis_host,
43- port=FLAGS.redis_port,
44- db=FLAGS.redis_db)
45- cls._instance = inst
46+ cls._instance = _StorageDict()
47 return cls._instance
48
49
50+class _StorageDict(dict):
51+ def keys(self, pat=None):
52+ ret = super(_StorageDict, self).keys()
53+ if pat is not None:
54+ ret = fnmatch.filter(ret, pat)
55+ return ret
56+
57+ def delete(self, key):
58+ try:
59+ del self[key]
60+ except KeyError:
61+ pass
62+
63+ def flushdb(self):
64+ self.clear()
65+
66+ def hgetall(self, key):
67+ """Returns the hash for the given key; creates
68+ the hash if the key doesn't exist."""
69+ try:
70+ return self[key]
71+ except KeyError:
72+ self[key] = {}
73+ return self[key]
74+
75+ def hget(self, key, field):
76+ hashdict = self.hgetall(key)
77+ try:
78+ return hashdict[field]
79+ except KeyError:
80+ hashdict[field] = {}
81+ return hashdict[field]
82+
83+ def hset(self, key, field, val):
84+ hashdict = self.hgetall(key)
85+ hashdict[field] = val
86+
87+ def hmset(self, key, value_dict):
88+ hashdict = self.hgetall(key)
89+ for field, val in value_dict.items():
90+ hashdict[field] = val
91+
92+
93 SCOPE_BASE = 0
94 SCOPE_ONELEVEL = 1 # Not implemented
95 SCOPE_SUBTREE = 2
96@@ -169,8 +200,6 @@
97
98
99 class FakeLDAP(object):
100- #TODO(vish): refactor this class to use a wrapper instead of accessing
101- # redis directly
102 """Fake LDAP connection."""
103
104 def simple_bind_s(self, dn, password):
105@@ -183,14 +212,13 @@
106
107 def add_s(self, dn, attr):
108 """Add an object with the specified attributes at dn."""
109- key = "%s%s" % (self.__redis_prefix, dn)
110-
111+ key = "%s%s" % (self.__prefix, dn)
112 value_dict = dict([(k, _to_json(v)) for k, v in attr])
113- Redis.instance().hmset(key, value_dict)
114+ Store.instance().hmset(key, value_dict)
115
116 def delete_s(self, dn):
117 """Remove the ldap object at specified dn."""
118- Redis.instance().delete("%s%s" % (self.__redis_prefix, dn))
119+ Store.instance().delete("%s%s" % (self.__prefix, dn))
120
121 def modify_s(self, dn, attrs):
122 """Modify the object at dn using the attribute list.
123@@ -201,18 +229,18 @@
124 ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
125
126 """
127- redis = Redis.instance()
128- key = "%s%s" % (self.__redis_prefix, dn)
129+ store = Store.instance()
130+ key = "%s%s" % (self.__prefix, dn)
131
132 for cmd, k, v in attrs:
133- values = _from_json(redis.hget(key, k))
134+ values = _from_json(store.hget(key, k))
135 if cmd == MOD_ADD:
136 values.append(v)
137 elif cmd == MOD_REPLACE:
138 values = [v]
139 else:
140 values.remove(v)
141- values = redis.hset(key, k, _to_json(values))
142+ values = store.hset(key, k, _to_json(values))
143
144 def search_s(self, dn, scope, query=None, fields=None):
145 """Search for all matching objects under dn using the query.
146@@ -226,16 +254,17 @@
147 """
148 if scope != SCOPE_BASE and scope != SCOPE_SUBTREE:
149 raise NotImplementedError(str(scope))
150- redis = Redis.instance()
151+ store = Store.instance()
152 if scope == SCOPE_BASE:
153- keys = ["%s%s" % (self.__redis_prefix, dn)]
154+ keys = ["%s%s" % (self.__prefix, dn)]
155 else:
156- keys = redis.keys("%s*%s" % (self.__redis_prefix, dn))
157+ keys = store.keys("%s*%s" % (self.__prefix, dn))
158+
159 objects = []
160 for key in keys:
161- # get the attributes from redis
162- attrs = redis.hgetall(key)
163- # turn the values from redis into lists
164+ # get the attributes from the store
165+ attrs = store.hgetall(key)
166+ # turn the values from the store into lists
167 # pylint: disable-msg=E1103
168 attrs = dict([(k, _from_json(v))
169 for k, v in attrs.iteritems()])
170@@ -244,13 +273,13 @@
171 # filter the attributes by fields
172 attrs = dict([(k, v) for k, v in attrs.iteritems()
173 if not fields or k in fields])
174- objects.append((key[len(self.__redis_prefix):], attrs))
175+ objects.append((key[len(self.__prefix):], attrs))
176 # pylint: enable-msg=E1103
177 if objects == []:
178 raise NO_SUCH_OBJECT()
179 return objects
180
181 @property
182- def __redis_prefix(self): # pylint: disable-msg=R0201
183- """Get the prefix to use for all redis keys."""
184+ def __prefix(self): # pylint: disable-msg=R0201
185+ """Get the prefix to use for all keys."""
186 return 'ldap:'
187
188=== modified file 'nova/tests/auth_unittest.py'
189--- nova/tests/auth_unittest.py 2010-12-10 00:05:13 +0000
190+++ nova/tests/auth_unittest.py 2010-12-17 17:31:34 +0000
191@@ -333,11 +333,10 @@
192 AuthManagerTestCase.__init__(self)
193 test.TestCase.__init__(self, *args, **kwargs)
194 import nova.auth.fakeldap as fakeldap
195- FLAGS.redis_db = 8
196 if FLAGS.flush_db:
197- logging.info("Flushing redis datastore")
198+ logging.info("Flushing datastore")
199 try:
200- r = fakeldap.Redis.instance()
201+ r = fakeldap.Store.instance()
202 r.flushdb()
203 except:
204 self.skip = True