diff options
author | Michael DeHaan <mdehaan@redhat.com> | 2008-02-15 14:54:25 -0500 |
---|---|---|
committer | Michael DeHaan <mdehaan@redhat.com> | 2008-02-15 14:54:25 -0500 |
commit | a47ee22ab684eed992128b60b136fca90ff9f119 (patch) | |
tree | cd55daf8057049368b4aaa6d23c30408eae411d2 /cobbler/server/xmlrpclib2.py | |
parent | 70bfc8f5a3150d09cc064a4c46efcaff80b29904 (diff) | |
parent | a7d67f35019af0c25f133c979112fc6035d7e04c (diff) | |
download | third_party-cobbler-a47ee22ab684eed992128b60b136fca90ff9f119.tar.gz third_party-cobbler-a47ee22ab684eed992128b60b136fca90ff9f119.tar.xz third_party-cobbler-a47ee22ab684eed992128b60b136fca90ff9f119.zip |
Merge branch 'devel'
Merging devel work on 0.7.X/0.8 release with master
Conflicts:
CHANGELOG
cobbler.spec
cobbler/action_import.py
cobbler/utils.py
cobbler/webui/master.py
setup.py
Diffstat (limited to 'cobbler/server/xmlrpclib2.py')
-rw-r--r-- | cobbler/server/xmlrpclib2.py | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/cobbler/server/xmlrpclib2.py b/cobbler/server/xmlrpclib2.py new file mode 100644 index 0000000..d329241 --- /dev/null +++ b/cobbler/server/xmlrpclib2.py @@ -0,0 +1,236 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 Anthony Liguori <aliguori@us.ibm.com> +# Copyright (C) 2006 XenSource Inc. +# Copyright (C) 2007 Red Hat Inc., Michael DeHaan <mdehaan@redhat.com> +#============================================================================ + +""" +An enhanced XML-RPC client/server interface for Python. +""" + +import re +import fcntl +from types import * +import os +import errno +import traceback + +from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler +import SocketServer +import xmlrpclib, socket, os, stat + +#import mkdir + +# +# Convert all integers to strings as described in the Xen API +# + + +def stringify(value): + if isinstance(value, long) or \ + (isinstance(value, int) and not isinstance(value, bool)): + return str(value) + elif isinstance(value, dict): + new_value = {} + for k, v in value.items(): + new_value[stringify(k)] = stringify(v) + return new_value + elif isinstance(value, (tuple, list)): + return [stringify(v) for v in value] + else: + return value + + +# We're forced to subclass the RequestHandler class so that we can work around +# some bugs in Keep-Alive handling and also enabled it by default +class XMLRPCRequestHandler(SimpleXMLRPCRequestHandler): + protocol_version = "HTTP/1.1" + + def __init__(self, request, client_address, server): + SimpleXMLRPCRequestHandler.__init__(self, request, client_address, + server) + + # this is inspired by SimpleXMLRPCRequestHandler's do_POST but differs + # in a few non-trivial ways + # 1) we never generate internal server errors. We let the exception + # propagate so that it shows up in the Xend debug logs + # 2) we don't bother checking for a _dispatch function since we don't + # use one + def do_POST(self): + addrport = self.client_address + #if not connection.hostAllowed(addrport, self.hosts_allowed): + # self.connection.shutdown(1) + # return + + data = self.rfile.read(int(self.headers["content-length"])) + rsp = self.server._marshaled_dispatch(data) + + self.send_response(200) + self.send_header("Content-Type", "text/xml") + self.send_header("Content-Length", str(len(rsp))) + self.end_headers() + + self.wfile.write(rsp) + self.wfile.flush() + #if self.close_connection == 1: + # self.connection.shutdown(1) + +def parents(dir, perms, enforcePermissions = False): + """ + Ensure that the given directory exists, creating it if necessary, but not + complaining if it's already there. + + @param dir The directory name. + @param perms One of the stat.S_ constants. + @param enforcePermissions Enforce our ownership and the given permissions, + even if the directory pre-existed with different ones. + """ + # Catch the exception here, rather than checking for the directory's + # existence first, to avoid races. + try: + os.makedirs(dir, perms) + except OSError, exn: + if exn.args[0] != errno.EEXIST or not os.path.isdir(dir): + raise + if enforcePermissions: + os.chown(dir, os.geteuid(), os.getegid()) + os.chmod(dir, stat.S_IRWXU) + + +# This is a base XML-RPC server for TCP. It sets allow_reuse_address to +# true, and has an improved marshaller that logs and serializes exceptions. + +class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer): + allow_reuse_address = True + + def __init__(self, addr, requestHandler=None, + logRequests = 1): + if requestHandler is None: + requestHandler = XMLRPCRequestHandler + SimpleXMLRPCServer.__init__(self, addr, + (lambda x, y, z: + requestHandler(x, y, z)), + logRequests) + + flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) + + def get_request(self): + (client, addr) = SimpleXMLRPCServer.get_request(self) + flags = fcntl.fcntl(client.fileno(), fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(client.fileno(), fcntl.F_SETFD, flags) + return (client, addr) + + def _marshaled_dispatch(self, data, dispatch_method = None): + params, method = xmlrpclib.loads(data) + if False: + # Enable this block of code to exit immediately without sending + # a response. This allows you to test client-side crash handling. + import sys + sys.exit(1) + try: + if dispatch_method is not None: + response = dispatch_method(method, params) + else: + response = self._dispatch(method, params) + + if (response is None or + not isinstance(response, dict) or + 'Status' not in response): + #log.exception('Internal error handling %s: Invalid result %s', + # method, response) + response = { "Status": "Failure", + "ErrorDescription": + ['INTERNAL_ERROR', + 'Invalid result %s handling %s' % + (response, method)]} + + # With either Unicode or normal strings, we can only transmit + # \t, \n, \r, \u0020-\ud7ff, \ue000-\ufffd, and \u10000-\u10ffff + # in an XML document. xmlrpclib does not escape these values + # properly, and then breaks when it comes to parse the document. + # To hack around this problem, we use repr here and exec above + # to transmit the string using Python encoding. + # Thanks to David Mertz <mertz@gnosis.cx> for the trick (buried + # in xml_pickle.py). + if isinstance(response, StringTypes): + response = repr(response)[1:-1] + + response = (response,) + response = xmlrpclib.dumps(response, + methodresponse=1, + allow_none=1) + except Exception, exn: + try: + #if self.xenapi: + # if _is_not_supported(exn): + # errdesc = ['MESSAGE_METHOD_UNKNOWN', method] + # else: + # #log.exception('Internal error handling %s', method) + # errdesc = ['INTERNAL_ERROR', str(exn)] + # + # response = xmlrpclib.dumps( + # ({ "Status": "Failure", + # "ErrorDescription": errdesc },), + # methodresponse = 1) + #else: + # import xen.xend.XendClient + if isinstance(exn, xmlrpclib.Fault): + response = xmlrpclib.dumps(exn) + else: + # log.exception('Internal error handling %s', method) + response = xmlrpclib.dumps( + xmlrpclib.Fault(101, str(exn))) + except Exception, exn2: + # FIXME + traceback.print_exc() + + return response + + +notSupportedRE = re.compile(r'method "(.*)" is not supported') +def _is_not_supported(exn): + try: + m = notSupportedRE.search(exn[0]) + return m is not None + except: + return False + + +# This is a XML-RPC server that sits on a Unix domain socket. +# It implements proper support for allow_reuse_address by +# unlink()'ing an existing socket. + +class UnixXMLRPCRequestHandler(XMLRPCRequestHandler): + def address_string(self): + try: + return XMLRPCRequestHandler.address_string(self) + except ValueError, e: + return self.client_address[:2] + +class UnixXMLRPCServer(TCPXMLRPCServer): + address_family = socket.AF_UNIX + allow_address_reuse = True + + def __init__(self, addr, logRequests = 1): + parents(os.path.dirname(addr), stat.S_IRWXU, True) + if self.allow_reuse_address and os.path.exists(addr): + os.unlink(addr) + + TCPXMLRPCServer.__init__(self, addr, + UnixXMLRPCRequestHandler, logRequests) |