Change user search to be asynchronous.

This way it returns results even if the search times out.
The find_users() search now returns a counter as the first result, which
is set to -1 if the results are partial.
This commit is contained in:
Kevin McCarthy 2007-08-28 16:01:07 -07:00
parent ef2dc5cefa
commit e9bd8dee3b
7 changed files with 74 additions and 17 deletions

View File

@ -94,12 +94,14 @@ class IPAClient:
return result
def find_users(self, criteria, sattrs=None):
"""Find users whose uid matches the criteria. Wildcards are
acceptable. Returns a list of User objects."""
"""Return a list: counter followed by a User object for each user that
matches the criteria. If the results are truncated, counter will
be set to -1"""
result = self.transport.find_users(criteria, sattrs)
counter = result[0]
users = []
for attrs in result:
users = [counter]
for attrs in result[1:]:
if attrs is not None:
users.append(user.User(attrs))

View File

@ -151,8 +151,9 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
def find_users (self, criteria, sattrs=None):
"""Return a list containing a User object for each user that matches
the criteria."""
"""Return a list: counter followed by a User object for each user that
matches the criteria. If the results are truncated, counter will
be set to -1"""
server = self.setup_server()
try:

View File

@ -140,10 +140,16 @@ class Root(controllers.RootController):
def userlist(self, **kw):
"""Retrieve a list of all users and display them in one huge list"""
users = None
counter = 0
uid = kw.get('uid')
if uid != None and len(uid) > 0:
try:
users = client.find_users(uid.encode('utf-8'))
counter = users[0]
users = users[1:]
if counter == -1:
turbogears.flash("These results are truncated.\n" +
"Please refine your search and try again.")
except ipaerror.IPAError, e:
turbogears.flash("User list failed: " + str(e))
raise turbogears.redirect("/userlist")

View File

@ -129,7 +129,7 @@ body {
#status_block {
margin: 0 auto 0.5em auto;
padding: 15px 10px 15px 55px;
background: #cec URL('../images/ok.png') left center no-repeat;
background: #cec;
border: 1px solid #9c9;
width: 450px;
font-size: 120%;

View File

@ -8,13 +8,13 @@
<body>
<div id="search">
<form action="${tg.url('/userlist')}" method="post">
Search by login/name:
Search:
<input type="text" name="uid" />
<input type="submit" />
</form>
</div>
<div py:if='users != None'>
<h2>Results</h2>
<h2>${len(users)} results returned:</h2>
<table py:if='len(users) > 0' border="1">
<tr>
<th>

View File

@ -300,6 +300,44 @@ class IPAdmin(SimpleLDAPObject):
return all_users
def getListAsync(self,*args):
"""This version performs an asynchronous search, to allow
results even if we hit a limit.
It returns a list: counter followed by the results.
If the results are truncated, counter will be set to -1.
"""
sctrl = self.__get_server_controls__()
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
entries = []
partial = 0
try:
msgid = self.search_ext(*args)
type, result_list = self.result(msgid, 0)
while result_list:
for result in result_list:
entries.append(result)
type, result_list = self.result(msgid, 0)
except (ldap.ADMINLIMIT_EXCEEDED, ldap.SIZELIMIT_EXCEEDED), e:
partial = 1
except ldap.LDAPError, e:
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e)
if not entries:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND,
"no such entry for " + str(args))
if partial == 1:
counter = -1
else:
counter = len(entries)
return [counter] + entries
def addEntry(self,*args):
"""This wraps the add function. It assumes that the entry is already
populated with all of the desired objectclasses and attributes"""

View File

@ -372,9 +372,8 @@ class IPAServer:
return users
def find_users (self, criteria, sattrs=None, opts=None):
"""Return a list containing a User object for each
existing user that matches the criteria.
"""
"""Returns a list: counter followed by the results.
If the results are truncated, counter will be set to -1."""
global _LDAPPool
if opts:
@ -400,25 +399,36 @@ class IPAServer:
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
try:
try:
exact_results = m1.getList(self.basedn, self.scope,
exact_results = m1.getListAsync(self.basedn, self.scope,
exact_match_filter, sattrs)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
exact_results = []
exact_results = [0]
try:
partial_results = m1.getList(self.basedn, self.scope,
partial_results = m1.getListAsync(self.basedn, self.scope,
partial_match_filter, sattrs)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
partial_results = []
partial_results = [0]
finally:
_LDAPPool.releaseConn(m1)
exact_counter = exact_results[0]
partial_counter = partial_results[0]
exact_results = exact_results[1:]
partial_results = partial_results[1:]
# Remove exact matches from the partial_match list
exact_dns = set(map(lambda e: e.dn, exact_results))
partial_results = filter(lambda e: e.dn not in exact_dns,
partial_results)
users = []
if (exact_counter == -1) or (partial_counter == -1):
counter = -1
else:
counter = len(exact_results) + len(partial_results)
users = [counter]
for u in exact_results + partial_results:
users.append(self.convert_entry(u))