Make fakeldap use redis
This commit is contained in:
@@ -21,37 +21,31 @@
|
||||
Fake LDAP server for test harnesses.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import json
|
||||
|
||||
from nova import datastore
|
||||
|
||||
SCOPE_SUBTREE = 1
|
||||
SCOPE_SUBTREE = 2
|
||||
MOD_ADD = 0
|
||||
MOD_DELETE = 1
|
||||
|
||||
SUBS = {
|
||||
'groupOfNames': ['novaProject']
|
||||
}
|
||||
|
||||
|
||||
class NO_SUCH_OBJECT(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def initialize(uri):
|
||||
return FakeLDAP(uri)
|
||||
return FakeLDAP()
|
||||
|
||||
|
||||
class FakeLDAP(object):
|
||||
def __init__(self, _uri):
|
||||
self.keeper = datastore.Keeper('fakeldap')
|
||||
if self.keeper['objects'] is None:
|
||||
self.keeper['objects'] = {}
|
||||
|
||||
def simple_bind_s(self, dn, password):
|
||||
"""This method is ignored, but provided for compatibility"""
|
||||
pass
|
||||
|
||||
def unbind_s(self):
|
||||
"""This method is ignored, but provided for compatibility"""
|
||||
pass
|
||||
|
||||
def _paren_groups(self, source):
|
||||
@@ -67,6 +61,7 @@ class FakeLDAP(object):
|
||||
count -= 1
|
||||
if count == 0:
|
||||
result.append(source[start:pos+1])
|
||||
return result
|
||||
|
||||
def _match_query(self, query, attrs):
|
||||
inner = query[1:-1]
|
||||
@@ -83,63 +78,84 @@ class FakeLDAP(object):
|
||||
return self._match(k, v, attrs)
|
||||
|
||||
def _subs(self, v):
|
||||
if v in SUBS:
|
||||
return [v] + SUBS[v]
|
||||
subs = {
|
||||
'groupOfNames': ['novaProject']
|
||||
}
|
||||
if v in subs:
|
||||
return [v] + subs[v]
|
||||
return [v]
|
||||
|
||||
def _match(self, k, v, attrs):
|
||||
if attrs.has_key(k):
|
||||
for v in self._subs(v):
|
||||
if (v in attrs[k]):
|
||||
if v in attrs[k]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def search_s(self, dn, scope, query=None, fields=None):
|
||||
#logging.debug("searching for %s" % dn)
|
||||
filtered = {}
|
||||
d = self.keeper['objects'] or {}
|
||||
for cn, attrs in d.iteritems():
|
||||
if cn[-len(dn):] == dn:
|
||||
filtered[cn] = attrs
|
||||
objects = filtered
|
||||
if query:
|
||||
objects = {}
|
||||
for cn, attrs in filtered.iteritems():
|
||||
if self._match_query(query, attrs):
|
||||
objects[cn] = attrs
|
||||
if objects == {}:
|
||||
"""search for all matching objects under dn using the query
|
||||
only SCOPE_SUBTREE is supported.
|
||||
"""
|
||||
if scope != SCOPE_SUBTREE:
|
||||
raise NotImplementedError(str(scope))
|
||||
redis = datastore.Redis.instance()
|
||||
keys = redis.keys(self._redis_prefix + '*' + dn)
|
||||
objects = []
|
||||
for key in keys:
|
||||
# get the attributes from redis
|
||||
attrs = redis.hgetall(key)
|
||||
# turn the values from redis into lists
|
||||
attrs = dict([(k, self._from_json(v))
|
||||
for k, v in attrs.iteritems()])
|
||||
# filter the objects by query
|
||||
if not query or self._match_query(query, attrs):
|
||||
# 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))
|
||||
if objects == []:
|
||||
raise NO_SUCH_OBJECT()
|
||||
return objects.items()
|
||||
return objects
|
||||
|
||||
def add_s(self, cn, attr):
|
||||
#logging.debug("adding %s" % cn)
|
||||
stored = {}
|
||||
for k, v in attr:
|
||||
if type(v) is list:
|
||||
stored[k] = v
|
||||
else:
|
||||
stored[k] = [v]
|
||||
d = self.keeper['objects']
|
||||
d[cn] = stored
|
||||
self.keeper['objects'] = d
|
||||
@property
|
||||
def _redis_prefix(self):
|
||||
return 'ldap:'
|
||||
|
||||
def delete_s(self, cn):
|
||||
logging.debug("deleting %s" % cn)
|
||||
d = self.keeper['objects']
|
||||
del d[cn]
|
||||
self.keeper['objects'] = d
|
||||
def _from_json(self, encoded):
|
||||
"""Convert attribute values from json representation."""
|
||||
# return as simple strings instead of unicode strings
|
||||
return [str(x) for x in json.loads(encoded)]
|
||||
|
||||
def modify_s(self, cn, attr):
|
||||
logging.debug("modifying %s" % cn)
|
||||
d = self.keeper['objects']
|
||||
for cmd, k, v in attr:
|
||||
logging.debug("command %s" % cmd)
|
||||
def _to_json(self, unencoded):
|
||||
"""Convert attribute values into json representation."""
|
||||
# all values are returned as lists from ldap
|
||||
return json.dumps(list(unencoded))
|
||||
|
||||
def add_s(self, dn, attr):
|
||||
"""Add an object with the specified attributes at dn."""
|
||||
key = self._redis_prefix + dn
|
||||
|
||||
value_dict = dict([(k, self._to_json(v)) for k, v in attr])
|
||||
datastore.Redis.instance().hmset(key, value_dict)
|
||||
|
||||
def delete_s(self, dn):
|
||||
"""Remove the ldap object at specified dn."""
|
||||
datastore.Redis.instance().delete(self._redis_prefix + dn)
|
||||
|
||||
def modify_s(self, dn, attrs):
|
||||
"""Modify the object at dn using the attribute list.
|
||||
attr is a list of tuples in the following form:
|
||||
([MOD_ADD | MOD_DELETE], attribute, value)
|
||||
"""
|
||||
redis = datastore.Redis.instance()
|
||||
key = self._redis_prefix + dn
|
||||
|
||||
for cmd, k, v in attrs:
|
||||
values = self._from_json(redis.hget(key, k))
|
||||
if cmd == MOD_ADD:
|
||||
d[cn][k].append(v)
|
||||
values.append(v)
|
||||
else:
|
||||
d[cn][k].remove(v)
|
||||
self.keeper['objects'] = d
|
||||
|
||||
|
||||
|
||||
values.remove(v)
|
||||
values = redis.hset(key, k, self._to_json(values))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user