From af4f09f155f4a4f1c5a9e3e29b4c8736f892dce4 Mon Sep 17 00:00:00 2001 From: Adrian Likins Date: Mon, 17 Mar 2008 17:10:32 -0400 Subject: remove unused certmaster/minion/ and certmaster/overlord/ dirs update spec and setup accordingly --- certmaster.spec | 7 +- certmaster/minion/AuthedXMLRPCServer.py | 140 ---- certmaster/minion/Makefile | 24 - certmaster/minion/__init__.py | 0 certmaster/minion/codes.py | 29 - certmaster/minion/server.py | 285 -------- certmaster/minion/sub_process.py | 1221 ------------------------------- certmaster/minion/utils.py | 207 ------ certmaster/overlord/.forkbomb.py.swp | Bin 16384 -> 0 bytes certmaster/overlord/Makefile | 18 - certmaster/overlord/__init__.py | 0 certmaster/overlord/client.py | 336 --------- certmaster/overlord/command.py | 287 -------- certmaster/overlord/func_command.py | 71 -- certmaster/overlord/groups.py | 95 --- certmaster/overlord/highlevel.py | 40 - certmaster/overlord/inventory.py | 191 ----- certmaster/overlord/sslclient.py | 50 -- po/messages.pot~ | 27 - setup.py | 2 - 20 files changed, 3 insertions(+), 3027 deletions(-) delete mode 100644 certmaster/minion/AuthedXMLRPCServer.py delete mode 100755 certmaster/minion/Makefile delete mode 100644 certmaster/minion/__init__.py delete mode 100755 certmaster/minion/codes.py delete mode 100755 certmaster/minion/server.py delete mode 100644 certmaster/minion/sub_process.py delete mode 100755 certmaster/minion/utils.py delete mode 100644 certmaster/overlord/.forkbomb.py.swp delete mode 100755 certmaster/overlord/Makefile delete mode 100644 certmaster/overlord/__init__.py delete mode 100755 certmaster/overlord/client.py delete mode 100644 certmaster/overlord/command.py delete mode 100644 certmaster/overlord/func_command.py delete mode 100644 certmaster/overlord/groups.py delete mode 100644 certmaster/overlord/highlevel.py delete mode 100755 certmaster/overlord/inventory.py delete mode 100755 certmaster/overlord/sslclient.py diff --git a/certmaster.spec b/certmaster.spec index 8dc5ef3..3a04a72 100644 --- a/certmaster.spec +++ b/certmaster.spec @@ -60,10 +60,6 @@ rm -fr $RPM_BUILD_ROOT %config(noreplace) /etc/certmaster/certmaster.conf %config(noreplace) /etc/logrotate.d/certmaster_rotate %dir %{python_sitelib}/certmaster -%dir %{python_sitelib}/certmaster/minion -%dir %{python_sitelib}/certmaster/overlord -%{python_sitelib}/certmaster/minion/*.py* -%{python_sitelib}/certmaster/overlord/*.py* %{python_sitelib}/certmaster/*.py* %dir /var/log/certmaster %dir /var/lib/certmaster @@ -103,6 +99,9 @@ fi %changelog +* Mon March 17 2008 Adrian Likins - 0.1-2 +- removed unused minion/ and overlord/ dirs + * Mon Feb 25 2008 Adrian Likins - 0.1-1 - remove certmasterd references diff --git a/certmaster/minion/AuthedXMLRPCServer.py b/certmaster/minion/AuthedXMLRPCServer.py deleted file mode 100644 index 0ec9ce0..0000000 --- a/certmaster/minion/AuthedXMLRPCServer.py +++ /dev/null @@ -1,140 +0,0 @@ -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Library General Public License for more details. -# -# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# Copyright 2005 Dan Williams and Red Hat, Inc. -# Modifications by Seth Vidal - 2007 - -import sys -import socket -import SimpleXMLRPCServer -from func import SSLCommon -import OpenSSL -import SocketServer - - -class AuthedSimpleXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): - - # For some reason, httplib closes the connection right after headers - # have been sent if the connection is _not_ HTTP/1.1, which results in - # a "Bad file descriptor" error when the client tries to read from the socket - protocol_version = "HTTP/1.1" - - def setup(self): - """ - We need to use socket._fileobject Because SSL.Connection - doesn't have a 'dup'. Not exactly sure WHY this is, but - this is backed up by comments in socket.py and SSL/connection.c - """ - self.connection = self.request # for doPOST - self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) - self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) - - def do_POST(self): - self.server._this_request = (self.request, self.client_address) - try: - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self) - except socket.timeout: - pass - except (socket.error, OpenSSL.SSL.SysCallError), e: - print "Error (%s): socket error - '%s'" % (self.client_address, e) - - -class BaseAuthedXMLRPCServer(SocketServer.ThreadingMixIn): - def __init__(self, address, authinfo_callback=None): - self.allow_reuse_address = 1 - self.logRequests = 1 - self.authinfo_callback = authinfo_callback - - self.funcs = {} - self.instance = None - - def get_authinfo(self, request, client_address): - print 'down here' - if self.authinfo_callback: - return self.authinfo_callback(request, client_address) - return None - - -class AuthedSSLXMLRPCServer(BaseAuthedXMLRPCServer, SSLCommon.BaseSSLServer, SimpleXMLRPCServer.SimpleXMLRPCServer): - """ Extension to allow more fine-tuned SSL handling """ - - def __init__(self, address, pkey, cert, ca_cert, authinfo_callback=None, timeout=None): - BaseAuthedXMLRPCServer.__init__(self, address, authinfo_callback) - SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, AuthedSimpleXMLRPCRequestHandler) - SSLCommon.BaseSSLServer.__init__(self, address, AuthedSimpleXMLRPCRequestHandler, pkey, cert, ca_cert, timeout=timeout) - - - -class AuthedXMLRPCServer(BaseAuthedXMLRPCServer, SSLCommon.BaseServer, SimpleXMLRPCServer.SimpleXMLRPCServer): - - def __init__(self, address, authinfo_callback=None): - BaseAuthedXMLRPCServer.__init__(self, address, authinfo_callback) - SSLCommon.BaseServer.__init__(self, address, AuthedSimpleXMLRPCRequestHandler) - - -########################################################### -# Testing stuff -########################################################### - -class ReqHandler: - def ping(self, callerid, trynum): - print 'clearly not' - print callerid - print trynum - return "pong %d / %d" % (callerid, trynum) - -class TestServer(AuthedSSLXMLRPCServer): - """ - SSL XMLRPC server that authenticates clients based on their certificate. - """ - - def __init__(self, address, pkey, cert, ca_cert): - AuthedSSLXMLRPCServer.__init__(self, address, pkey, cert, ca_cert, self.auth_cb) - - def _dispatch(self, method, params): - if method == 'trait_names' or method == '_getAttributeNames': - return dir(self) - # if we have _this_request then we get the peer cert from it - # handling all the authZ checks in _dispatch() means we don't even call the method - # for whatever it wants to do and we have the method name. - - if hasattr(self, '_this_request'): - r,a = self._this_request - p = r.get_peer_certificate() - print dir(p) - print p.get_subject() - else: - print 'no cert' - - return "your mom" - - def auth_cb(self, request, client_address): - peer_cert = request.get_peer_certificate() - return peer_cert.get_subject().CN - - -if __name__ == '__main__': - if len(sys.argv) < 4: - print "Usage: python AuthdXMLRPCServer.py key cert ca_cert" - sys.exit(1) - - pkey = sys.argv[1] - cert = sys.argv[2] - ca_cert = sys.argv[3] - - print "Starting the server." - server = TestServer(('localhost', 51234), pkey, cert, ca_cert) - h = ReqHandler() - server.register_instance(h) - server.serve_forever() diff --git a/certmaster/minion/Makefile b/certmaster/minion/Makefile deleted file mode 100755 index d630382..0000000 --- a/certmaster/minion/Makefile +++ /dev/null @@ -1,24 +0,0 @@ - - -PYFILES = $(wildcard *.py) -PYDIRS = modules - -PYCHECKER = /usr/bin/pychecker -PYFLAKES = /usr/bin/pyflakes - -clean:: - @rm -fv *.pyc *~ .*~ *.pyo - @find . -name .\#\* -exec rm -fv {} \; - @rm -fv *.rpm - - -pychecker:: - @$(PYCHECKER) $(PYFILES) || exit 0 - -pyflakes:: - @$(PYFLAKES) $(PYFILES) || exit 0 - -pychecker:: - -for d in $(PYDIRS); do ($(MAKE) -C $$d pychecker ); done -pyflakes:: - -for d in $(PYDIRS); do ($(MAKE) -C $$d pyflakes ); done diff --git a/certmaster/minion/__init__.py b/certmaster/minion/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/certmaster/minion/codes.py b/certmaster/minion/codes.py deleted file mode 100755 index a20c95e..0000000 --- a/certmaster/minion/codes.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -func - -Copyright 2007, Red Hat, Inc -See AUTHORS - -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 exceptions - - -class FuncException(exceptions.Exception): - pass - - -class InvalidMethodException(FuncException): - pass - - -class AccessToMethodDenied(FuncException): - pass - -# FIXME: more sub-exceptions maybe diff --git a/certmaster/minion/server.py b/certmaster/minion/server.py deleted file mode 100755 index f1b827f..0000000 --- a/certmaster/minion/server.py +++ /dev/null @@ -1,285 +0,0 @@ -""" -func - -Copyright 2007, Red Hat, Inc -see AUTHORS - -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. -""" - -# standard modules -import SimpleXMLRPCServer -import string -import sys -import traceback -import socket -import fnmatch - -from gettext import textdomain -I18N_DOMAIN = "func" - - -from func.config import read_config -from func.commonconfig import FuncdConfig -from func import logger -from func import certs -import func.jobthing as jobthing -import utils - -# our modules -import AuthedXMLRPCServer -import codes -import module_loader -import func.utils as futils - - - -class XmlRpcInterface(object): - - def __init__(self): - - """ - Constructor. - """ - - config_file = '/etc/func/minion.conf' - self.config = read_config(config_file, FuncdConfig) - self.logger = logger.Logger().logger - self.audit_logger = logger.AuditLogger() - self.__setup_handlers() - - # need a reference so we can log ip's, certs, etc - # self.server = server - - def __setup_handlers(self): - - """ - Add RPC functions from each class to the global list so they can be called. - """ - - self.handlers = {} - for x in self.modules.keys(): - try: - self.modules[x].register_rpc(self.handlers, x) - self.logger.debug("adding %s" % x) - except AttributeError, e: - self.logger.warning("module %s not loaded, missing register_rpc method" % self.modules[x]) - - - # internal methods that we do instead of spreading internal goo - # all over the modules. For now, at lest -akl - - - # system.listMethods os a quasi stanard xmlrpc method, so - # thats why it has a odd looking name - self.handlers["system.listMethods"] = self.list_methods - self.handlers["system.list_methods"] = self.list_methods - self.handlers["system.list_modules"] = self.list_modules - - def list_modules(self): - modules = self.modules.keys() - modules.sort() - return modules - - def list_methods(self): - methods = self.handlers.keys() - methods.sort() - return methods - - def get_dispatch_method(self, method): - - if method in self.handlers: - return FuncApiMethod(self.logger, method, self.handlers[method]) - - else: - self.logger.info("Unhandled method call for method: %s " % method) - raise codes.InvalidMethodException - - -class FuncApiMethod: - - """ - Used to hold a reference to all of the registered functions. - """ - - def __init__(self, logger, name, method): - - self.logger = logger - self.__method = method - self.__name = name - - def __log_exc(self): - - """ - Log an exception. - """ - - (t, v, tb) = sys.exc_info() - self.logger.info("Exception occured: %s" % t ) - self.logger.info("Exception value: %s" % v) - self.logger.info("Exception Info:\n%s" % string.join(traceback.format_list(traceback.extract_tb(tb)))) - - def __call__(self, *args): - - self.logger.debug("(X) -------------------------------------------") - - try: - rc = self.__method(*args) - except codes.FuncException, e: - self.__log_exc() - (t, v, tb) = sys.exc_info() - rc = futils.nice_exception(t,v,tb) - except: - self.__log_exc() - (t, v, tb) = sys.exc_info() - rc = futils.nice_exception(t,v,tb) - self.logger.debug("Return code for %s: %s" % (self.__name, rc)) - - return rc - - -def serve(): - - """ - Code for starting the XMLRPC service. - """ - server =FuncSSLXMLRPCServer(('', 51234)) - server.logRequests = 0 # don't print stuff to console - server.serve_forever() - - - -class FuncXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, XmlRpcInterface): - - def __init__(self, args): - - self.allow_reuse_address = True - - self.modules = module_loader.load_modules() - SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, args) - XmlRpcInterface.__init__(self) - - -class FuncSSLXMLRPCServer(AuthedXMLRPCServer.AuthedSSLXMLRPCServer, - XmlRpcInterface): - def __init__(self, args): - self.allow_reuse_address = True - self.modules = module_loader.load_modules() - - XmlRpcInterface.__init__(self) - hn = utils.get_hostname() - self.key = "%s/%s.pem" % (self.config.cert_dir, hn) - self.cert = "%s/%s.cert" % (self.config.cert_dir, hn) - self.ca = "%s/ca.cert" % self.config.cert_dir - - self._our_ca = certs.retrieve_cert_from_file(self.ca) - - AuthedXMLRPCServer.AuthedSSLXMLRPCServer.__init__(self, ("", 51234), - self.key, self.cert, - self.ca) - - def _dispatch(self, method, params): - - """ - the SimpleXMLRPCServer class will call _dispatch if it doesn't - find a handler method - """ - # take _this_request and hand it off to check out the acls of the method - # being called vs the requesting host - - if not hasattr(self, '_this_request'): - raise codes.InvalidMethodException - - r,a = self._this_request - peer_cert = r.get_peer_certificate() - ip = a[0] - - - # generally calling conventions are: hardware.info - # async convention is async.hardware.info - # here we parse out the async to decide how to invoke it. - # see the async docs on the Wiki for further info. - async_dispatch = False - if method.startswith("async."): - async_dispatch = True - method = method.replace("async.","",1) - - if not self._check_acl(peer_cert, ip, method, params): - raise codes.AccessToMethodDenied - - # Recognize ipython's tab completion calls - if method == 'trait_names' or method == '_getAttributeNames': - return self.handlers.keys() - - cn = peer_cert.get_subject().CN - sub_hash = peer_cert.subject_name_hash() - self.audit_logger.log_call(ip, cn, sub_hash, method, params) - - try: - if not async_dispatch: - return self.get_dispatch_method(method)(*params) - else: - return jobthing.minion_async_run(self.get_dispatch_method, method, params) - except: - (t, v, tb) = sys.exc_info() - rc = futils.nice_exception(t, v, tb) - return rc - - def auth_cb(self, request, client_address): - peer_cert = request.get_peer_certificate() - return peer_cert.get_subject().CN - - def _check_acl(self, cert, ip, method, params): - acls = utils.get_acls_from_config(acldir=self.config.acl_dir) - - # certmaster always gets to run things - ca_cn = self._our_ca.get_subject().CN - ca_hash = self._our_ca.subject_name_hash() - ca_key = '%s-%s' % (ca_cn, ca_hash) - acls[ca_key] = ['*'] - - cn = cert.get_subject().CN - sub_hash = cert.subject_name_hash() - if acls: - allow_list = [] - hostkey = '%s-%s' % (cn, sub_hash) - # search all the keys, match to 'cn-subhash' - for hostmatch in acls.keys(): - if fnmatch.fnmatch(hostkey, hostmatch): - allow_list.extend(acls[hostmatch]) - # go through the allow_list and make sure this method is in there - for methodmatch in allow_list: - if fnmatch.fnmatch(method, methodmatch): - return True - - return False - - -def main(argv): - - """ - Start things up. - """ - - if "daemon" in sys.argv or "--daemon" in sys.argv: - futils.daemonize("/var/run/funcd.pid") - else: - print "serving...\n" - - try: - utils.create_minion_keys() - serve() - except codes.FuncException, e: - print >> sys.stderr, 'error: %s' % e - sys.exit(1) - - -# ====================================================================================== -if __name__ == "__main__": - textdomain(I18N_DOMAIN) - main(sys.argv) diff --git a/certmaster/minion/sub_process.py b/certmaster/minion/sub_process.py deleted file mode 100644 index 351a951..0000000 --- a/certmaster/minion/sub_process.py +++ /dev/null @@ -1,1221 +0,0 @@ -# subprocess - Subprocesses with accessible I/O streams -# -# For more information about this module, see PEP 324. -# -# This module should remain compatible with Python 2.2, see PEP 291. -# -# Copyright (c) 2003-2005 by Peter Astrand -# -# Licensed to PSF under a Contributor Agreement. -# See http://www.python.org/2.4/license for licensing details. - -r"""subprocess - Subprocesses with accessible I/O streams - -This module allows you to spawn processes, connect to their -input/output/error pipes, and obtain their return codes. This module -intends to replace several other, older modules and functions, like: - -os.system -os.spawn* -os.popen* -popen2.* -commands.* - -Information about how the subprocess module can be used to replace these -modules and functions can be found below. - - - -Using the subprocess module -=========================== -This module defines one class called Popen: - -class Popen(args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - - -Arguments are: - -args should be a string, or a sequence of program arguments. The -program to execute is normally the first item in the args sequence or -string, but can be explicitly set by using the executable argument. - -On UNIX, with shell=False (default): In this case, the Popen class -uses os.execvp() to execute the child program. args should normally -be a sequence. A string will be treated as a sequence with the string -as the only item (the program to execute). - -On UNIX, with shell=True: If args is a string, it specifies the -command string to execute through the shell. If args is a sequence, -the first item specifies the command string, and any additional items -will be treated as additional shell arguments. - -On Windows: the Popen class uses CreateProcess() to execute the child -program, which operates on strings. If args is a sequence, it will be -converted to a string using the list2cmdline method. Please note that -not all MS Windows applications interpret the command line the same -way: The list2cmdline is designed for applications using the same -rules as the MS C runtime. - -bufsize, if given, has the same meaning as the corresponding argument -to the built-in open() function: 0 means unbuffered, 1 means line -buffered, any other positive value means use a buffer of -(approximately) that size. A negative bufsize means to use the system -default, which usually means fully buffered. The default value for -bufsize is 0 (unbuffered). - -stdin, stdout and stderr specify the executed programs' standard -input, standard output and standard error file handles, respectively. -Valid values are PIPE, an existing file descriptor (a positive -integer), an existing file object, and None. PIPE indicates that a -new pipe to the child should be created. With None, no redirection -will occur; the child's file handles will be inherited from the -parent. Additionally, stderr can be STDOUT, which indicates that the -stderr data from the applications should be captured into the same -file handle as for stdout. - -If preexec_fn is set to a callable object, this object will be called -in the child process just before the child is executed. - -If close_fds is true, all file descriptors except 0, 1 and 2 will be -closed before the child process is executed. - -if shell is true, the specified command will be executed through the -shell. - -If cwd is not None, the current directory will be changed to cwd -before the child is executed. - -If env is not None, it defines the environment variables for the new -process. - -If universal_newlines is true, the file objects stdout and stderr are -opened as a text files, but lines may be terminated by any of '\n', -the Unix end-of-line convention, '\r', the Macintosh convention or -'\r\n', the Windows convention. All of these external representations -are seen as '\n' by the Python program. Note: This feature is only -available if Python is built with universal newline support (the -default). Also, the newlines attribute of the file objects stdout, -stdin and stderr are not updated by the communicate() method. - -The startupinfo and creationflags, if given, will be passed to the -underlying CreateProcess() function. They can specify things such as -appearance of the main window and priority for the new process. -(Windows only) - - -This module also defines two shortcut functions: - -call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - -check_call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete. If the - exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - -Exceptions ----------- -Exceptions raised in the child process, before the new program has -started to execute, will be re-raised in the parent. Additionally, -the exception object will have one extra attribute called -'child_traceback', which is a string containing traceback information -from the childs point of view. - -The most common exception raised is OSError. This occurs, for -example, when trying to execute a non-existent file. Applications -should prepare for OSErrors. - -A ValueError will be raised if Popen is called with invalid arguments. - -check_call() will raise CalledProcessError, if the called process -returns a non-zero return code. - - -Security --------- -Unlike some other popen functions, this implementation will never call -/bin/sh implicitly. This means that all characters, including shell -metacharacters, can safely be passed to child processes. - - -Popen objects -============= -Instances of the Popen class have the following methods: - -poll() - Check if child process has terminated. Returns returncode - attribute. - -wait() - Wait for child process to terminate. Returns returncode attribute. - -communicate(input=None) - Interact with process: Send data to stdin. Read data from stdout - and stderr, until end-of-file is reached. Wait for process to - terminate. The optional stdin argument should be a string to be - sent to the child process, or None, if no data should be sent to - the child. - - communicate() returns a tuple (stdout, stderr). - - Note: The data read is buffered in memory, so do not use this - method if the data size is large or unlimited. - -The following attributes are also available: - -stdin - If the stdin argument is PIPE, this attribute is a file object - that provides input to the child process. Otherwise, it is None. - -stdout - If the stdout argument is PIPE, this attribute is a file object - that provides output from the child process. Otherwise, it is - None. - -stderr - If the stderr argument is PIPE, this attribute is file object that - provides error output from the child process. Otherwise, it is - None. - -pid - The process ID of the child process. - -returncode - The child return code. A None value indicates that the process - hasn't terminated yet. A negative value -N indicates that the - child was terminated by signal N (UNIX only). - - -Replacing older functions with the subprocess module -==================================================== -In this section, "a ==> b" means that b can be used as a replacement -for a. - -Note: All functions in this section fail (more or less) silently if -the executed program cannot be found; this module raises an OSError -exception. - -In the following examples, we assume that the subprocess module is -imported with "from subprocess import *". - - -Replacing /bin/sh shell backquote ---------------------------------- -output=`mycmd myarg` -==> -output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] - - -Replacing shell pipe line -------------------------- -output=`dmesg | grep hda` -==> -p1 = Popen(["dmesg"], stdout=PIPE) -p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) -output = p2.communicate()[0] - - -Replacing os.system() ---------------------- -sts = os.system("mycmd" + " myarg") -==> -p = Popen("mycmd" + " myarg", shell=True) -pid, sts = os.waitpid(p.pid, 0) - -Note: - -* Calling the program through the shell is usually not required. - -* It's easier to look at the returncode attribute than the - exitstatus. - -A more real-world example would look like this: - -try: - retcode = call("mycmd" + " myarg", shell=True) - if retcode < 0: - print >>sys.stderr, "Child was terminated by signal", -retcode - else: - print >>sys.stderr, "Child returned", retcode -except OSError, e: - print >>sys.stderr, "Execution failed:", e - - -Replacing os.spawn* -------------------- -P_NOWAIT example: - -pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") -==> -pid = Popen(["/bin/mycmd", "myarg"]).pid - - -P_WAIT example: - -retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") -==> -retcode = call(["/bin/mycmd", "myarg"]) - - -Vector example: - -os.spawnvp(os.P_NOWAIT, path, args) -==> -Popen([path] + args[1:]) - - -Environment example: - -os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) -==> -Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) - - -Replacing os.popen* -------------------- -pipe = os.popen(cmd, mode='r', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout - -pipe = os.popen(cmd, mode='w', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin - - -(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdin, child_stdout) = (p.stdin, p.stdout) - - -(child_stdin, - child_stdout, - child_stderr) = os.popen3(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) -(child_stdin, - child_stdout, - child_stderr) = (p.stdin, p.stdout, p.stderr) - - -(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) -(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) - - -Replacing popen2.* ------------------- -Note: If the cmd argument to popen2 functions is a string, the command -is executed through /bin/sh. If it is a list, the command is directly -executed. - -(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) -==> -p = Popen(["somestring"], shell=True, bufsize=bufsize - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - - -(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) -==> -p = Popen(["mycmd", "myarg"], bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - -The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, -except that: - -* subprocess.Popen raises an exception if the execution fails -* the capturestderr argument is replaced with the stderr argument. -* stdin=PIPE and stdout=PIPE must be specified. -* popen2 closes all filedescriptors by default, but you have to specify - close_fds=True with subprocess.Popen. - - -""" - -import sys -mswindows = (sys.platform == "win32") - -import os -import types -import traceback - -# Exception classes used by this module. -class CalledProcessError(Exception): - """This exception is raised when a process run by check_call() returns - a non-zero exit status. The exit status will be stored in the - returncode attribute.""" - def __init__(self, returncode, cmd): - self.returncode = returncode - self.cmd = cmd - def __str__(self): - return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) - - -if mswindows: - import threading - import msvcrt - if 0: # <-- change this to use pywin32 instead of the _subprocess driver - import pywintypes - from win32api import GetStdHandle, STD_INPUT_HANDLE, \ - STD_OUTPUT_HANDLE, STD_ERROR_HANDLE - from win32api import GetCurrentProcess, DuplicateHandle, \ - GetModuleFileName, GetVersion - from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE - from win32pipe import CreatePipe - from win32process import CreateProcess, STARTUPINFO, \ - GetExitCodeProcess, STARTF_USESTDHANDLES, \ - STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE - from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 - else: - from _subprocess import * - class STARTUPINFO: - dwFlags = 0 - hStdInput = None - hStdOutput = None - hStdError = None - wShowWindow = 0 - class pywintypes: - error = IOError -else: - import select - import errno - import fcntl - import pickle - -__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] - -try: - MAXFD = os.sysconf("SC_OPEN_MAX") -except: - MAXFD = 256 - -# True/False does not exist on 2.2.0 -try: - False -except NameError: - False = 0 - True = 1 - -_active = [] - -def _cleanup(): - for inst in _active[:]: - if inst.poll(_deadstate=sys.maxint) >= 0: - try: - _active.remove(inst) - except ValueError: - # This can happen if two threads create a new Popen instance. - # It's harmless that it was already removed, so ignore. - pass - -PIPE = -1 -STDOUT = -2 - - -def call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - """ - return Popen(*popenargs, **kwargs).wait() - - -def check_call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete. If - the exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - """ - retcode = call(*popenargs, **kwargs) - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - if retcode: - raise CalledProcessError(retcode, cmd) - return retcode - - -def list2cmdline(seq): - """ - Translate a sequence of arguments into a command line - string, using the same rules as the MS C runtime: - - 1) Arguments are delimited by white space, which is either a - space or a tab. - - 2) A string surrounded by double quotation marks is - interpreted as a single argument, regardless of white space - contained within. A quoted string can be embedded in an - argument. - - 3) A double quotation mark preceded by a backslash is - interpreted as a literal double quotation mark. - - 4) Backslashes are interpreted literally, unless they - immediately precede a double quotation mark. - - 5) If backslashes immediately precede a double quotation mark, - every pair of backslashes is interpreted as a literal - backslash. If the number of backslashes is odd, the last - backslash escapes the next double quotation mark as - described in rule 3. - """ - - # See - # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp - result = [] - needquote = False - for arg in seq: - bs_buf = [] - - # Add a space to separate this argument from the others - if result: - result.append(' ') - - needquote = (" " in arg) or ("\t" in arg) - if needquote: - result.append('"') - - for c in arg: - if c == '\\': - # Don't know if we need to double yet. - bs_buf.append(c) - elif c == '"': - # Double backspaces. - result.append('\\' * len(bs_buf)*2) - bs_buf = [] - result.append('\\"') - else: - # Normal char - if bs_buf: - result.extend(bs_buf) - bs_buf = [] - result.append(c) - - # Add remaining backspaces, if any. - if bs_buf: - result.extend(bs_buf) - - if needquote: - result.extend(bs_buf) - result.append('"') - - return ''.join(result) - - -class Popen(object): - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - """Create new Popen instance.""" - _cleanup() - - self._child_created = False - if not isinstance(bufsize, (int, long)): - raise TypeError("bufsize must be an integer") - - if mswindows: - if preexec_fn is not None: - raise ValueError("preexec_fn is not supported on Windows " - "platforms") - if close_fds: - raise ValueError("close_fds is not supported on Windows " - "platforms") - else: - # POSIX - if startupinfo is not None: - raise ValueError("startupinfo is only supported on Windows " - "platforms") - if creationflags != 0: - raise ValueError("creationflags is only supported on Windows " - "platforms") - - self.stdin = None - self.stdout = None - self.stderr = None - self.pid = None - self.returncode = None - self.universal_newlines = universal_newlines - - # Input and output objects. The general principle is like - # this: - # - # Parent Child - # ------ ----- - # p2cwrite ---stdin---> p2cread - # c2pread <--stdout--- c2pwrite - # errread <--stderr--- errwrite - # - # On POSIX, the child objects are file descriptors. On - # Windows, these are Windows file handles. The parent objects - # are file descriptors on both platforms. The parent objects - # are None when not using PIPEs. The child objects are None - # when not redirecting. - - (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) = self._get_handles(stdin, stdout, stderr) - - self._execute_child(args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - if p2cwrite: - self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) - if c2pread: - if universal_newlines: - self.stdout = os.fdopen(c2pread, 'rU', bufsize) - else: - self.stdout = os.fdopen(c2pread, 'rb', bufsize) - if errread: - if universal_newlines: - self.stderr = os.fdopen(errread, 'rU', bufsize) - else: - self.stderr = os.fdopen(errread, 'rb', bufsize) - - - def _translate_newlines(self, data): - data = data.replace("\r\n", "\n") - data = data.replace("\r", "\n") - return data - - - def __del__(self): - if not self._child_created: - # We didn't get to successfully create a child process. - return - # In case the child hasn't been waited on, check if it's done. - self.poll(_deadstate=sys.maxint) - if self.returncode is None and _active is not None: - # Child is still running, keep us alive until we can wait on it. - _active.append(self) - - - def communicate(self, input=None): - """Interact with process: Send data to stdin. Read data from - stdout and stderr, until end-of-file is reached. Wait for - process to terminate. The optional input argument should be a - string to be sent to the child process, or None, if no data - should be sent to the child. - - communicate() returns a tuple (stdout, stderr).""" - - # Optimization: If we are only using one pipe, or no pipe at - # all, using select() or threads is unnecessary. - if [self.stdin, self.stdout, self.stderr].count(None) >= 2: - stdout = None - stderr = None - if self.stdin: - if input: - self.stdin.write(input) - self.stdin.close() - elif self.stdout: - stdout = self.stdout.read() - elif self.stderr: - stderr = self.stderr.read() - self.wait() - return (stdout, stderr) - - return self._communicate(input) - - - if mswindows: - # - # Windows methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - if stdin is None and stdout is None and stderr is None: - return (None, None, None, None, None, None) - - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - p2cread = GetStdHandle(STD_INPUT_HANDLE) - elif stdin == PIPE: - p2cread, p2cwrite = CreatePipe(None, 0) - # Detach and turn into fd - p2cwrite = p2cwrite.Detach() - p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) - elif isinstance(stdin, int): - p2cread = msvcrt.get_osfhandle(stdin) - else: - # Assuming file-like object - p2cread = msvcrt.get_osfhandle(stdin.fileno()) - p2cread = self._make_inheritable(p2cread) - - if stdout is None: - c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) - elif stdout == PIPE: - c2pread, c2pwrite = CreatePipe(None, 0) - # Detach and turn into fd - c2pread = c2pread.Detach() - c2pread = msvcrt.open_osfhandle(c2pread, 0) - elif isinstance(stdout, int): - c2pwrite = msvcrt.get_osfhandle(stdout) - else: - # Assuming file-like object - c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) - c2pwrite = self._make_inheritable(c2pwrite) - - if stderr is None: - errwrite = GetStdHandle(STD_ERROR_HANDLE) - elif stderr == PIPE: - errread, errwrite = CreatePipe(None, 0) - # Detach and turn into fd - errread = errread.Detach() - errread = msvcrt.open_osfhandle(errread, 0) - elif stderr == STDOUT: - errwrite = c2pwrite - elif isinstance(stderr, int): - errwrite = msvcrt.get_osfhandle(stderr) - else: - # Assuming file-like object - errwrite = msvcrt.get_osfhandle(stderr.fileno()) - errwrite = self._make_inheritable(errwrite) - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _make_inheritable(self, handle): - """Return a duplicate of handle, which is inheritable""" - return DuplicateHandle(GetCurrentProcess(), handle, - GetCurrentProcess(), 0, 1, - DUPLICATE_SAME_ACCESS) - - - def _find_w9xpopen(self): - """Find and return absolut path to w9xpopen.exe""" - w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - # Eeek - file-not-found - possibly an embedding - # situation - see if we can locate it in sys.exec_prefix - w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - raise RuntimeError("Cannot locate w9xpopen.exe, which is " - "needed for Popen to work with your " - "shell or platform.") - return w9xpopen - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (MS Windows version)""" - - if not isinstance(args, types.StringTypes): - args = list2cmdline(args) - - # Process startup details - if startupinfo is None: - startupinfo = STARTUPINFO() - if None not in (p2cread, c2pwrite, errwrite): - startupinfo.dwFlags |= STARTF_USESTDHANDLES - startupinfo.hStdInput = p2cread - startupinfo.hStdOutput = c2pwrite - startupinfo.hStdError = errwrite - - if shell: - startupinfo.dwFlags |= STARTF_USESHOWWINDOW - startupinfo.wShowWindow = SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") - args = comspec + " /c " + args - if (GetVersion() >= 0x80000000L or - os.path.basename(comspec).lower() == "command.com"): - # Win9x, or using command.com on NT. We need to - # use the w9xpopen intermediate program. For more - # information, see KB Q150956 - # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) - w9xpopen = self._find_w9xpopen() - args = '"%s" %s' % (w9xpopen, args) - # Not passing CREATE_NEW_CONSOLE has been known to - # cause random failures on win9x. Specifically a - # dialog: "Your program accessed mem currently in - # use at xxx" and a hopeful warning about the - # stability of your system. Cost is Ctrl+C wont - # kill children. - creationflags |= CREATE_NEW_CONSOLE - - # Start the process - try: - hp, ht, pid, tid = CreateProcess(executable, args, - # no special security - None, None, - # must inherit handles to pass std - # handles - 1, - creationflags, - env, - cwd, - startupinfo) - except pywintypes.error, e: - # Translate pywintypes.error to WindowsError, which is - # a subclass of OSError. FIXME: We should really - # translate errno using _sys_errlist (or simliar), but - # how can this be done from Python? - raise WindowsError(*e.args) - - # Retain the process handle, but close the thread handle - self._child_created = True - self._handle = hp - self.pid = pid - ht.Close() - - # Child is launched. Close the parent's copy of those pipe - # handles that only the child should have open. You need - # to make sure that no handles to the write end of the - # output pipe are maintained in this process or else the - # pipe will not close when the child process exits and the - # ReadFile will hang. - if p2cread is not None: - p2cread.Close() - if c2pwrite is not None: - c2pwrite.Close() - if errwrite is not None: - errwrite.Close() - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - obj = WaitForSingleObject(self._handle, INFINITE) - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def _readerthread(self, fh, buffer): - buffer.append(fh.read()) - - - def _communicate(self, input): - stdout = None # Return - stderr = None # Return - - if self.stdout: - stdout = [] - stdout_thread = threading.Thread(target=self._readerthread, - args=(self.stdout, stdout)) - stdout_thread.setDaemon(True) - stdout_thread.start() - if self.stderr: - stderr = [] - stderr_thread = threading.Thread(target=self._readerthread, - args=(self.stderr, stderr)) - stderr_thread.setDaemon(True) - stderr_thread.start() - - if self.stdin: - if input is not None: - self.stdin.write(input) - self.stdin.close() - - if self.stdout: - stdout_thread.join() - if self.stderr: - stderr_thread.join() - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = stdout[0] - if stderr is not None: - stderr = stderr[0] - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - else: - # - # POSIX methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - pass - elif stdin == PIPE: - p2cread, p2cwrite = os.pipe() - elif isinstance(stdin, int): - p2cread = stdin - else: - # Assuming file-like object - p2cread = stdin.fileno() - - if stdout is None: - pass - elif stdout == PIPE: - c2pread, c2pwrite = os.pipe() - elif isinstance(stdout, int): - c2pwrite = stdout - else: - # Assuming file-like object - c2pwrite = stdout.fileno() - - if stderr is None: - pass - elif stderr == PIPE: - errread, errwrite = os.pipe() - elif stderr == STDOUT: - errwrite = c2pwrite - elif isinstance(stderr, int): - errwrite = stderr - else: - # Assuming file-like object - errwrite = stderr.fileno() - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _set_cloexec_flag(self, fd): - try: - cloexec_flag = fcntl.FD_CLOEXEC - except AttributeError: - cloexec_flag = 1 - - old = fcntl.fcntl(fd, fcntl.F_GETFD) - fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) - - - def _close_fds(self, but): - for i in xrange(3, MAXFD): - if i == but: - continue - try: - os.close(i) - except: - pass - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (POSIX version)""" - - if isinstance(args, types.StringTypes): - args = [args] - - if shell: - args = ["/bin/sh", "-c"] + args - - if executable is None: - executable = args[0] - - # For transferring possible exec failure from child to parent - # The first char specifies the exception type: 0 means - # OSError, 1 means some other error. - errpipe_read, errpipe_write = os.pipe() - self._set_cloexec_flag(errpipe_write) - - self.pid = os.fork() - self._child_created = True - if self.pid == 0: - # Child - try: - # Close parent's pipe ends - if p2cwrite: - os.close(p2cwrite) - if c2pread: - os.close(c2pread) - if errread: - os.close(errread) - os.close(errpipe_read) - - # Dup fds for child - if p2cread: - os.dup2(p2cread, 0) - if c2pwrite: - os.dup2(c2pwrite, 1) - if errwrite: - os.dup2(errwrite, 2) - - # Close pipe fds. Make sure we don't close the same - # fd more than once, or standard fds. - if p2cread: - os.close(p2cread) - if c2pwrite and c2pwrite not in (p2cread,): - os.close(c2pwrite) - if errwrite and errwrite not in (p2cread, c2pwrite): - os.close(errwrite) - - # Close all other fds, if asked for - if close_fds: - self._close_fds(but=errpipe_write) - - if cwd is not None: - os.chdir(cwd) - - if preexec_fn: - apply(preexec_fn) - - if env is None: - os.execvp(executable, args) - else: - os.execvpe(executable, args, env) - - except: - exc_type, exc_value, tb = sys.exc_info() - # Save the traceback and attach it to the exception object - exc_lines = traceback.format_exception(exc_type, - exc_value, - tb) - exc_value.child_traceback = ''.join(exc_lines) - os.write(errpipe_write, pickle.dumps(exc_value)) - - # This exitcode won't be reported to applications, so it - # really doesn't matter what we return. - os._exit(255) - - # Parent - os.close(errpipe_write) - if p2cread and p2cwrite: - os.close(p2cread) - if c2pwrite and c2pread: - os.close(c2pwrite) - if errwrite and errread: - os.close(errwrite) - - # Wait for exec to fail or succeed; possibly raising exception - data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB - os.close(errpipe_read) - if data != "": - os.waitpid(self.pid, 0) - child_exception = pickle.loads(data) - raise child_exception - - - def _handle_exitstatus(self, sts): - if os.WIFSIGNALED(sts): - self.returncode = -os.WTERMSIG(sts) - elif os.WIFEXITED(sts): - self.returncode = os.WEXITSTATUS(sts) - else: - # Should never happen - raise RuntimeError("Unknown child exit status!") - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - try: - pid, sts = os.waitpid(self.pid, os.WNOHANG) - if pid == self.pid: - self._handle_exitstatus(sts) - except os.error: - if _deadstate is not None: - self.returncode = _deadstate - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - pid, sts = os.waitpid(self.pid, 0) - self._handle_exitstatus(sts) - return self.returncode - - - def _communicate(self, input): - read_set = [] - write_set = [] - stdout = None # Return - stderr = None # Return - - if self.stdin: - # Flush stdio buffer. This might block, if the user has - # been writing to .stdin in an uncontrolled fashion. - self.stdin.flush() - if input: - write_set.append(self.stdin) - else: - self.stdin.close() - if self.stdout: - read_set.append(self.stdout) - stdout = [] - if self.stderr: - read_set.append(self.stderr) - stderr = [] - - while read_set or write_set: - rlist, wlist, xlist = select.select(read_set, write_set, []) - - if self.stdin in wlist: - # When select has indicated that the file is writable, - # we can write up to PIPE_BUF bytes without risk - # blocking. POSIX defines PIPE_BUF >= 512 - bytes_written = os.write(self.stdin.fileno(), input[:512]) - input = input[bytes_written:] - if not input: - self.stdin.close() - write_set.remove(self.stdin) - - if self.stdout in rlist: - data = os.read(self.stdout.fileno(), 1024) - if data == "": - self.stdout.close() - read_set.remove(self.stdout) - stdout.append(data) - - if self.stderr in rlist: - data = os.read(self.stderr.fileno(), 1024) - if data == "": - self.stderr.close() - read_set.remove(self.stderr) - stderr.append(data) - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = ''.join(stdout) - if stderr is not None: - stderr = ''.join(stderr) - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - -def _demo_posix(): - # - # Example 1: Simple redirection: Get process list - # - plist = Popen(["ps"], stdout=PIPE).communicate()[0] - print "Process list:" - print plist - - # - # Example 2: Change uid before executing child - # - if os.getuid() == 0: - p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) - p.wait() - - # - # Example 3: Connecting several subprocesses - # - print "Looking for 'hda'..." - p1 = Popen(["dmesg"], stdout=PIPE) - p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 4: Catch execution error - # - print - print "Trying a weird file..." - try: - print Popen(["/this/path/does/not/exist"]).communicate() - except OSError, e: - if e.errno == errno.ENOENT: - print "The file didn't exist. I thought so..." - print "Child traceback:" - print e.child_traceback - else: - print "Error", e.errno - else: - print >>sys.stderr, "Gosh. No error." - - -def _demo_windows(): - # - # Example 1: Connecting several subprocesses - # - print "Looking for 'PROMPT' in set output..." - p1 = Popen("set", stdout=PIPE, shell=True) - p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 2: Simple execution of program - # - print "Executing calc..." - p = Popen("calc") - p.wait() - - -if __name__ == "__main__": - if mswindows: - _demo_windows() - else: - _demo_posix() diff --git a/certmaster/minion/utils.py b/certmaster/minion/utils.py deleted file mode 100755 index a7ea788..0000000 --- a/certmaster/minion/utils.py +++ /dev/null @@ -1,207 +0,0 @@ -""" -Copyright 2007, Red Hat, Inc -see AUTHORS - -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 os -import socket -import string -import sys -import time -import traceback -import xmlrpclib -import glob -import traceback - -import codes -from func import certs -from func.config import read_config -from func.commonconfig import FuncdConfig -from func import logger - -# "localhost" is a lame hostname to use for a key, so try to get -# a more meaningful hostname. We do this by connecting to the certmaster -# and seeing what interface/ip it uses to make that connection, and looking -# up the hostname for that. -def get_hostname(): - - # FIXME: this code ignores http proxies (which granted, we don't - # support elsewhere either. It also hardcodes the port number - # for the certmaster for now - hostname = None - hostname = socket.gethostname() - try: - ip = socket.gethostbyname(hostname) - except: - return hostname - if ip != "127.0.0.1": - return hostname - - - config_file = '/etc/func/minion.conf' - config = read_config(config_file, FuncdConfig) - - server = config.certmaster - port = 51235 - - try: - s = socket.socket() - s.settimeout(5) - s.connect((server, port)) - (intf, port) = s.getsockname() - hostname = socket.gethostbyaddr(intf)[0] - s.close() - except: - s.close() - raise - - return hostname - - - -def create_minion_keys(): - config_file = '/etc/func/minion.conf' - config = read_config(config_file, FuncdConfig) - cert_dir = config.cert_dir - master_uri = 'http://%s:51235/' % config.certmaster - hn = get_hostname() - - if hn is None: - raise codes.FuncException("Could not determine a hostname other than localhost") - - key_file = '%s/%s.pem' % (cert_dir, hn) - csr_file = '%s/%s.csr' % (cert_dir, hn) - cert_file = '%s/%s.cert' % (cert_dir, hn) - ca_cert_file = '%s/ca.cert' % cert_dir - - - if os.path.exists(cert_file) and os.path.exists(ca_cert_file): - return - - keypair = None - try: - if not os.path.exists(cert_dir): - os.makedirs(cert_dir) - if not os.path.exists(key_file): - keypair = certs.make_keypair(dest=key_file) - if not os.path.exists(csr_file): - if not keypair: - keypair = certs.retrieve_key_from_file(key_file) - csr = certs.make_csr(keypair, dest=csr_file) - except Exception, e: - traceback.print_exc() - raise codes.FuncException, "Could not create local keypair or csr for minion funcd session" - - result = False - log = logger.Logger().logger - while not result: - try: - log.debug("submitting CSR to certmaster %s" % master_uri) - result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri) - except socket.gaierror, e: - raise codes.FuncException, "Could not locate certmaster at %s" % master_uri - - # logging here would be nice - if not result: - log.warning("no response from certmaster %s, sleeping 10 seconds" % master_uri) - time.sleep(10) - - - if result: - log.debug("received certificate from certmaster %s, storing" % master_uri) - cert_fd = os.open(cert_file, os.O_RDWR|os.O_CREAT, 0644) - os.write(cert_fd, cert_string) - os.close(cert_fd) - - ca_cert_fd = os.open(ca_cert_file, os.O_RDWR|os.O_CREAT, 0644) - os.write(ca_cert_fd, ca_cert_string) - os.close(ca_cert_fd) - -def submit_csr_to_master(csr_file, master_uri): - """" - gets us our cert back from the certmaster.wait_for_cert() method - takes csr_file as path location and master_uri - returns Bool, str(cert), str(ca_cert) - """ - - fo = open(csr_file) - csr = fo.read() - s = xmlrpclib.ServerProxy(master_uri) - - return s.wait_for_cert(csr) - - -# this is kind of handy, so keep it around for now -# but we really need to fix out server side logging and error -# reporting so we don't need it -def trace_me(): - x = traceback.extract_stack() - bar = string.join(traceback.format_list(x)) - return bar - - -def daemonize(pidfile=None): - """ - Daemonize this process with the UNIX double-fork trick. - Writes the new PID to the provided file name if not None. - """ - - print pidfile - pid = os.fork() - if pid > 0: - sys.exit(0) - os.setsid() - os.umask(0) - pid = os.fork() - - - if pid > 0: - if pidfile is not None: - open(pidfile, "w").write(str(pid)) - sys.exit(0) - -def get_acls_from_config(acldir='/etc/func/minion-acl.d'): - """ - takes a dir of .acl files - returns a dict of hostname+hash = [methods, to, run] - - """ - - acls = {} - if not os.path.exists(acldir): - print 'acl dir does not exist: %s' % acldir - return acls - - # get the set of files - acl_glob = '%s/*.acl' % acldir - files = glob.glob(acl_glob) - - for acl_file in files: - - try: - fo = open(acl_file, 'r') - except (IOError, OSError), e: - print 'cannot open acl config file: %s - %s' % (acl_file, e) - continue - - for line in fo.readlines(): - if line.startswith('#'): continue - if line.strip() == '': continue - line = line.replace('\n', '') - (host, methods) = line.split('=') - host = host.strip().lower() - methods = methods.strip() - methods = methods.replace(',',' ') - methods = methods.split() - if not acls.has_key(host): - acls[host] = [] - acls[host].extend(methods) - - return acls diff --git a/certmaster/overlord/.forkbomb.py.swp b/certmaster/overlord/.forkbomb.py.swp deleted file mode 100644 index 242b6f4..0000000 Binary files a/certmaster/overlord/.forkbomb.py.swp and /dev/null differ diff --git a/certmaster/overlord/Makefile b/certmaster/overlord/Makefile deleted file mode 100755 index f2bc6c4..0000000 --- a/certmaster/overlord/Makefile +++ /dev/null @@ -1,18 +0,0 @@ - - -PYFILES = $(wildcard *.py) - -PYCHECKER = /usr/bin/pychecker -PYFLAKES = /usr/bin/pyflakes - -clean:: - @rm -fv *.pyc *~ .*~ *.pyo - @find . -name .\#\* -exec rm -fv {} \; - @rm -fv *.rpm - - -pychecker:: - @$(PYCHECKER) $(PYFILES) || exit 0 - -pyflakes:: - @$(PYFLAKES) $(PYFILES) || exit 0 diff --git a/certmaster/overlord/__init__.py b/certmaster/overlord/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/certmaster/overlord/client.py b/certmaster/overlord/client.py deleted file mode 100755 index cf1009c..0000000 --- a/certmaster/overlord/client.py +++ /dev/null @@ -1,336 +0,0 @@ -## -## func command line interface & client lib -## -## Copyright 2007, Red Hat, Inc -## Michael DeHaan -## +AUTHORS -## -## 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 sys -import glob -import os - -from func.commonconfig import CMConfig -from func.config import read_config, CONFIG_FILE - -import sslclient - -import command -import groups -import func.forkbomb as forkbomb -import func.jobthing as jobthing -import func.utils as utils -from func.CommonErrors import * - -# =================================== -# defaults -# TO DO: some of this may want to come from config later - -DEFAULT_PORT = 51234 -FUNC_USAGE = "Usage: %s [ --help ] [ --verbose ] target.example.org module method arg1 [...]" - -# =================================== - -class CommandAutomagic(object): - """ - This allows a client object to act as if it were one machine, when in - reality it represents many. - """ - - def __init__(self, clientref, base, nforks=1): - self.base = base - self.clientref = clientref - self.nforks = nforks - - def __getattr__(self,name): - base2 = self.base[:] - base2.append(name) - return CommandAutomagic(self.clientref, base2, self.nforks) - - def __call__(self, *args): - if not self.base: - raise AttributeError("something wrong here") - if len(self.base) < 2: - raise AttributeError("no method called: %s" % ".".join(self.base)) - module = self.base[0] - method = ".".join(self.base[1:]) - return self.clientref.run(module,method,args,nforks=self.nforks) - - -def get_groups(): - group_class = groups.Groups() - return group_class.get_groups() - - -def get_hosts_by_groupgoo(groups, groupgoo): - group_gloobs = groupgoo.split(':') - hosts = [] - for group_gloob in group_gloobs: - if not group_gloob[0] == "@": - continue - if groups.has_key(group_gloob[1:]): - hosts = hosts + groups[group_gloob[1:]] - else: - print "group %s not defined" % group_gloob - return hosts - -# =================================== -# this is a module level def so we can use it and isServer() from -# other modules with a Client class -def expand_servers(spec, port=51234, noglobs=None, verbose=None, just_fqdns=False): - """ - Given a regex/blob of servers, expand to a list - of server ids. - """ - - - # FIXME: we need to refactor expand_servers, it seems to do - # weird things, reload the config and groups config everytime it's - # called for one, which may or may not be bad... -akl - config = read_config(CONFIG_FILE, CMConfig) - - if noglobs: - if not just_fqdns: - return [ "https://%s:%s" % (spec, port) ] - else: - return spec - - group_dict = get_groups() - - all_hosts = [] - all_certs = [] - seperate_gloobs = spec.split(";") - - new_hosts = get_hosts_by_groupgoo(group_dict, spec) - - seperate_gloobs = spec.split(";") - seperate_gloobs = seperate_gloobs + new_hosts - for each_gloob in seperate_gloobs: - actual_gloob = "%s/%s.cert" % (config.certroot, each_gloob) - certs = glob.glob(actual_gloob) - for cert in certs: - all_certs.append(cert) - host = cert.replace(config.certroot,"")[1:-5] - all_hosts.append(host) - - all_urls = [] - for x in all_hosts: - if not just_fqdns: - all_urls.append("https://%s:%s" % (x, port)) - else: - all_urls.append(x) - - if verbose and len(all_urls) == 0: - sys.stderr.write("no hosts matched\n") - - return all_urls - - -# does the hostnamegoo actually expand to anything? -def isServer(server_string): - servers = expand_servers(server_string) - if len(servers) > 0: - return True - return False - - -class Client(object): - - def __init__(self, server_spec, port=DEFAULT_PORT, interactive=False, - verbose=False, noglobs=False, nforks=1, config=None, async=False, init_ssl=True): - """ - Constructor. - @server_spec -- something like "*.example.org" or "foosball" - @port -- is the port where all funcd processes should be contacted - @verbose -- whether to print unneccessary things - @noglobs -- specifies server_spec is not a glob, and run should return single values - @config -- optional config object - """ - self.config = config - if config is None: - self.config = read_config(CONFIG_FILE, CMConfig) - - - self.server_spec = server_spec - self.port = port - self.verbose = verbose - self.interactive = interactive - self.noglobs = noglobs - self.nforks = nforks - self.async = async - - self.servers = expand_servers(self.server_spec, port=self.port, noglobs=self.noglobs,verbose=self.verbose) - - if init_ssl: - self.setup_ssl() - - def setup_ssl(self, client_key=None, client_cert=None, ca=None): - # defaults go: - # certmaster key, cert, ca - # funcd key, cert, ca - # raise FuncClientError - ol_key = '%s/funcmaster.key' % self.config.cadir - ol_crt = '%s/funcmaster.crt' % self.config.cadir - myname = utils.get_hostname() - # maybe /etc/pki/func is a variable somewhere? - fd_key = '/etc/pki/func/%s.pem' % myname - fd_crt = '/etc/pki/func/%s.cert' % myname - self.ca = '%s/funcmaster.crt' % self.config.cadir - if client_key and client_cert and ca: - if (os.access(client_key, os.R_OK) and os.access(client_cert, os.R_OK) - and os.access(ca, os.R_OK)): - self.key = client_key - self.cert = client_cert - self.ca = ca - # otherwise fall through our defaults - elif os.access(ol_key, os.R_OK) and os.access(ol_crt, os.R_OK): - self.key = ol_key - self.cert = ol_crt - elif os.access(fd_key, os.R_OK) and os.access(fd_crt, os.R_OK): - self.key = fd_key - self.cert = fd_crt - else: - raise Func_Client_Exception, 'Cannot read ssl credentials: ssl, cert, ca' - - - - - def __getattr__(self, name): - """ - This getattr allows manipulation of the object as if it were - a XMLRPC handle to a single machine, when in reality it is a handle - to an unspecified number of machines. - - So, it enables stuff like this: - - Client("*.example.org").yum.install("foo") - - # WARNING: any missing values in Client's source will yield - # strange errors with this engaged. Be aware of that. - """ - - return CommandAutomagic(self, [name], self.nforks) - - # ----------------------------------------------- - - def job_status(self, jobid): - """ - Use this to acquire status from jobs when using run with async client handles - """ - return jobthing.job_status(jobid, client_class=Client) - - # ----------------------------------------------- - - def run(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 - returns. - - The returns may include exception objects. - If Client() was constructed with noglobs=True, the return is instead - just a single value, not a hash. - """ - - results = {} - - def process_server(bucketnumber, buckets, server): - - conn = sslclient.FuncServer(server, self.key, self.cert, self.ca ) - # conn = xmlrpclib.ServerProxy(server) - - if self.interactive: - sys.stderr.write("on %s running %s %s (%s)\n" % (server, - module, method, ",".join(args))) - - # FIXME: support userland command subclassing only if a module - # is present, otherwise run as follows. -- MPD - - try: - # 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) - - # async calling signature has an "imaginary" prefix - # so async.abc.def does abc.def as a background task. - # see Wiki docs for details - if self.async: - meth = "async.%s" % meth - - # this is the point at which we make the remote call. - retval = getattr(conn, meth)(*args[:]) - - if self.interactive: - print retval - except Exception, e: - (t, v, tb) = sys.exc_info() - retval = utils.nice_exception(t,v,tb) - if self.interactive: - sys.stderr.write("remote exception on %s: %s\n" % - (server, str(e))) - - if self.noglobs: - return retval - else: - left = server.rfind("/")+1 - 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 - if not self.async: - results = forkbomb.batch_run(self.servers, process_server, nforks) - else: - results = jobthing.batch_run(self.servers, process_server, nforks) - else: - # no need to go through the fork code, we can do this directly - results = {} - for x in self.servers: - (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] - results = process_server(0, 0, expanded) - - return results - - # ----------------------------------------------- - - def cli_return(self,results): - """ - As the return code list could return strings and exceptions - and all sorts of crazy stuff, reduce it down to a simple - integer return. It may not be useful but we need one. - """ - numbers = [] - for x in results.keys(): - # faults are the most important - if type(x) == Exception: - return -911 - # then pay attention to numbers - if type(x) == int: - numbers.append(x) - - # if there were no numbers, assume 0 - if len(numbers) == 0: - return 0 - - # if there were numbers, return the highest - # (presumably the worst error code - max = -9999 - for x in numbers: - if x > max: - max = x - return max diff --git a/certmaster/overlord/command.py b/certmaster/overlord/command.py deleted file mode 100644 index 7fb7de4..0000000 --- a/certmaster/overlord/command.py +++ /dev/null @@ -1,287 +0,0 @@ -# -*- Mode: Python; test-case-name: test_command -*- -# vi:si:et:sw=4:sts=4:ts=4 - -# This file is released under the standard PSF license. -# -# from MOAP - https://thomas.apestaart.org/moap/trac -# written by Thomas Vander Stichele (thomas at apestaart dot org) -# - -""" -Command class. -""" - -import optparse -import sys - -from func.config import read_config, CONFIG_FILE -from func.commonconfig import CMConfig - -class CommandHelpFormatter(optparse.IndentedHelpFormatter): - """ - I format the description as usual, but add an overview of commands - after it if there are any, formatted like the options. - """ - _commands = None - - def addCommand(self, name, description): - if self._commands is None: - self._commands = {} - self._commands[name] = description - - ### override parent method - def format_description(self, description): - # textwrap doesn't allow for a way to preserve double newlines - # to separate paragraphs, so we do it here. - blocks = description.split('\n\n') - rets = [] - - for block in blocks: - rets.append(optparse.IndentedHelpFormatter.format_description(self, - block)) - ret = "\n".join(rets) - if self._commands: - commandDesc = [] - commandDesc.append("commands:") - keys = self._commands.keys() - keys.sort() - length = 0 - for key in keys: - if len(key) > length: - length = len(key) - for name in keys: - format = " %-" + "%d" % length + "s %s" - commandDesc.append(format % (name, self._commands[name])) - ret += "\n" + "\n".join(commandDesc) + "\n" - return ret - -class CommandOptionParser(optparse.OptionParser): - """ - I parse options as usual, but I explicitly allow setting stdout - so that our print_help() method (invoked by default with -h/--help) - defaults to writing there. - """ - _stdout = sys.stdout - - def set_stdout(self, stdout): - self._stdout = stdout - - # we're overriding the built-in file, but we need to since this is - # the signature from the base class - __pychecker__ = 'no-shadowbuiltin' - def print_help(self, file=None): - # we are overriding a parent method so we can't do anything about file - __pychecker__ = 'no-shadowbuiltin' - if file is None: - file = self._stdout - file.write(self.format_help()) - -class Command: - """ - I am a class that handles a command for a program. - Commands can be nested underneath a command for further processing. - - @cvar name: name of the command, lowercase - @cvar aliases: list of alternative lowercase names recognized - @type aliases: list of str - @cvar usage: short one-line usage string; - %command gets expanded to a sub-command or [commands] - as appropriate - @cvar summary: short one-line summary of the command - @cvar description: longer paragraph explaining the command - @cvar subCommands: dict of name -> commands below this command - @type subCommands: dict of str -> L{Command} - """ - name = None - aliases = None - usage = None - summary = None - description = None - parentCommand = None - subCommands = None - subCommandClasses = None - aliasedSubCommands = None - - def __init__(self, parentCommand=None, stdout=sys.stdout, - stderr=sys.stderr): - """ - Create a new command instance, with the given parent. - Allows for redirecting stdout and stderr if needed. - This redirection will be passed on to child commands. - """ - if not self.name: - self.name = str(self.__class__).split('.')[-1].lower() - self.stdout = stdout - self.stderr = stderr - self.parentCommand = parentCommand - - self.config = read_config(CONFIG_FILE, CMConfig) - - # create subcommands if we have them - self.subCommands = {} - self.aliasedSubCommands = {} - if self.subCommandClasses: - for C in self.subCommandClasses: - c = C(self, stdout=stdout, stderr=stderr) - self.subCommands[c.name] = c - if c.aliases: - for alias in c.aliases: - self.aliasedSubCommands[alias] = c - - # create our formatter and add subcommands if we have them - formatter = CommandHelpFormatter() - if self.subCommands: - for name, command in self.subCommands.items(): - formatter.addCommand(name, command.summary or - command.description) - - # expand %command for the bottom usage - usage = self.usage or self.name - if usage.find("%command") > -1: - usage = usage.split("%command")[0] + '[command]' - usages = [usage, ] - - # FIXME: abstract this into getUsage that takes an optional - # parentCommand on where to stop recursing up - # useful for implementing subshells - - # walk the tree up for our usage - c = self.parentCommand - while c: - usage = c.usage or c.name - if usage.find(" %command") > -1: - usage = usage.split(" %command")[0] - usages.append(usage) - c = c.parentCommand - usages.reverse() - usage = " ".join(usages) - - # create our parser - description = self.description or self.summary - self.parser = CommandOptionParser( - usage=usage, description=description, - formatter=formatter) - self.parser.set_stdout(self.stdout) - self.parser.disable_interspersed_args() - - # allow subclasses to add options - self.addOptions() - - def addOptions(self): - """ - Override me to add options to the parser. - """ - pass - - def do(self, args): - """ - Override me to implement the functionality of the command. - """ - pass - - def parse(self, argv): - """ - Parse the given arguments and act on them. - - @rtype: int - @returns: an exit code - """ - self.options, args = self.parser.parse_args(argv) - - # FIXME: make handleOptions not take options, since we store it - # in self.options now - ret = self.handleOptions(self.options) - if ret: - return ret - - # handle pleas for help - if args and args[0] == 'help': - self.debug('Asked for help, args %r' % args) - - # give help on current command if only 'help' is passed - if len(args) == 1: - self.outputHelp() - return 0 - - # complain if we were asked for help on a subcommand, but we don't - # have any - if not self.subCommands: - self.stderr.write('No subcommands defined.') - self.parser.print_usage(file=self.stderr) - self.stderr.write( - "Use --help to get more information about this command.\n") - return 1 - - # rewrite the args the other way around; - # help doap becomes doap help so it gets deferred to the doap - # command - args = [args[1], args[0]] - - - # if we have args that we need to deal with, do it now - # before we start looking for subcommands - self.handleArguments(args) - - # if we don't have subcommands, defer to our do() method - if not self.subCommands: - ret = self.do(args) - - # if everything's fine, we return 0 - if not ret: - ret = 0 - - return ret - - - # if we do have subcommands, defer to them - try: - command = args[0] - except IndexError: - self.parser.print_usage(file=self.stderr) - self.stderr.write( - "Use --help to get a list of commands.\n") - return 1 - - if command in self.subCommands.keys(): - return self.subCommands[command].parse(args[1:]) - - if self.aliasedSubCommands: - if command in self.aliasedSubCommands.keys(): - return self.aliasedSubCommands[command].parse(args[1:]) - - self.stderr.write("Unknown command '%s'.\n" % command) - return 1 - - def outputHelp(self): - """ - Output help information. - """ - self.parser.print_help(file=self.stderr) - - def outputUsage(self): - """ - Output usage information. - Used when the options or arguments were missing or wrong. - """ - self.parser.print_usage(file=self.stderr) - - def handleOptions(self, options): - """ - Handle the parsed options. - """ - pass - - def handleArguments(self, arguments): - """ - Handle the parsed arguments. - """ - pass - - def getRootCommand(self): - """ - Return the top-level command, which is typically the program. - """ - c = self - while c.parentCommand: - c = c.parentCommand - return c diff --git a/certmaster/overlord/func_command.py b/certmaster/overlord/func_command.py deleted file mode 100644 index 4cec8a0..0000000 --- a/certmaster/overlord/func_command.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/python - -## func command line interface & client lib -## -## Copyright 2007,2008 Red Hat, Inc -## +AUTHORS -## -## 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 sys - - -import command - -#FIXME: need a plug-in runtime module loader here -from cmd_modules import call -from cmd_modules import show -from cmd_modules import copyfile -from cmd_modules import listminions -from cmd_modules import ping - -from func.overlord import client - -class FuncCommandLine(command.Command): - name = "func" - usage = "func is the commandline interface to a func minion" - - subCommandClasses = [call.Call, show.Show, - copyfile.CopyFile, listminions.ListMinions, ping.Ping] - - def __init__(self): - - command.Command.__init__(self) - - def do(self, args): - pass - - def addOptions(self): - self.parser.add_option('', '--version', action="store_true", - help="show version information") - - # just some ugly goo to try to guess if arg[1] is hostnamegoo or - # a command name - def _isGlob(self, str): - if str.find("*") or str.find("?") or str.find("[") or str.find("]"): - return True - return False - - def handleArguments(self, args): - if len(args) < 2: - print "see the func manpage for usage" - sys.exit(411) - server_string = args[0] - # try to be clever about this for now - if client.isServer(server_string) or self._isGlob(server_string): - self.server_spec = server_string - args.pop(0) - # if it doesn't look like server, assume it - # is a sub command? that seems wrong, what about - # typo's and such? How to catch that? -akl - # maybe a class variable self.data on Command? - - def handleOptions(self, options): - if options.version: - #FIXME - print "version is NOT IMPLEMENTED YET" diff --git a/certmaster/overlord/groups.py b/certmaster/overlord/groups.py deleted file mode 100644 index 8eaf28e..0000000 --- a/certmaster/overlord/groups.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/python - -## func command line interface & client lib -## -## Copyright 2007,2008 Red Hat, Inc -## Adrian Likins -## +AUTHORS -## -## 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. -## - - -# this module lets you define groups of systems to work with from the -# commandline. It uses an "ini" style config parser like: - -#[groupname] -#host = foobar, baz, blip -#subgroup = blippy - - -import ConfigParser -import os - - -class Groups(object): - - def __init__(self, filename="/etc/func/groups"): - self.filename = filename - self.group_names = {} - self.groups = {} - self.__parse() - - def __parse(self): - - self.cp = ConfigParser.SafeConfigParser() - self.cp.read(self.filename) - - for section in self.cp.sections(): - self.add_group(section) - options = self.cp.options(section) - for option in options: - if option == "host": - self.add_hosts_to_group(section, self.cp.get(section, option)) - if option == "subgroup": - pass - - - def show(self): - print self.cp.sections() - print self.groups - - def add_group(self, group): - pass - - def __parse_hoststrings(self, hoststring): - hosts = [] - bits = hoststring.split(';') - for bit in bits: - blip = bit.strip().split(' ') - for host in blip: - if host not in hosts: - hosts.append(host.strip()) - - return hosts - - def add_hosts_to_group(self, group, hoststring): - hosts = self.__parse_hoststrings(hoststring) - for host in hosts: - self.add_host_to_group(group, host) - - - - def add_host_to_group(self, group, host): - if not self.groups.has_key(group): - self.groups[group] = [] - self.groups[group].append(host) - - def get_groups(self): - return self.groups - - - -def main(): - g = Groups("/tmp/testgroups") - print g.show() - - - -if __name__ == "__main__": - main() diff --git a/certmaster/overlord/highlevel.py b/certmaster/overlord/highlevel.py deleted file mode 100644 index 977dcb4..0000000 --- a/certmaster/overlord/highlevel.py +++ /dev/null @@ -1,40 +0,0 @@ -## -## func higher level API interface for overlord side operations -## -## Copyright 2007, Red Hat, Inc -## Michael DeHaan -## +AUTHORS -## -## 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 exceptions - -class HigherLevelObject: - - def __init__(self, client): - self.client = client_handle - - def modify(self, key, properties): - """ - Modify or create an entity named key. - properties should contain all neccessary fields. - """ - raise exceptions.NotImplementedError - - def remove(self, key): - """ - Remove an entity named key. - """ - raise exceptions.NotImplementedError - - def list(self): - """ - List all objects - """ - raise exceptions.NotImplementedError diff --git a/certmaster/overlord/inventory.py b/certmaster/overlord/inventory.py deleted file mode 100755 index 8302a1c..0000000 --- a/certmaster/overlord/inventory.py +++ /dev/null @@ -1,191 +0,0 @@ -## -## func inventory app. -## use func to collect inventory data on anything, yes, anything -## -## Copyright 2007, Red Hat, Inc -## Michael DeHaan -## +AUTHORS -## -## 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 os.path -import time -import optparse -import sys -import pprint -import xmlrpclib -from func.minion import sub_process -import func.overlord.client as func_client -import func.utils as utils - -DEFAULT_TREE = "/var/lib/func/inventory/" - - -class FuncInventory(object): - - def __init__(self): - pass - - def run(self,args): - - p = optparse.OptionParser() - p.add_option("-v", "--verbose", - dest="verbose", - action="store_true", - help="provide extra output") - p.add_option("-s", "--server-spec", - dest="server_spec", - default="*", - help="run against specific servers, default: '*'") - p.add_option("-m", "--methods", - dest="methods", - default="inventory", - help="run inventory only on certain function names, default: 'inventory'") - p.add_option("-M", "--modules", - dest="modules", - default="all", - help="run inventory only on certain module names, default: 'all'") - p.add_option("-t", "--tree", - dest="tree", - default=DEFAULT_TREE, - help="output results tree here, default: %s" % DEFAULT_TREE) - p.add_option("-n", "--no-git", - dest="nogit", - action="store_true", - help="disable useful change tracking features") - p.add_option("-x", "--xmlrpc", dest="xmlrpc", - help="output data using XMLRPC format", - action="store_true") - p.add_option("-j", "--json", dest="json", - help="output data using JSON", - action="store_true") - - - (options, args) = p.parse_args(args) - self.options = options - - filtered_module_list = options.modules.split(",") - filtered_function_list = options.methods.split(",") - - self.git_setup(options) - - # see what modules each host provides (as well as what hosts we have) - host_methods = func_client.Client(options.server_spec).system.list_methods() - - # call all remote info methods and handle them - if options.verbose: - print "- scanning ..." - # for (host, modules) in host_modules.iteritems(): - - for (host, methods) in host_methods.iteritems(): - - if utils.is_error(methods): - print "-- connection refused: %s" % host - break - - for each_method in methods: - - #if type(each_method) == int: - # if self.options.verbose: - # print "-- connection refused: %s" % host - # break - - tokens = each_method.split(".") - module_name = ".".join(tokens[:-1]) - method_name = tokens[-1] - - if not "all" in filtered_module_list and not module_name in filtered_module_list: - continue - - if not "all" in filtered_function_list and not method_name in filtered_function_list: - continue - - client = func_client.Client(host,noglobs=True) # ,noglobs=True) - results = getattr(getattr(client,module_name),method_name)() - if self.options.verbose: - print "-- %s: running: %s %s" % (host, module_name, method_name) - self.save_results(options, host, module_name, method_name, results) - self.git_update(options) - return 1 - - def format_return(self, data): - """ - The call module supports multiple output return types, the default is pprint. - """ - - # special case... if the return is a string, just print it straight - if type(data) == str: - return data - - if self.options.xmlrpc: - return xmlrpclib.dumps((data,"")) - - if self.options.json: - try: - import simplejson - return simplejson.dumps(data) - except ImportError: - print "ERROR: json support not found, install python-simplejson" - sys.exit(1) - - return pprint.pformat(data) - - # FUTURE: skvidal points out that guest symlinking would be an interesting feature - - def save_results(self, options, host_name, module_name, method_name, results): - dirname = os.path.join(options.tree, host_name, module_name) - if not os.path.exists(dirname): - os.makedirs(dirname) - filename = os.path.join(dirname, method_name) - results_file = open(filename,"w+") - data = self.format_return(results) - results_file.write(data) - results_file.close() - - def git_setup(self,options): - if options.nogit: - return - if not os.path.exists("/usr/bin/git"): - print "git-core is not installed, so no change tracking is available." - print "use --no-git or, better, just install it." - sys.exit(411) - - if not os.path.exists(options.tree): - os.makedirs(options.tree) - dirname = os.path.join(options.tree, ".git") - if not os.path.exists(dirname): - if options.verbose: - print "- initializing git repo: %s" % options.tree - cwd = os.getcwd() - os.chdir(options.tree) - rc1 = sub_process.call(["/usr/bin/git", "init"], shell=False) - # FIXME: check rc's - os.chdir(cwd) - else: - if options.verbose: - print "- git already initialized: %s" % options.tree - - def git_update(self,options): - if options.nogit: - return - else: - if options.verbose: - print "- updating git" - mytime = time.asctime() - cwd = os.getcwd() - os.chdir(options.tree) - rc1 = sub_process.call(["/usr/bin/git", "add", "*" ], shell=False) - rc2 = sub_process.call(["/usr/bin/git", "commit", "-a", "-m", "Func-inventory update: %s" % mytime], shell=False) - # FIXME: check rc's - os.chdir(cwd) - - -if __name__ == "__main__": - inv = FuncInventory() - inv.run(sys.argv) diff --git a/certmaster/overlord/sslclient.py b/certmaster/overlord/sslclient.py deleted file mode 100755 index 3861bb8..0000000 --- a/certmaster/overlord/sslclient.py +++ /dev/null @@ -1,50 +0,0 @@ -import sys -import xmlrpclib -import urllib - -from func import SSLCommon - - -class SSL_Transport(xmlrpclib.Transport): - - user_agent = "pyOpenSSL_XMLRPC/%s - %s" % ('0.1', xmlrpclib.Transport.user_agent) - - def __init__(self, ssl_context, timeout=None, use_datetime=0): - if sys.version_info[:3] >= (2, 5, 0): - xmlrpclib.Transport.__init__(self, use_datetime) - self.ssl_ctx=ssl_context - self._timeout = timeout - - def make_connection(self, host): - # Handle username and password. - try: - host, extra_headers, x509 = self.get_host_info(host) - except AttributeError: - # Yay for Python 2.2 - pass - _host, _port = urllib.splitport(host) - return SSLCommon.HTTPS(_host, int(_port), ssl_context=self.ssl_ctx, timeout=self._timeout) - - -class SSLXMLRPCServerProxy(xmlrpclib.ServerProxy): - def __init__(self, uri, pkey_file, cert_file, ca_cert_file, timeout=None): - self.ctx = SSLCommon.CreateSSLContext(pkey_file, cert_file, ca_cert_file) - xmlrpclib.ServerProxy.__init__(self, uri, SSL_Transport(ssl_context=self.ctx, timeout=timeout)) - - -class FuncServer(SSLXMLRPCServerProxy): - def __init__(self, uri, pem=None, crt=None, ca=None): - self.pem = pem - self.crt = crt - self.ca = ca - - SSLXMLRPCServerProxy.__init__(self, uri, - self.pem, - self.crt, - self.ca) - - -if __name__ == "__main__": - s = SSLXMLRPCServerProxy('https://localhost:51234/', '/etc/pki/func/slave.pem', '/etc/pki/func/slave.cert', '/etc/pki/func/ca/funcmaster.crt') - f = s.ping(1, 2) - print f diff --git a/po/messages.pot~ b/po/messages.pot~ index b27ddbb..e69de29 100644 --- a/po/messages.pot~ +++ b/po/messages.pot~ @@ -1,27 +0,0 @@ -# func. -# Copyright (C) 2007 Red Hat, inc. -# This file is distributed under the same license as the func package. -# Adrian Likins , 2007. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: func 0.16-1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-02-06 12:52-0500\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: func/minion/module_loader.py:92 -#, python-format -msgid "Could not load %s module: %s" -msgstr "" - -#: func/minion/module_loader.py:97 -#, python-format -msgid "Could not load %s module" -msgstr "" diff --git a/setup.py b/setup.py index 203c405..bd1cf53 100644 --- a/setup.py +++ b/setup.py @@ -36,8 +36,6 @@ if __name__ == "__main__": package_dir = {"%s" % NAME: "%s" % NAME }, packages = ["%s" % NAME, - "%s/minion" % NAME, - "%s/overlord" % NAME, ], data_files = [(initpath, ["init-scripts/certmaster"]), (etcpath, ["etc/minion.conf"]), -- cgit