[Freeipa-devel] [PATCH] add asynchronous user search

Kevin McCarthy kmccarth at redhat.com
Tue Aug 28 23:03:10 UTC 2007


This patch adds asynchronous searching: ipaldap.getListAsync().  This
method returns a counter as a first entry which is -1 in the case of
partial results.

The change allows user search to show results even if it's truncated.

-Kevin

-------------- next part --------------
# HG changeset patch
# User Kevin McCarthy <kmccarth at redhat.com>
# Date 1188342067 25200
# Node ID 1748d1e697b26272617d2058ecdf7582608726ed
# Parent  2b57d537aacaa15a5be1d75756b276d7ee874dc8
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.

diff -r 2b57d537aaca -r 1748d1e697b2 ipa-python/ipaclient.py
--- a/ipa-python/ipaclient.py	Tue Aug 28 09:20:12 2007 -0700
+++ b/ipa-python/ipaclient.py	Tue Aug 28 16:01:07 2007 -0700
@@ -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)
-
-        users = []
-        for attrs in result:
+        counter = result[0]
+
+        users = [counter]
+        for attrs in result[1:]:
             if attrs is not None:
                 users.append(user.User(attrs))
 
diff -r 2b57d537aaca -r 1748d1e697b2 ipa-python/rpcclient.py
--- a/ipa-python/rpcclient.py	Tue Aug 28 09:20:12 2007 -0700
+++ b/ipa-python/rpcclient.py	Tue Aug 28 16:01:07 2007 -0700
@@ -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 -r 2b57d537aaca -r 1748d1e697b2 ipa-server/ipa-gui/ipagui/controllers.py
--- a/ipa-server/ipa-gui/ipagui/controllers.py	Tue Aug 28 09:20:12 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/controllers.py	Tue Aug 28 16:01:07 2007 -0700
@@ -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 -r 2b57d537aaca -r 1748d1e697b2 ipa-server/ipa-gui/ipagui/static/css/style.css
--- a/ipa-server/ipa-gui/ipagui/static/css/style.css	Tue Aug 28 09:20:12 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/static/css/style.css	Tue Aug 28 16:01:07 2007 -0700
@@ -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 -r 2b57d537aaca -r 1748d1e697b2 ipa-server/ipa-gui/ipagui/templates/userlist.kid
--- a/ipa-server/ipa-gui/ipagui/templates/userlist.kid	Tue Aug 28 09:20:12 2007 -0700
+++ b/ipa-server/ipa-gui/ipagui/templates/userlist.kid	Tue Aug 28 16:01:07 2007 -0700
@@ -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>
diff -r 2b57d537aaca -r 1748d1e697b2 ipa-server/ipaserver/ipaldap.py
--- a/ipa-server/ipaserver/ipaldap.py	Tue Aug 28 09:20:12 2007 -0700
+++ b/ipa-server/ipaserver/ipaldap.py	Tue Aug 28 16:01:07 2007 -0700
@@ -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 -r 2b57d537aaca -r 1748d1e697b2 ipa-server/xmlrpc-server/funcs.py
--- a/ipa-server/xmlrpc-server/funcs.py	Tue Aug 28 09:20:12 2007 -0700
+++ b/ipa-server/xmlrpc-server/funcs.py	Tue Aug 28 16:01:07 2007 -0700
@@ -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 = []
-        finally:
-            _LDAPPool.releaseConn(m1)
+                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))
 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 2228 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/freeipa-devel/attachments/20070828/b07be349/attachment.bin>


More information about the Freeipa-devel mailing list