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

View File

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

View File

@ -140,10 +140,16 @@ class Root(controllers.RootController):
def userlist(self, **kw): def userlist(self, **kw):
"""Retrieve a list of all users and display them in one huge list""" """Retrieve a list of all users and display them in one huge list"""
users = None users = None
counter = 0
uid = kw.get('uid') uid = kw.get('uid')
if uid != None and len(uid) > 0: if uid != None and len(uid) > 0:
try: try:
users = client.find_users(uid.encode('utf-8')) 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: except ipaerror.IPAError, e:
turbogears.flash("User list failed: " + str(e)) turbogears.flash("User list failed: " + str(e))
raise turbogears.redirect("/userlist") raise turbogears.redirect("/userlist")

View File

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

View File

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

View File

@ -300,6 +300,44 @@ class IPAdmin(SimpleLDAPObject):
return all_users 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): def addEntry(self,*args):
"""This wraps the add function. It assumes that the entry is already """This wraps the add function. It assumes that the entry is already
populated with all of the desired objectclasses and attributes""" populated with all of the desired objectclasses and attributes"""

View File

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