mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
215 lines
8.1 KiB
Python
215 lines
8.1 KiB
Python
#============================================================================
|
|
# 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.
|
|
#============================================================================
|
|
|
|
"""
|
|
An enhanced XML-RPC client/server interface for Python.
|
|
"""
|
|
|
|
import string
|
|
import types
|
|
import fcntl
|
|
|
|
from httplib import HTTPConnection, HTTP
|
|
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
|
import SocketServer
|
|
import xmlrpclib, socket, os, stat
|
|
|
|
from xen.xend.XendLogging import log
|
|
|
|
try:
|
|
import SSHTransport
|
|
ssh_enabled = True
|
|
except ImportError:
|
|
# SSHTransport is disabled on Python <2.4, because it uses the subprocess
|
|
# package.
|
|
ssh_enabled = False
|
|
|
|
|
|
# A new ServerProxy that also supports httpu urls. An http URL comes in the
|
|
# form:
|
|
#
|
|
# httpu:///absolute/path/to/socket.sock
|
|
#
|
|
# It assumes that the RPC handler is /RPC2. This probably needs to be improved
|
|
|
|
# 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"
|
|
|
|
# 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):
|
|
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)
|
|
|
|
class HTTPUnixConnection(HTTPConnection):
|
|
def connect(self):
|
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
self.sock.connect(self.host)
|
|
|
|
class HTTPUnix(HTTP):
|
|
_connection_class = HTTPUnixConnection
|
|
|
|
class UnixTransport(xmlrpclib.Transport):
|
|
def request(self, host, handler, request_body, verbose=0):
|
|
self.__handler = handler
|
|
return xmlrpclib.Transport.request(self, host, '/RPC2',
|
|
request_body, verbose)
|
|
def make_connection(self, host):
|
|
return HTTPUnix(self.__handler)
|
|
|
|
|
|
# See _marshalled_dispatch below.
|
|
def conv_string(x):
|
|
if (isinstance(x, types.StringType) or
|
|
isinstance(x, unicode)):
|
|
s = string.replace(x, "'", r"\047")
|
|
exec "s = '" + s + "'"
|
|
return s
|
|
else:
|
|
return x
|
|
|
|
|
|
class ServerProxy(xmlrpclib.ServerProxy):
|
|
def __init__(self, uri, transport=None, encoding=None, verbose=0,
|
|
allow_none=1):
|
|
if transport == None:
|
|
(protocol, rest) = uri.split(':', 1)
|
|
if protocol == 'httpu':
|
|
uri = 'http:' + rest
|
|
transport = UnixTransport()
|
|
elif protocol == 'ssh':
|
|
global ssh_enabled
|
|
if ssh_enabled:
|
|
(transport, uri) = SSHTransport.getHTTPURI(uri)
|
|
else:
|
|
raise ValueError(
|
|
"SSH transport not supported on Python <2.4.")
|
|
xmlrpclib.ServerProxy.__init__(self, uri, transport, encoding,
|
|
verbose, allow_none)
|
|
|
|
def __request(self, methodname, params):
|
|
response = xmlrpclib.ServerProxy.__request(self, methodname, params)
|
|
|
|
if isinstance(response, tuple):
|
|
return tuple([conv_string(x) for x in response])
|
|
else:
|
|
return conv_string(response)
|
|
|
|
|
|
# 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=XMLRPCRequestHandler,
|
|
logRequests = 1):
|
|
SimpleXMLRPCServer.__init__(self, addr, requestHandler, 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)
|
|
|
|
# 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, types.StringType) or
|
|
isinstance(response, unicode)):
|
|
response = repr(response)[1:-1]
|
|
|
|
response = (response,)
|
|
response = xmlrpclib.dumps(response,
|
|
methodresponse=1,
|
|
allow_none=1)
|
|
except xmlrpclib.Fault, fault:
|
|
response = xmlrpclib.dumps(fault)
|
|
except Exception, exn:
|
|
import xen.xend.XendClient
|
|
log.exception(exn)
|
|
response = xmlrpclib.dumps(
|
|
xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn)))
|
|
|
|
return response
|
|
|
|
# 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
|
|
|
|
def __init__(self, addr, logRequests = 1):
|
|
parent = os.path.dirname(addr)
|
|
if os.path.exists(parent):
|
|
os.chown(parent, os.geteuid(), os.getegid())
|
|
os.chmod(parent, stat.S_IRWXU)
|
|
if self.allow_reuse_address and os.path.exists(addr):
|
|
os.unlink(addr)
|
|
else:
|
|
os.makedirs(parent, stat.S_IRWXU)
|
|
TCPXMLRPCServer.__init__(self, addr, UnixXMLRPCRequestHandler,
|
|
logRequests)
|