From e9bd8dee3b0350c08d16d57bfd008a72cb283c48 Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Tue, 28 Aug 2007 16:01:07 -0700 Subject: 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. --- ipa-python/ipaclient.py | 10 ++++--- ipa-python/rpcclient.py | 5 ++-- ipa-server/ipa-gui/ipagui/controllers.py | 6 ++++ ipa-server/ipa-gui/ipagui/static/css/style.css | 2 +- ipa-server/ipa-gui/ipagui/templates/userlist.kid | 4 +-- ipa-server/ipaserver/ipaldap.py | 38 ++++++++++++++++++++++++ ipa-server/xmlrpc-server/funcs.py | 26 +++++++++++----- 7 files changed, 74 insertions(+), 17 deletions(-) diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py index 9fd51db3..fcfb29f1 100644 --- a/ipa-python/ipaclient.py +++ b/ipa-python/ipaclient.py @@ -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)) diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index 7d41caee..e0d6e2ee 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -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: diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py index ca2d15a2..ebd0b20d 100644 --- a/ipa-server/ipa-gui/ipagui/controllers.py +++ b/ipa-server/ipa-gui/ipagui/controllers.py @@ -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") diff --git a/ipa-server/ipa-gui/ipagui/static/css/style.css b/ipa-server/ipa-gui/ipagui/static/css/style.css index b5bd8280..152175e1 100644 --- a/ipa-server/ipa-gui/ipagui/static/css/style.css +++ b/ipa-server/ipa-gui/ipagui/static/css/style.css @@ -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%; diff --git a/ipa-server/ipa-gui/ipagui/templates/userlist.kid b/ipa-server/ipa-gui/ipagui/templates/userlist.kid index 1b4a1201..c3b53272 100644 --- a/ipa-server/ipa-gui/ipagui/templates/userlist.kid +++ b/ipa-server/ipa-gui/ipagui/templates/userlist.kid @@ -8,13 +8,13 @@
-

Results

+

${len(users)} results returned:

diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index f626f2f3..4d0630ee 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -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""" diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index a0af41d4..fd95470b 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -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)) -- cgit