mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
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>
221 lines
8.5 KiB
Python
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)
|