ia64/xen-unstable

view tools/python/xen/util/xmlrpclib2.py @ 12075:52bf7bbb0f36

[XEND] Make logging an optional parameter for the XMLRPCServer

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Oct 05 17:29:19 2006 +0100 (2006-10-05)
parents 5db6984e4503
children 91c7ee18c978
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2006 Anthony Liguori <aliguori@us.ibm.com>
16 # Copyright (C) 2006 XenSource Inc.
17 #============================================================================
19 """
20 An enhanced XML-RPC client/server interface for Python.
21 """
23 import string
24 import types
25 import fcntl
27 from httplib import HTTPConnection, HTTP
28 from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
29 import SocketServer
30 import xmlrpclib, socket, os, stat
32 from xen.xend.XendLogging import log
34 try:
35 import SSHTransport
36 ssh_enabled = True
37 except ImportError:
38 # SSHTransport is disabled on Python <2.4, because it uses the subprocess
39 # package.
40 ssh_enabled = False
43 # A new ServerProxy that also supports httpu urls. An http URL comes in the
44 # form:
45 #
46 # httpu:///absolute/path/to/socket.sock
47 #
48 # It assumes that the RPC handler is /RPC2. This probably needs to be improved
50 # We're forced to subclass the RequestHandler class so that we can work around
51 # some bugs in Keep-Alive handling and also enabled it by default
52 class XMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
53 protocol_version = "HTTP/1.1"
55 # this is inspired by SimpleXMLRPCRequestHandler's do_POST but differs
56 # in a few non-trivial ways
57 # 1) we never generate internal server errors. We let the exception
58 # propagate so that it shows up in the Xend debug logs
59 # 2) we don't bother checking for a _dispatch function since we don't
60 # use one
61 def do_POST(self):
62 data = self.rfile.read(int(self.headers["content-length"]))
63 rsp = self.server._marshaled_dispatch(data)
65 self.send_response(200)
66 self.send_header("Content-Type", "text/xml")
67 self.send_header("Content-Length", str(len(rsp)))
68 self.end_headers()
70 self.wfile.write(rsp)
71 self.wfile.flush()
72 if self.close_connection == 1:
73 self.connection.shutdown(1)
75 class HTTPUnixConnection(HTTPConnection):
76 def connect(self):
77 self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
78 self.sock.connect(self.host)
80 class HTTPUnix(HTTP):
81 _connection_class = HTTPUnixConnection
83 class UnixTransport(xmlrpclib.Transport):
84 def request(self, host, handler, request_body, verbose=0):
85 self.__handler = handler
86 return xmlrpclib.Transport.request(self, host, '/RPC2',
87 request_body, verbose)
88 def make_connection(self, host):
89 return HTTPUnix(self.__handler)
92 # See _marshalled_dispatch below.
93 def conv_string(x):
94 if (isinstance(x, types.StringType) or
95 isinstance(x, unicode)):
96 s = string.replace(x, "'", r"\047")
97 exec "s = '" + s + "'"
98 return s
99 else:
100 return x
103 class ServerProxy(xmlrpclib.ServerProxy):
104 def __init__(self, uri, transport=None, encoding=None, verbose=0,
105 allow_none=1):
106 if transport == None:
107 (protocol, rest) = uri.split(':', 1)
108 if protocol == 'httpu':
109 uri = 'http:' + rest
110 transport = UnixTransport()
111 elif protocol == 'ssh':
112 global ssh_enabled
113 if ssh_enabled:
114 (transport, uri) = SSHTransport.getHTTPURI(uri)
115 else:
116 raise ValueError(
117 "SSH transport not supported on Python <2.4.")
118 xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding,
119 verbose, allow_none)
121 def __request(self, methodname, params):
122 response = xmlrpclib.ServerProxy.__request(self, methodname, params)
124 if isinstance(response, tuple):
125 return tuple([conv_string(x) for x in response])
126 else:
127 return conv_string(response)
130 # This is a base XML-RPC server for TCP. It sets allow_reuse_address to
131 # true, and has an improved marshaller that logs and serializes exceptions.
133 class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
134 allow_reuse_address = True
136 def __init__(self, addr, requestHandler=XMLRPCRequestHandler,
137 logRequests = 1):
138 SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
140 flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
141 flags |= fcntl.FD_CLOEXEC
142 fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
144 def get_request(self):
145 (client, addr) = SimpleXMLRPCServer.get_request(self)
146 flags = fcntl.fcntl(client.fileno(), fcntl.F_GETFD)
147 flags |= fcntl.FD_CLOEXEC
148 fcntl.fcntl(client.fileno(), fcntl.F_SETFD, flags)
149 return (client, addr)
151 def _marshaled_dispatch(self, data, dispatch_method = None):
152 params, method = xmlrpclib.loads(data)
153 if False:
154 # Enable this block of code to exit immediately without sending
155 # a response. This allows you to test client-side crash handling.
156 import sys
157 sys.exit(1)
158 try:
159 if dispatch_method is not None:
160 response = dispatch_method(method, params)
161 else:
162 response = self._dispatch(method, params)
164 # With either Unicode or normal strings, we can only transmit
165 # \t, \n, \r, \u0020-\ud7ff, \ue000-\ufffd, and \u10000-\u10ffff
166 # in an XML document. xmlrpclib does not escape these values
167 # properly, and then breaks when it comes to parse the document.
168 # To hack around this problem, we use repr here and exec above
169 # to transmit the string using Python encoding.
170 # Thanks to David Mertz <mertz@gnosis.cx> for the trick (buried
171 # in xml_pickle.py).
172 if (isinstance(response, types.StringType) or
173 isinstance(response, unicode)):
174 response = repr(response)[1:-1]
176 response = (response,)
177 response = xmlrpclib.dumps(response,
178 methodresponse=1,
179 allow_none=1)
180 except xmlrpclib.Fault, fault:
181 response = xmlrpclib.dumps(fault)
182 except Exception, exn:
183 import xen.xend.XendClient
184 log.exception(exn)
185 response = xmlrpclib.dumps(
186 xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn)))
188 return response
190 # This is a XML-RPC server that sits on a Unix domain socket.
191 # It implements proper support for allow_reuse_address by
192 # unlink()'ing an existing socket.
194 class UnixXMLRPCRequestHandler(XMLRPCRequestHandler):
195 def address_string(self):
196 try:
197 return XMLRPCRequestHandler.address_string(self)
198 except ValueError, e:
199 return self.client_address[:2]
201 class UnixXMLRPCServer(TCPXMLRPCServer):
202 address_family = socket.AF_UNIX
204 def __init__(self, addr, logRequests = 1):
205 parent = os.path.dirname(addr)
206 if os.path.exists(parent):
207 os.chown(parent, os.geteuid(), os.getegid())
208 os.chmod(parent, stat.S_IRWXU)
209 if self.allow_reuse_address and os.path.exists(addr):
210 os.unlink(addr)
211 else:
212 os.makedirs(parent, stat.S_IRWXU)
213 TCPXMLRPCServer.__init__(self, addr, UnixXMLRPCRequestHandler,
214 logRequests)