[PATCH] Adding delegation call grouping to reduce the amount of necessary delegated calls to perform an action

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hey guys,
So I got bored today while waiting for some Satellites to install and came up with this nifty way to group delegation paths together to reduce the amount of XMLRPC calls made over the network while using delegation. If this code works as well as it does on my box, we may come close to meeting the performance ends we were hoping that delegated func calls could provide. Anyways, here it is:

From 7f524383cc7f975545ff51e8719c0bffdbd114e7 Mon Sep 17 00:00:00 2001
From: Steve Salevan <ssalevan@xxxxxxxxxxxxxxxxxxxxxxxx>
Date: Wed, 16 Jul 2008 16:58:45 -0400
Subject: [PATCH] Adding delegation call grouping to reduce the amount of necessary delegated calls to perform an action

---
func/minion/modules/delegation.py |   27 ++++++++++++++------
func/overlord/client.py           |   18 ++++++++++----
func/overlord/delegation_tools.py | 48 ++++++++++++++++++++++++++++++++++++-
3 files changed, 79 insertions(+), 14 deletions(-)

diff --git a/func/minion/modules/delegation.py b/func/minion/modules/delegation.py
index 9f34d1a..38aef2c 100644
--- a/func/minion/modules/delegation.py
+++ b/func/minion/modules/delegation.py
@@ -11,6 +11,7 @@
import func_module
import func.overlord.client as fc
from func import utils
+from func.overlord import delegation_tools as dtools

class DelegationModule(func_module.FuncModule):
@@ -18,18 +19,28 @@ class DelegationModule(func_module.FuncModule):
    api_version = "0.0.1"
description = "Minion-side module to support delegation on sub-Overlords." - def run(self,module,method,args,delegation_path):
+    def run(self,module,method,args,delegation_list):
        """
        Delegates commands down the path of delegation
        supplied as an argument
        """
+        result_dict = {}
- next_hop = delegation_path[0]
-        overlord = fc.Overlord(next_hop)
-        if len(delegation_path) == 1: #minion exists under this overlord
+        #separate list passed to us into minions we can call directly and
+        #further delegation paths
+        (single_paths, grouped_paths) = dtools.group_paths(delegation_list)
+ + #run delegated calls
+        for group in grouped_paths.keys():
+            overlord = fc.Overlord(group)
+            path_list = grouped_paths[group]
+ delegation_results = overlord.delegation.run(module,method,args,path_list) + result_dict.update(delegation_results[group]) #strip away nesting hash + + #run direct calls
+        for minion in single_paths:
+            overlord = fc.Overlord(minion)
            overlord_module = getattr(overlord,module)
-            return getattr(overlord_module,method)(*args[:])
+            result_dict.update(getattr(overlord_module,method)(*args[:]))
- stripped_list = delegation_path[1:len(delegation_path)] - delegation_results = overlord.delegation.run(module,method,args,stripped_list) - return delegation_results[next_hop] #strip away nested hash data from results
+        return result_dict
diff --git a/func/overlord/client.py b/func/overlord/client.py
index 4cb8216..e29e875 100755
--- a/func/overlord/client.py
+++ b/func/overlord/client.py
@@ -259,17 +259,24 @@ class Overlord(object):
            return self.run_direct(module, method, args, nforks)
resulthash = {}
+        grouped_paths = []
#First we get all call paths for minions not directly beneath this overlord dele_paths = dtools.get_paths_for_glob(self.server_spec, self.minionmap)
-        non_single_paths = [path for path in dele_paths if len(path) > 1]
- for path in non_single_paths:
+        #Then we group them together in a dictionary by a common next hop
+        (single_paths,grouped_paths) = dtools.group_paths(dele_paths)
+        #print "single:%s" % single_paths
+        #print "grouped:%s" % grouped_paths
+ + for group in grouped_paths.keys():
+            #print "mygroup:%s" % group
            resulthash.update(self.run_direct(module,
                                              method,
                                              args,
                                              nforks,
-                                              call_path=path))
+ call_path=grouped_paths[group],
+                                              suboverlord=group))
#Next, we run everything that can be run directly beneath this overlord #Why do we do this after delegation calls? Imagine what happens when
@@ -333,6 +340,7 @@ class Overlord(object):

                if self.interactive:
                    print retval
+ except Exception, e:
                (t, v, tb) = sys.exc_info()
                retval = utils.nice_exception(t,v,tb)
@@ -349,10 +357,10 @@ class Overlord(object):
                return (server_name, retval)
if kwargs.has_key('call_path'): #we're delegating if this key exists - spec = kwargs['call_path'][0] #the sub-overlord directly beneath this one
+            delegation_path = kwargs['call_path']
+ spec = kwargs['suboverlord'] #the sub-overlord directly beneath this one
            minionobj = Minions(spec, port=self.port, verbose=self.verbose)
use_delegate = True #signal to process_server to call delegate method - delegation_path = kwargs['call_path'][1:len(kwargs['call_path'])] minionurls = minionobj.get_urls() #the single-item url list to make async #tools such as jobthing/forkbomb happy
        else: #we're directly calling minions, so treat everything normally
diff --git a/func/overlord/delegation_tools.py b/func/overlord/delegation_tools.py
index f3445c0..7d0bc4f 100644
--- a/func/overlord/delegation_tools.py
+++ b/func/overlord/delegation_tools.py
@@ -17,6 +17,49 @@

import fnmatch

+class groupby(object):
+    """
+    Borrowing the groupby iterator class directly
+    from the Python API as it does not exist in Pythons < 2.4
+    """
+ + def __init__(self, iterable, key=None):
+        if key is None:
+            key = lambda x: x
+        self.keyfunc = key
+        self.it = iter(iterable)
+        self.tgtkey = self.currkey = self.currvalue = xrange(0)
+    def __iter__(self):
+        return self
+    def next(self):
+        while self.currkey == self.tgtkey:
+            self.currvalue = self.it.next() # Exit on StopIteration
+            self.currkey = self.keyfunc(self.currvalue)
+        self.tgtkey = self.currkey
+        return (self.currkey, self._grouper(self.tgtkey))
+    def _grouper(self, tgtkey):
+        while self.currkey == tgtkey:
+            yield self.currvalue
+            self.currvalue = self.it.next() # Exit on StopIteration
+            self.currkey = self.keyfunc(self.currvalue)
+
+def group_paths(ungrouped_list):
+    """
+    Given a list of multi-element path lists,
+    groups them together into a list of single-element paths (which
+    exist directly under the current overlord) and a dictionary of paths
+ to send to next hops in the delegation chain, containing a list of lists
+    keyed by their common next hop.
+    """
+ + single_paths = [path[0] for path in ungrouped_list if len(path) == 1]
+    non_single_paths = [path for path in ungrouped_list if len(path) > 1]
+    path_group = dict([(key,[path[1:len(path)] for path in list(gen)])
+                       for key, gen in groupby(non_single_paths,
+                                               key=lambda x:x[0])])
+ + return (single_paths,path_group) + def get_paths_for_glob(glob, minionmap):
    """
    Given a glob, returns shortest path to all minions
@@ -151,5 +194,8 @@ if __name__ == "__main__":
print "Element: %s, best path: %s" % (elem, get_shortest_path(elem,mymap)) print "- And finally, with all duplicates removed:"
-    for elem in get_paths_for_glob('*path*',mymap):
+    for elem in get_paths_for_glob('*',mymap):
        print "Valid Path: %s" % elem
+ + print "- And grouped together:"
+    print group_paths(get_paths_for_glob('*',mymap))
--
1.5.5.1

_______________________________________________
Func-list mailing list
Func-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/func-list

[Index of Archives]     [Fedora Users]     [Linux Networking]     [Fedora Legacy List]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]

  Powered by Linux