commit 8283d6c11afb29398113da59fde3a467762c84c3
Author: Stratos Psomadakis <psomas@grnet.gr>
Date:   Wed Mar 6 16:02:29 2013 +0200

    Improve perf for snf-manage cyclades commands
    
    When the snf-manage {server, network}-list commands are invoked with the
    --displayname option, batch the UUID-to-displayname requests to astakos,
    instead of issuing a request for each (unique) UUID.
    
    Refs #3375

diff --git a/snf-cyclades-app/synnefo/api/management/commands/network-list.py b/snf-cyclades-app/synnefo/api/management/commands/network-list.py
index d8a92ca..9bcf0ed 100644
--- a/snf-cyclades-app/synnefo/api/management/commands/network-list.py
+++ b/snf-cyclades-app/synnefo/api/management/commands/network-list.py
@@ -34,7 +34,7 @@
 from optparse import make_option
 
 from django.core.management.base import BaseCommand, CommandError
-from synnefo.management.common import (format_bool, filter_results, UUIDCache,
+from synnefo.management.common import (format_bool, filter_results, UserCache,
                                        Omit)
 from synnefo.db.models import Network
 from synnefo.management.common import pprint_table
@@ -87,7 +87,7 @@ class Command(BaseCommand):
         if args:
             raise CommandError("Command doesn't accept any arguments")
 
-        ucache = UUIDCache()
+        ucache = UserCache()
 
         if options['deleted']:
             networks = Network.objects.all()
@@ -100,7 +100,7 @@ class Command(BaseCommand):
         user = options['user']
         if user:
             if '@' in user:
-                user = ucache.get_user(user)
+                user = ucache.get_uuid(user)
             networks = networks.filter(userid=user)
 
         filter_by = options['filter_by']
@@ -128,11 +128,14 @@ class Command(BaseCommand):
         else:
             headers.extend(['IPv4 Subnet', 'IPv4 Gateway'])
 
+        uuids = list(set([network.userid for network in networks]))
+        ucache.fetch_names(uuids)
+
         table = []
         for network in networks.order_by("id"):
             uuid = network.userid
             if displayname:
-                dname = ucache.get_user(uuid)
+                dname = ucache.get_name(uuid)
 
             fields = filter(lambda x: x is not Omit,
                             [str(network.id),
diff --git a/snf-cyclades-app/synnefo/api/management/commands/server-inspect.py b/snf-cyclades-app/synnefo/api/management/commands/server-inspect.py
index 875f166..a3770ab 100644
--- a/snf-cyclades-app/synnefo/api/management/commands/server-inspect.py
+++ b/snf-cyclades-app/synnefo/api/management/commands/server-inspect.py
@@ -77,7 +77,7 @@ class Command(BaseCommand):
 
         displayname = options['displayname']
 
-        ucache = common.UUIDCache()
+        ucache = common.UserCache()
 
         try:
             image = common.get_image(vm.imageid, vm.userid)['name']
@@ -94,7 +94,7 @@ class Command(BaseCommand):
 
         uuid = vm.userid
         if displayname:
-            dname = ucache.get_user(uuid)
+            dname = ucache.get_name(uuid)
 
         fields = filter(lambda x: x is not Omit,
                         [vm.name, uuid, dname if displayname else Omit,
diff --git a/snf-cyclades-app/synnefo/api/management/commands/server-list.py b/snf-cyclades-app/synnefo/api/management/commands/server-list.py
index ee371ae..e2ddba2 100644
--- a/snf-cyclades-app/synnefo/api/management/commands/server-list.py
+++ b/snf-cyclades-app/synnefo/api/management/commands/server-list.py
@@ -35,7 +35,7 @@ from optparse import make_option
 
 from django.core.management.base import BaseCommand, CommandError
 from synnefo.management.common import (format_vm_state, get_backend, Omit,
-                                       filter_results, pprint_table, UUIDCache)
+                                       filter_results, pprint_table, UserCache)
 from synnefo.api.util import get_image
 from synnefo.db.models import VirtualMachine
 
@@ -92,7 +92,7 @@ class Command(BaseCommand):
         if args:
             raise CommandError("Command doesn't accept any arguments")
 
-        ucache = UUIDCache()
+        ucache = UserCache()
 
         if options['backend_id']:
             backend = get_backend(options['backend_id'])
@@ -114,7 +114,7 @@ class Command(BaseCommand):
         user = options['user']
         if user:
             if '@' in user:
-                user = ucache.get_user(user)
+                user = ucache.get_uuid(user)
             servers = servers.filter(userid=user)
 
         filter_by = options['filter_by']
@@ -136,6 +136,9 @@ class Command(BaseCommand):
                          'backend',
                           ])
 
+        uuids = list(set([server.userid for server in servers]))
+        ucache.fetch_names(uuids)
+
         table = []
         for server in servers.order_by('id'):
             try:
@@ -151,7 +154,7 @@ class Command(BaseCommand):
 
             uuid = server.userid
             if displayname:
-                dname = ucache.get_user(server.userid)
+                dname = ucache.get_name(server.userid)
 
             fields = filter(lambda x: x is not Omit,
                             [str(server.id),
diff --git a/snf-cyclades-app/synnefo/api/management/commands/server-show.py b/snf-cyclades-app/synnefo/api/management/commands/server-show.py
index 905a0a5..e56c79a 100644
--- a/snf-cyclades-app/synnefo/api/management/commands/server-show.py
+++ b/snf-cyclades-app/synnefo/api/management/commands/server-show.py
@@ -34,7 +34,7 @@
 from django.core.management.base import BaseCommand, CommandError
 from synnefo.management.common import (format_bool, format_date,
                                        format_vm_state, get_vm,
-                                       get_image, UUIDCache)
+                                       get_image, UserCache)
 
 
 class Command(BaseCommand):
@@ -61,7 +61,7 @@ class Command(BaseCommand):
             'id': server.id,
             'name': server.name,
             'owner_uuid': userid,
-            'owner_name': UUIDCache().get_user(userid),
+            'owner_name': UserCache().get_name(userid),
             'created': format_date(server.created),
             'updated': format_date(server.updated),
             'image': image,
@@ -69,7 +69,7 @@ class Command(BaseCommand):
             'flavor': flavor,
             'deleted': format_bool(server.deleted),
             'suspended': format_bool(server.suspended),
-            'state': format_vm_state(server)
+            'state': format_vm_state(server),
         }
 
         for key, val in sorted(kv.items()):
diff --git a/snf-cyclades-app/synnefo/logic/management/commands/network-inspect.py b/snf-cyclades-app/synnefo/logic/management/commands/network-inspect.py
index 99562fa..3e1e0cb 100644
--- a/snf-cyclades-app/synnefo/logic/management/commands/network-inspect.py
+++ b/snf-cyclades-app/synnefo/logic/management/commands/network-inspect.py
@@ -36,7 +36,7 @@ import json
 from optparse import make_option
 
 from django.core.management.base import BaseCommand, CommandError
-from synnefo.management.common import get_network, UUIDCache, Omit
+from synnefo.management.common import get_network, UserCache, Omit
 
 from synnefo.db.models import (Backend, BackendNetwork,
                                pooled_rapi_client)
@@ -62,7 +62,7 @@ class Command(BaseCommand):
 
         net = get_network(args[0])
 
-        ucache = UUIDCache()
+        ucache = UserCache()
 
         displayname = options['displayname']
 
@@ -75,7 +75,7 @@ class Command(BaseCommand):
 
         uuid = net.userid
         if displayname:
-            dname = ucache.get_user(uuid)
+            dname = ucache.get_name(uuid)
 
         fields = filter(lambda x: x is not Omit,
                         [net.name, net.backend_id, net.state, uuid or '-',
diff --git a/snf-cyclades-app/synnefo/management/common.py b/snf-cyclades-app/synnefo/management/common.py
index adb07dc..4c0475e 100644
--- a/snf-cyclades-app/synnefo/management/common.py
+++ b/snf-cyclades-app/synnefo/management/common.py
@@ -248,23 +248,56 @@ def pprint_table(out, table, headers=None, separator=None):
                                for val, width in zip(row, widths)))
 
 
-class UUIDCache(object):
-    """UUID-to-email cache"""
+class UserCache(object):
+    """uuid<->displayname user 'cache'"""
 
     user_catalogs_url = ASTAKOS_URL.replace("im/authenticate",
                                             "service/api/user_catalogs")
 
-    def __init__(self):
+    def __init__(self, split=100):
         self.users = {}
 
-    def get_user(self, uuid):
+        self.split = split
+        assert(self.split > 0), "split must be positive"
+
+    def fetch_names(self, uuid_list):
+        l = len(uuid_list)
+
+        start = 0
+        while start < l:
+            end = self.split if l > self.split else l
+            try:
+                names = \
+                    astakos.get_displaynames(token=ASTAKOS_TOKEN,
+                                             url=UserCache.user_catalogs_url,
+                                             uuids=uuid_list[start:end])
+                self.users.update(names)
+            except Exception as e:
+                log.error("Failed to fetch names: %s",  e)
+
+            start = end
+
+    def get_uuid(self, name):
+        if not name in self.users:
+            try:
+                self.users[name] = \
+                    astakos.get_user_uuid(token=ASTAKOS_TOKEN,
+                                          url=UserCache.user_catalogs_url,
+                                          displayname=name)
+            except Exception as e:
+                log.error("Can not get uuid for name %s: %s", name, e)
+                self.users[name] = name
+
+        return self.users[name]
+
+    def get_name(self, uuid):
         """Do the uuid-to-email resolving"""
 
         if not uuid in self.users:
             try:
                 self.users[uuid] = \
                     astakos.get_displayname(token=ASTAKOS_TOKEN,
-                                            url=UUIDCache.user_catalogs_url,
+                                            url=UserCache.user_catalogs_url,
                                             uuid=uuid)
             except Exception as e:
                 log.error("Can not get display name for uuid %s: %s", uuid, e)
