[PATCH] Giant patch to enable basic delegation functionality

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

 



This is a diff from makkalot's master func branch on github, and, unfortunately, this is a giant huge thing, so I apologize in advance to the maintainers.

At the moment, this adds in a requirement for PyYAML into the RPM spec; when we replace it with mpdehaan's new YAML parser, we can chuck that out, as PyYAML doesn't exist in any EL repos.

Without further ado, here we goooooooo:

From d31688d92dd00f4d1b62e07afa69217ee7e413b6 Mon Sep 17 00:00:00 2001
From: Steve Salevan <ssalevan@xxxxxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 27 Jun 2008 11:38:00 -0400
Subject: [PATCH] Fixing certmaster to work with branched certmaster project (you can no
longer import certmaster via func) and added function to glean currently
registered minions.

Adding in new 'overlord.py' module to build maps of func networks where
sub-overlords control minions.  It's not quite ready for prime time yet,
but hopefully within the next week or two it should be working reliably
enough to help power command delegation features and the like.

Fixing mapping function to produce correct map

Renaming minion module certmaster to prevent gnarl namespace issues.
Fixing overlord module to prevent calls to test.ping() to reduce XMLRPC calls.

Adding in a command-line map building tool,
adjusting spec and setup.py to account for new PyYAML
dependency and new script

Adding in some basic map append functionality to mapper tool

Adding in arguments for calling func with delegation

Adding in tree manipulation tools and minion-side delegation module.

Adding potentially-working delegation code to overlord client

Committing fixes to delegation/overlord client code

Removing superfluous print statements

Adding in some code to make CLI access available for delegation
---
func.spec                            |    5 +
func/minion/modules/certmaster.py    |   61 -------------
func/minion/modules/certmastermod.py |   69 +++++++++++++++
func/minion/modules/delegation.py    |   35 ++++++++
func/minion/modules/overlord.py      |   41 +++++++++
func/overlord/base_command.py        |    8 ++-
func/overlord/client.py              |  103 ++++++++++++++++++++---
func/overlord/cmd_modules/call.py    |    6 +-
func/overlord/delegation_tools.py | 155 ++++++++++++++++++++++++++++++++++
func/overlord/mapper.py              |   91 ++++++++++++++++++++
scripts/func-build-map               |    8 ++
setup.py                             |    3 +-
12 files changed, 508 insertions(+), 77 deletions(-)
delete mode 100644 func/minion/modules/certmaster.py
create mode 100644 func/minion/modules/certmastermod.py
create mode 100644 func/minion/modules/delegation.py
create mode 100644 func/minion/modules/overlord.py
create mode 100644 func/overlord/delegation_tools.py
create mode 100755 func/overlord/mapper.py
create mode 100755 scripts/func-build-map

diff --git a/func.spec b/func.spec
index e8f6d23..da74d88 100644
--- a/func.spec
+++ b/func.spec
@@ -13,6 +13,7 @@ License: GPLv2+
Group: Applications/System
Requires: python >= 2.3
Requires: pyOpenSSL
+Requires: pyYaml
Requires: certmaster >= 0.1
BuildRequires: python-devel
%if %is_suse
@@ -54,6 +55,7 @@ rm -fr $RPM_BUILD_ROOT
%{_bindir}/func
%{_bindir}/func-inventory
%{_bindir}/func-create-module
+%{_bindir}/func-build-map
#%{_bindir}/update-func
/etc/init.d/funcd
%dir %{_sysconfdir}/%{name}
@@ -123,6 +125,9 @@ fi


%changelog
+* Fri Jun 27 2008 Steve Salevan <ssalevan@xxxxxxxxxx> - 0.19-1
+- Various bugfixes, adding in mapping/delegation tools
+
* Mon Mar 03 2008 Adrian Likins <alikins@xxxxxxxxxx> - 0.18-1
- split off certmaster

diff --git a/func/minion/modules/certmaster.py b/func/minion/modules/certmaster.py
deleted file mode 100644
index c30a39c..0000000
--- a/func/minion/modules/certmaster.py
+++ /dev/null
@@ -1,61 +0,0 @@
-## -*- coding: utf-8 -*-
-##
-## Process lister (control TBA)
-##
-## Copyright 2008, Red Hat, Inc
-## Michael DeHaan <mdehaan@xxxxxxxxxx>
-##
-## 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.
-##
-
-# our modules
-import func_module
-from func import certmaster as certmaster
-
-# =================================
-
-class CertMasterModule(func_module.FuncModule):
-
-    version = "0.0.1"
-    api_version = "0.0.1"
-    description = "Administers certs on an overlord."
-
-    def get_hosts_to_sign(self, list_of_hosts):
-        """
-        ...
-        """
-        list_of_hosts = self.__listify(list_of_hosts)
-        cm = certmaster.CertMaster()
-        return cm.get_csrs_waiting()
-
-    def sign_hosts(self, list_of_hosts):
-        """
-        ...
-        """
-        list_of_hosts = self.__listify(list_of_hosts)
-        cm = certmaster.CertMaster()
-        for x in list_of_hosts:
-           cm.sign_this_csr(x)
-        return True
-
-    def cleanup_hosts(self, list_of_hosts):
-        """
-        ...
-        """
-        list_of_hosts = self.__listify(list_of_hosts)
-        cm = certmaster.CertMaster()
-        for x in list_of_hosts:
-           cm.remove_this_cert(x)
-        return True
-
-    def __listify(self, list_of_hosts):
-        if type(list_of_hosts) is type([]):
-            return list_of_hosts
-        else:
- return [ list_of_hosts ] - diff --git a/func/minion/modules/certmastermod.py b/func/minion/modules/certmastermod.py
new file mode 100644
index 0000000..73f1468
--- /dev/null
+++ b/func/minion/modules/certmastermod.py
@@ -0,0 +1,69 @@
+## -*- coding: utf-8 -*-
+##
+## Process lister (control TBA)
+##
+## Copyright 2008, Red Hat, Inc
+## Michael DeHaan <mdehaan@xxxxxxxxxx>
+##
+## 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.
+##
+
+# our modules
+import func_module
+from certmaster import certmaster as certmaster
+
+# =================================
+
+class CertMasterModule(func_module.FuncModule):
+
+    version = "0.0.1"
+    api_version = "0.0.1"
+    description = "Administers certs on an overlord."
+
+    def get_hosts_to_sign(self, list_of_hosts):
+        """
+        ...
+        """
+        list_of_hosts = self.__listify(list_of_hosts)
+        cm = certmaster.CertMaster()
+        return cm.get_csrs_waiting()
+ + def get_signed_certs(self, list_of_hosts):
+        """
+        Returns a list of all signed certs on this minion
+        """
+        list_of_hosts = self.__listify(list_of_hosts)
+        cm = certmaster.CertMaster()
+        return cm.get_signed_certs()
+
+    def sign_hosts(self, list_of_hosts):
+        """
+        ...
+        """
+        list_of_hosts = self.__listify(list_of_hosts)
+        cm = certmaster.CertMaster()
+        for x in list_of_hosts:
+           cm.sign_this_csr(x)
+        return True
+
+    def cleanup_hosts(self, list_of_hosts):
+        """
+        ...
+        """
+        list_of_hosts = self.__listify(list_of_hosts)
+        cm = certmaster.CertMaster()
+        for x in list_of_hosts:
+           cm.remove_this_cert(x)
+        return True
+
+    def __listify(self, list_of_hosts):
+        if type(list_of_hosts) is type([]):
+            return list_of_hosts
+        else:
+ return [ list_of_hosts ] + diff --git a/func/minion/modules/delegation.py b/func/minion/modules/delegation.py
new file mode 100644
index 0000000..9f34d1a
--- /dev/null
+++ b/func/minion/modules/delegation.py
@@ -0,0 +1,35 @@
+# Copyright 2008, Red Hat, Inc
+# Steve Salevan <ssalevan@xxxxxxxxxx>
+#
+# 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
+            overlord_module = getattr(overlord,module)
+            return 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 diff --git a/func/minion/modules/overlord.py b/func/minion/modules/overlord.py
new file mode 100644
index 0000000..710f6d1
--- /dev/null
+++ b/func/minion/modules/overlord.py
@@ -0,0 +1,41 @@
+# Copyright 2008, Red Hat, Inc
+# Steve Salevan <ssalevan@xxxxxxxxxx>
+#
+# 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 certmaster import certmaster as certmaster
+from func import utils
+
+class OverlordModule(func_module.FuncModule):
+
+    version = "0.0.1"
+    api_version = "0.0.1"
+    description = "Module for controlling minions that are also overlords."
+
+    def map_minions(self,get_only_alive=False):
+        """
+        Builds a recursive map of the minions currently assigned to this
+        overlord
+        """
+        maphash = {}
+        current_minions = []
+        if get_only_alive:
+            ping_results = fc.Overlord("*").test.ping()
+            for minion in ping_results.keys():
+                if ping_results[minion] == 1: #if minion is alive
+ current_minions.append(minion) #add it to the list of current minions
+        else:
+            cm = certmaster.CertMaster()
+            current_minions = cm.get_signed_certs()
+        for current_minion in current_minions:
+ maphash[current_minion] = fc.Overlord(current_minion).overlord.map_minions()[current_minion]
+        return maphash
+ + diff --git a/func/overlord/base_command.py b/func/overlord/base_command.py
index f7c33c0..9052c20 100644
--- a/func/overlord/base_command.py
+++ b/func/overlord/base_command.py
@@ -16,6 +16,8 @@ import command
import client

DEFAULT_PORT = 51234
+DEFAULT_MAPLOC = "/var/lib/func/map"
+
class BaseCommand(command.Command):
    """ wrapper class for commands with some convience functions, namely
    getOverlord() for getting a overlord client api handle"""
@@ -25,6 +27,8 @@ class BaseCommand(command.Command):
    port=DEFAULT_PORT
    async=False
    forks=1
+    delegate=False
+    mapfile=DEFAULT_MAPLOC
    def getOverlord(self):
        self.overlord_obj = client.Overlord(self.server_spec,
                                            port=self.port,
@@ -32,4 +36,6 @@ class BaseCommand(command.Command):
                                            verbose=self.verbose,
                                            config=self.config,
                                            async=self.async,
-                                            nforks=self.forks)
+                                            nforks=self.forks,
+                                            delegate=self.delegate,
+                                            mapfile=self.mapfile)
diff --git a/func/overlord/client.py b/func/overlord/client.py
index fb436ed..5ade5ad 100755
--- a/func/overlord/client.py
+++ b/func/overlord/client.py
@@ -16,6 +16,7 @@
import sys
import glob
import os
+import yaml

from certmaster.commonconfig import CMConfig
from func.config import read_config, CONFIG_FILE
@@ -24,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
@@ -35,6 +37,8 @@ from func.CommonErrors import *

DEFAULT_PORT = 51234
FUNC_USAGE = "Usage: %s [ --help ] [ --verbose ] target.example.org module method arg1 [...]"
+DEFAULT_MAPLOC = "/var/lib/func/map"
+DELEGATION_METH = "delegation.run"

# ===================================

@@ -88,13 +92,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)
@@ -153,12 +160,11 @@ def is_minion(minion_string):
    return minions.is_minion()


-
-
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): + verbose=False, noglobs=False, nforks=1, config=None, async=False, init_ssl=True,
+        delegate=False, mapfile=DEFAULT_MAPLOC):
        """
        Constructor.
        @server_spec -- something like "*.example.org" or "foosball"
@@ -179,9 +185,19 @@ class Overlord(object):
        self.noglobs     = noglobs
        self.nforks      = nforks
        self.async       = async
+        self.delegate    = delegate
+        self.mapfile     = mapfile
self.minions_class = Minions(self.server_spec, port=self.port, noglobs=self.noglobs,verbose=self.verbose)
        self.minions = self.minions_class.get_urls()
+ + if self.delegate:
+            try:
+                mapstream = file(self.mapfile, 'r')
+                self.minionmap = yaml.load(mapstream)
+            except e:
+ sys.stderr.write("mapfile load failed, switching delegation off")
+                self.delegate = False
if init_ssl:
            self.setup_ssl()
@@ -255,9 +271,50 @@ class Overlord(object):
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, so run normally
+            return self.run_direct(module, method, args, nforks)
+ + resulthash = {} + + #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:
+            resulthash.update(self.run_direct(module,
+                                              method,
+                                              args,
+                                              nforks,
+                                              call_path=path))
+ + #Next, we run everything that can be run directly beneath this overlord + #Why do we do this after delegation calls? Imagine what happens when
+        #reboot is called...
+        resulthash.update(self.run_direct(module,method,args,nforks))
+ + return resulthash + + + # -----------------------------------------------

-        results = {}
+ def run_direct(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.
+        """

+        results = {}
+        spec = ''
+        minionurls = []
+        use_delegate = False
+        delegation_path = []
+ def process_server(bucketnumber, buckets, server): conn = sslclient.FuncServer(server, self.key, self.cert, self.ca )
@@ -274,7 +331,10 @@ class Overlord(object):
                # thats some pretty code right there aint it? -akl
                # we can't call "call" on s, since thats a rpc, so
                # we call gettatr around it.
-                meth = "%s.%s" % (module, method)
+                if use_delegate:
+                    meth = DELEGATION_METH #call delegation module
+                else:
+                    meth = "%s.%s" % (module, method)

                # async calling signature has an "imaginary" prefix
                # so async.abc.def does abc.def as a background task.
@@ -283,7 +343,10 @@ class Overlord(object):
                    meth = "async.%s" % meth

                # this is the point at which we make the remote call.
-                retval = getattr(conn, meth)(*args[:])
+                if use_delegate:
+ retval = getattr(conn, meth)(module, method, args, delegation_path)
+                else:
+                    retval = getattr(conn, meth)(*args[:])

                if self.interactive:
                    print retval
@@ -301,29 +364,43 @@ class Overlord(object):
                right = server.rfind(":")
                server_name = server[left:right]
                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
+            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
+            spec = self.server_spec
+            minionurls = self.minions
+ if not self.noglobs:
            if self.nforks > 1 or self.async:
# using forkbomb module to distribute job over multiple threads
                if not self.async:
- results = forkbomb.batch_run(self.minions, process_server, nforks) + results = forkbomb.batch_run(minionurls, process_server, nforks)
                else:
- results = jobthing.batch_run(self.minions, process_server, nforks) + results = jobthing.batch_run(minionurls, process_server, nforks)
            else:
# no need to go through the fork code, we can do this directly
                results = {}
-                for x in self.minions:
+                for x in minionurls:
                    (nkey,nvalue) = process_server(0, 0, x)
results[nkey] = nvalue else:
            # globbing is not being used, but still need to make sure
            # URI is well formed.
# expanded = expand_servers(self.server_spec, port=self.port, noglobs=True, verbose=self.verbose)[0] - expanded_minions = Minions(self.server_spec, port=self.port, noglobs=True, verbose=self.verbose) + expanded_minions = Minions(spec, port=self.port, noglobs=True, verbose=self.verbose)
            minions = expanded_minions.get_urls()[0]
#            print minions
            results = process_server(0, 0, minions)
-
+ + if use_delegate:
+            return results[spec]
+ return results

   # -----------------------------------------------
diff --git a/func/overlord/cmd_modules/call.py b/func/overlord/cmd_modules/call.py
index 53db70e..2496527 100644
--- a/func/overlord/cmd_modules/call.py
+++ b/func/overlord/cmd_modules/call.py
@@ -58,6 +58,9 @@ class Call(base_command.BaseCommand):
        self.parser.add_option("-s", "--jobstatus", dest="jobstatus",
help="Do not run any job, just check for status.",
                               action="store_true")
+        self.parser.add_option('-d', '--delegate', dest="delegate",
+                               help="use delegation to make function call",
+                               action="store_true")

    def handleOptions(self, options):
        self.options = options
@@ -124,10 +127,11 @@ class Call(base_command.BaseCommand):
        self.interactive = False
        self.async = self.options.async
        self.forks = self.options.forks
+        self.delegate = self.options.delegate
self.server_spec = self.parentCommand.server_spec
        self.getOverlord()
- + print self.overlord_obj

        if not self.options.jobstatus:
results = self.overlord_obj.run(self.module, self.method, self.method_args) diff --git a/func/overlord/delegation_tools.py b/func/overlord/delegation_tools.py
new file mode 100644
index 0000000..f3445c0
--- /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@xxxxxxxxxx>
+##
+## 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('*path*',mymap):
+        print "Valid Path: %s" % elem
diff --git a/func/overlord/mapper.py b/func/overlord/mapper.py
new file mode 100755
index 0000000..345eb6b
--- /dev/null
+++ b/func/overlord/mapper.py
@@ -0,0 +1,91 @@
+##
+## func topology map-building tool
+## If you've got a giant, tangled, complex web of func overlords
+## and minions, this tool will help you construct or augment a map
+## of your func network topology so that delegating commands to
+## minions and overlords becomes a simple matter.
+##
+## Copyright 2008, Red Hat, Inc.
+## Steve Salevan <ssalevan@xxxxxxxxxx>
+##
+## 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 optparse
+import sys
+import yaml
+import func.overlord.client as func_client
+
+DEFAULT_TREE = "/var/lib/func/map"
+
+class MapperTool(object):
+
+    def __init__(self):
+        pass
+ + def run(self,args): + + p = optparse.OptionParser()
+        #currently not implemented
+        p.add_option("-a", "--append",
+                     dest="append",
+                     action="store_true",
+                     help="append new map to current map")
+        p.add_option("-o", "--onlyalive",
+                     dest="only_alive",
+                     action="store_true",
+                     help="gather only currently-living minions")
+        p.add_option("-v", "--verbose",
+                     dest="verbose",
+                     action="store_true",
+                     help="provide extra output")
+ + (options, args) = p.parse_args(args)
+        self.options = options
+ + if options.verbose:
+            print "- recursively calling map function"
+ + self.build_map() + + return 1 + + def build_map(self): + + minion_hash = func_client.Overlord("*").overlord.map_minions(self.options.only_alive==True) + + if self.options.verbose:
+            print "- built the following map:"
+            print minion_hash
+ + if self.options.append:
+            try:
+                oldmap = file(DEFAULT_TREE, 'r')
+                old_hash = yaml.load(oldmap)
+                oldmap.close()
+            except e:
+                print "ERROR: old map could not be read, append failed"
+                sys.exit(-1)
+ + merged_map = {}
+            merged_map.update(old_hash)
+            merged_map.update(minion_hash)
+ + if self.options.verbose:
+                print "- appended new map to the following map:"
+                print old_hash
+                print "  resulting in:"
+                print merged_map
+ + minion_hash = merged_map + + if self.options.verbose:
+            print "- writing to %s" % DEFAULT_TREE
+ + mapfile = file(DEFAULT_TREE, 'w')
+        yaml.dump(minion_hash,mapfile)
diff --git a/scripts/func-build-map b/scripts/func-build-map
new file mode 100755
index 0000000..734929a
--- /dev/null
+++ b/scripts/func-build-map
@@ -0,0 +1,8 @@
+#!/usr/bin/python
+
+import sys
+import distutils.sysconfig
+import func.overlord.mapper as mapper
+
+mapper = mapper.MapperTool()
+mapper.run(sys.argv)
diff --git a/setup.py b/setup.py
index 2d1d971..66f5a83 100644
--- a/setup.py
+++ b/setup.py
@@ -33,7 +33,8 @@ if __name__ == "__main__":
             "scripts/func",
                     "scripts/func-inventory",
                     "scripts/func-create-module",
-             # "scripts/update-func",
+             "scripts/func-build-map"
+                     # "scripts/update-func",
                ],
        # package_data = { '' : ['*.*'] },
                package_dir = {"%s" % NAME: "%s" % NAME
--
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