Files
xen/tools/python/xen/util/xmlrpclib2.py
T
Michael Young 4053efae85 tools/python: fix xm list for Python 2.7
This patch fixes
  Unexpected error: <type 'exceptions.AttributeError'>
This is due to xmlrpc changes in Python 2.7.  This patch should
fixe it for both old and new versions.

Signed-off-by: Michael Young <m.a.young@durham.ac.uk>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
2010-08-19 17:09:30 +01:00

221 lines
8.5 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 re
import fcntl
from types import *
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
import SocketServer
import xmlrpclib, socket, os, stat
import mkdir
from xen.web import connection
from xen.xend.XendLogging import log
#
# 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"
# xend crashes in python 2.7 unless disable_nagle_algorithm = False
# it isn't used in earlier versions so it is harmless to set it generally
disable_nagle_algorithm = False
def __init__(self, hosts_allowed, request, client_address, server):
self.hosts_allowed = hosts_allowed
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)
# 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, allowed, xenapi, requestHandler=None,
logRequests = 1):
self.xenapi = xenapi
if requestHandler is None:
requestHandler = XMLRPCRequestHandler
SimpleXMLRPCServer.__init__(self, addr,
(lambda x, y, z:
requestHandler(allowed, 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 self.xenapi and \
(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(xen.xend.XendClient.ERROR_INTERNAL, str(exn)))
except:
log.exception('Internal error handling error')
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, allowed, xenapi, logRequests = 1):
mkdir.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, allowed, xenapi,
UnixXMLRPCRequestHandler, logRequests)