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.
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.
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
#============================================================================
20
An enhanced XML-RPC client/server interface for Python.
28
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
30
import xmlrpclib, socket, os, stat
34
from xen.web import connection
35
from xen.xend.XendLogging import log
38
# Convert all integers to strings as described in the Xen API
43
if isinstance(value, long) or \
44
(isinstance(value, int) and not isinstance(value, bool)):
46
elif isinstance(value, dict):
48
for k, v in value.items():
49
new_value[stringify(k)] = stringify(v)
51
elif isinstance(value, (tuple, list)):
52
return [stringify(v) for v in value]
57
# We're forced to subclass the RequestHandler class so that we can work around
58
# some bugs in Keep-Alive handling and also enabled it by default
59
class XMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
60
protocol_version = "HTTP/1.1"
62
def __init__(self, hosts_allowed, request, client_address, server):
63
self.hosts_allowed = hosts_allowed
64
SimpleXMLRPCRequestHandler.__init__(self, request, client_address,
67
# this is inspired by SimpleXMLRPCRequestHandler's do_POST but differs
68
# in a few non-trivial ways
69
# 1) we never generate internal server errors. We let the exception
70
# propagate so that it shows up in the Xend debug logs
71
# 2) we don't bother checking for a _dispatch function since we don't
74
addrport = self.client_address
75
if not connection.hostAllowed(addrport, self.hosts_allowed):
76
self.connection.shutdown(1)
79
data = self.rfile.read(int(self.headers["content-length"]))
80
rsp = self.server._marshaled_dispatch(data)
82
self.send_response(200)
83
self.send_header("Content-Type", "text/xml")
84
self.send_header("Content-Length", str(len(rsp)))
89
if self.close_connection == 1:
90
self.connection.shutdown(1)
92
# This is a base XML-RPC server for TCP. It sets allow_reuse_address to
93
# true, and has an improved marshaller that logs and serializes exceptions.
95
class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
96
allow_reuse_address = True
98
def __init__(self, addr, allowed, xenapi, requestHandler=None,
102
if requestHandler is None:
103
requestHandler = XMLRPCRequestHandler
104
SimpleXMLRPCServer.__init__(self, addr,
106
requestHandler(allowed, x, y, z)),
109
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
110
flags |= fcntl.FD_CLOEXEC
111
fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
113
def get_request(self):
114
(client, addr) = SimpleXMLRPCServer.get_request(self)
115
flags = fcntl.fcntl(client.fileno(), fcntl.F_GETFD)
116
flags |= fcntl.FD_CLOEXEC
117
fcntl.fcntl(client.fileno(), fcntl.F_SETFD, flags)
118
return (client, addr)
120
def _marshaled_dispatch(self, data, dispatch_method = None):
121
params, method = xmlrpclib.loads(data)
123
# Enable this block of code to exit immediately without sending
124
# a response. This allows you to test client-side crash handling.
128
if dispatch_method is not None:
129
response = dispatch_method(method, params)
131
response = self._dispatch(method, params)
135
not isinstance(response, dict) or
136
'Status' not in response):
137
log.exception('Internal error handling %s: Invalid result %s',
139
response = { "Status": "Failure",
142
'Invalid result %s handling %s' %
145
# With either Unicode or normal strings, we can only transmit
146
# \t, \n, \r, \u0020-\ud7ff, \ue000-\ufffd, and \u10000-\u10ffff
147
# in an XML document. xmlrpclib does not escape these values
148
# properly, and then breaks when it comes to parse the document.
149
# To hack around this problem, we use repr here and exec above
150
# to transmit the string using Python encoding.
151
# Thanks to David Mertz <mertz@gnosis.cx> for the trick (buried
153
if isinstance(response, StringTypes):
154
response = repr(response)[1:-1]
156
response = (response,)
157
response = xmlrpclib.dumps(response,
160
except Exception, exn:
163
if _is_not_supported(exn):
164
errdesc = ['MESSAGE_METHOD_UNKNOWN', method]
166
log.exception('Internal error handling %s', method)
167
errdesc = ['INTERNAL_ERROR', str(exn)]
169
response = xmlrpclib.dumps(
170
({ "Status": "Failure",
171
"ErrorDescription": errdesc },),
174
import xen.xend.XendClient
175
if isinstance(exn, xmlrpclib.Fault):
176
response = xmlrpclib.dumps(exn)
178
log.exception('Internal error handling %s', method)
179
response = xmlrpclib.dumps(
180
xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn)))
182
log.exception('Internal error handling error')
187
notSupportedRE = re.compile(r'method "(.*)" is not supported')
188
def _is_not_supported(exn):
190
m = notSupportedRE.search(exn[0])
196
# This is a XML-RPC server that sits on a Unix domain socket.
197
# It implements proper support for allow_reuse_address by
198
# unlink()'ing an existing socket.
200
class UnixXMLRPCRequestHandler(XMLRPCRequestHandler):
201
def address_string(self):
203
return XMLRPCRequestHandler.address_string(self)
204
except ValueError, e:
205
return self.client_address[:2]
207
class UnixXMLRPCServer(TCPXMLRPCServer):
208
address_family = socket.AF_UNIX
209
allow_address_reuse = True
211
def __init__(self, addr, allowed, xenapi, logRequests = 1):
212
mkdir.parents(os.path.dirname(addr), stat.S_IRWXU, True)
213
if self.allow_reuse_address and os.path.exists(addr):
216
TCPXMLRPCServer.__init__(self, addr, allowed, xenapi,
217
UnixXMLRPCRequestHandler, logRequests)