summaryrefslogtreecommitdiffstats
path: root/func
diff options
context:
space:
mode:
authorSteve Salevan <ssalevan@marillion.rdu.redhat.com>2008-07-08 12:38:20 -0400
committerSteve Salevan <ssalevan@marillion.rdu.redhat.com>2008-07-08 12:38:20 -0400
commit8770e4dffb3bf4693e01ec761217956fc9f8d355 (patch)
tree1d67b95c4d48c7ad85dde5819cd2b57627668aab /func
parent498ee363f87317ec3649e3ab704933e93e8d4216 (diff)
downloadfunc-8770e4dffb3bf4693e01ec761217956fc9f8d355.tar.gz
func-8770e4dffb3bf4693e01ec761217956fc9f8d355.tar.xz
func-8770e4dffb3bf4693e01ec761217956fc9f8d355.zip
Adding in tree manipulation tools and minion-side delegation module.
Diffstat (limited to 'func')
-rw-r--r--func/minion/modules/delegation.py35
-rwxr-xr-xfunc/overlord/client.py31
-rw-r--r--func/overlord/delegation_tools.py155
3 files changed, 217 insertions, 4 deletions
diff --git a/func/minion/modules/delegation.py b/func/minion/modules/delegation.py
new file mode 100644
index 0000000..7af3284
--- /dev/null
+++ b/func/minion/modules/delegation.py
@@ -0,0 +1,35 @@
+# Copyright 2008, Red Hat, Inc
+# Steve Salevan <ssalevan@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import func_module
+import func.overlord.client as fc
+from func import utils
+
+class DelegationModule(func_module.FuncModule):
+
+ version = "0.0.1"
+ api_version = "0.0.1"
+ description = "Minion-side module to support delegation on sub-Overlords."
+
+ def run(self,module,method,args,delegation_path):
+ """
+ Delegates commands down the path of delegation
+ supplied as an argument
+ """
+
+ next_hop = delegation_path[0]
+ overlord = fc.Overlord(next_hop)
+ if len(delegation_path) == 1: #minion exists under this overlord
+ meth = "%s.%s" % (module, method)
+ return getattr(overlord,meth)(*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
diff --git a/func/overlord/client.py b/func/overlord/client.py
index d3e7546..74b3ee4 100755
--- a/func/overlord/client.py
+++ b/func/overlord/client.py
@@ -25,6 +25,7 @@ import sslclient
import command
import groups
+import delegation_tools as dtools
import func.forkbomb as forkbomb
import func.jobthing as jobthing
import func.utils as utils
@@ -89,13 +90,16 @@ class CommandAutomagic(object):
class Minions(object):
def __init__(self, spec, port=51234,
noglobs=None, verbose=None,
- just_fqdns=False, groups_file=None):
+ just_fqdns=False, groups_file=None,
+ delegate=False, minionmap={}):
self.spec = spec
self.port = port
self.noglobs = noglobs
self.verbose = verbose
self.just_fqdns = just_fqdns
+ self.delegate = delegate
+ self.minionmap = minionmap
self.config = read_config(CONFIG_FILE, CMConfig)
self.group_class = groups.Groups(filename=groups_file)
@@ -160,7 +164,7 @@ class Overlord(object):
def __init__(self, server_spec, port=DEFAULT_PORT, interactive=False,
verbose=False, noglobs=False, nforks=1, config=None, async=False, init_ssl=True,
- delegate=False, mapfile=""):
+ delegate=True, mapfile="/var/lib/func/inventory/map"):
"""
Constructor.
@server_spec -- something like "*.example.org" or "foosball"
@@ -257,7 +261,26 @@ class Overlord(object):
# -----------------------------------------------
- def run(self, module, method, args, nforks=1):
+ def run(self, module, method, args, nforks=1, *extraargs, **kwargs):
+ """
+ Invoke a remote method on one or more servers.
+ Run returns a hash, the keys are server names, the values are the
+ returns.
+
+ The returns may include exception objects.
+ If Overlord() was constructed with noglobs=True, the return is instead
+ just a single value, not a hash.
+ """
+
+ #if not self.delegate: #delegation is turned off
+ # return self.run_nodelegate(module, method, args, nforks)
+ #print self.minionmap
+ return self.run_nodelegate(module,method,args,nforks)
+
+
+ # -----------------------------------------------
+
+ def run_nodelegate(self, module, method, args, nforks=1):
"""
Invoke a remote method on one or more servers.
Run returns a hash, the keys are server names, the values are the
@@ -313,7 +336,7 @@ class Overlord(object):
right = server.rfind(":")
server_name = server[left:right]
return (server_name, retval)
-
+
if not self.noglobs:
if self.nforks > 1 or self.async:
# using forkbomb module to distribute job over multiple threads
diff --git a/func/overlord/delegation_tools.py b/func/overlord/delegation_tools.py
new file mode 100644
index 0000000..4907c9d
--- /dev/null
+++ b/func/overlord/delegation_tools.py
@@ -0,0 +1,155 @@
+##
+## func delegation tools
+## These are some helper methods to make dealing with delegation
+## dictionary trees a little more sane when dealing with delegation
+## and related functions.
+##
+## Copyright 2008, Red Hat, Inc.
+## Steve Salevan <ssalevan@redhat.com>
+##
+## This software may be freely redistributed under the terms of the GNU
+## general public license.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##
+
+import fnmatch
+
+def get_paths_for_glob(glob, minionmap):
+ """
+ Given a glob, returns shortest path to all minions
+ matching it in the delegation dictionary tree
+ """
+
+ pathlist = []
+ for elem in match_glob_in_tree(glob,minionmap):
+ result = get_shortest_path(elem,minionmap)
+ if result not in pathlist: #prevents duplicates
+ pathlist.append(result)
+ return pathlist
+
+def flatten_list(bumpy_list):
+ """
+ Flattens gnarly nested lists into much
+ nicer, flat lists
+ """
+
+ flat_list = []
+ for item in bumpy_list:
+ if isinstance(item, list):
+ for elem in flatten_list(item):
+ flat_list.append(elem)
+ else:
+ flat_list.append(item)
+ return flat_list
+
+def match_glob_on_toplevel(pattern, minionmap):
+ """
+ Searches through the top level of a dictionary
+ for all keys (minion FQDNs) matching the given
+ glob, returns matches
+ """
+
+ matched = []
+ for k,v in minionmap.iteritems():
+ if fnmatch.fnmatch(k,pattern):
+ matched.append(k)
+ return matched
+
+def match_glob_in_tree(pattern, minionmap):
+ """
+ Searches through given tree dictionary for all
+ keys (minion FQDNs) matching the given glob,
+ returns matches
+ """
+
+ matched = []
+ for k,v in minionmap.iteritems():
+ for result in match_glob_in_tree(pattern, v):
+ matched.append(result)
+ if fnmatch.fnmatch(k,pattern):
+ matched.append(k)
+ return matched
+
+def minion_exists_under_node(minion, minionmap):
+ """
+ A little wrapper around the match_glob_on_toplevel
+ method that you can use if you want to get a boolean
+ result denoting minion existence under your current
+ node
+ """
+
+ return len(match_glob_on_toplevel(minion,minionmap)) > 0
+
+def get_shortest_path(minion, minionmap):
+ """
+ Given a minion that exists in the given tree,
+ this method returns all paths from the top
+ node to the minion in the form of a flat list
+ """
+
+ def lensort(a,b):
+ if len(a) > len(b):
+ return 1
+ return -1
+
+ results = get_all_paths(minion,minionmap)
+ results.sort(lensort)
+ return results[0]
+
+def get_all_paths(minion, minionmap):
+ """
+ Given a minion that exists in the given tree,
+ this method returns all paths that exist from the top
+ node to the minion in the delegation dictionary tree
+ """
+
+ #This is an ugly kludge of franken-code. If someone with
+ #more knowledge of graph theory than myself can improve this
+ #module, please, please do so. - ssalevan 7/2/08
+ seq_list = []
+
+ if minion_exists_under_node(minion, minionmap):
+ return [[minion]] #minion found, terminate branch
+
+ if minionmap == {}:
+ return [[]] #no minion found, terminate branch
+
+ for k,v in minionmap.iteritems():
+ branch_list = []
+ branch_list.append(k)
+
+ for branchlet in get_all_paths(minion, v):
+ branch_list.append(branchlet)
+
+ single_branch = flatten_list(branch_list)
+ if minion in single_branch:
+ seq_list.append(single_branch)
+
+ return seq_list
+
+if __name__ == "__main__":
+ mymap = {'anthony':{'longpath1':{'longpath2':{'longpath3':{}}}},
+ 'phil':{'steve':{'longpath3':{}}},
+ 'tony':{'mike':{'anthony':{}}},
+ 'just_a_minion':{}
+ }
+
+ print "- Testing an element that exists in multiple lists of varying length:"
+ for elem in match_glob_in_tree('*path3',mymap):
+ print "Element: %s, all paths: %s" % (elem, get_all_paths(elem,mymap))
+ print "best path: %s" % get_shortest_path(elem, mymap)
+
+ print "- Testing an element that is simply a minion and has no sub-nodes:"
+ for elem in match_glob_in_tree('*minion',mymap):
+ print "Element: %s, best path: %s" % (elem, get_shortest_path(elem,mymap))
+
+ print "- OK, now the whole thing:"
+ for elem in match_glob_in_tree('*',mymap):
+ 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('*',mymap):
+ print "Valid Path: %s" % elem