~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/python/xen/util/xmlrpclib2.py

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#============================================================================
 
18
 
 
19
"""
 
20
An enhanced XML-RPC client/server interface for Python.
 
21
"""
 
22
 
 
23
import re
 
24
import fcntl
 
25
from types import *
 
26
    
 
27
 
 
28
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
 
29
import SocketServer
 
30
import xmlrpclib, socket, os, stat
 
31
 
 
32
import mkdir
 
33
 
 
34
from xen.web import connection
 
35
from xen.xend.XendLogging import log
 
36
 
 
37
#
 
38
# Convert all integers to strings as described in the Xen API
 
39
#
 
40
 
 
41
 
 
42
def stringify(value):
 
43
    if isinstance(value, long) or \
 
44
       (isinstance(value, int) and not isinstance(value, bool)):
 
45
        return str(value)
 
46
    elif isinstance(value, dict):
 
47
        new_value = {}
 
48
        for k, v in value.items():
 
49
            new_value[stringify(k)] = stringify(v)
 
50
        return new_value
 
51
    elif isinstance(value, (tuple, list)):
 
52
        return [stringify(v) for v in value]
 
53
    else:
 
54
        return value
 
55
 
 
56
 
 
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"
 
61
 
 
62
    def __init__(self, hosts_allowed, request, client_address, server):
 
63
        self.hosts_allowed = hosts_allowed
 
64
        SimpleXMLRPCRequestHandler.__init__(self, request, client_address,
 
65
                                            server)
 
66
 
 
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
 
72
    #    use one
 
73
    def do_POST(self):
 
74
        addrport = self.client_address
 
75
        if not connection.hostAllowed(addrport, self.hosts_allowed):
 
76
            self.connection.shutdown(1)
 
77
            return
 
78
 
 
79
        data = self.rfile.read(int(self.headers["content-length"]))
 
80
        rsp = self.server._marshaled_dispatch(data)
 
81
 
 
82
        self.send_response(200)
 
83
        self.send_header("Content-Type", "text/xml")
 
84
        self.send_header("Content-Length", str(len(rsp)))
 
85
        self.end_headers()
 
86
 
 
87
        self.wfile.write(rsp)
 
88
        self.wfile.flush()
 
89
        if self.close_connection == 1:
 
90
            self.connection.shutdown(1)
 
91
 
 
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.
 
94
 
 
95
class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
 
96
    allow_reuse_address = True
 
97
 
 
98
    def __init__(self, addr, allowed, xenapi, requestHandler=None,
 
99
                 logRequests = 1):
 
100
        self.xenapi = xenapi
 
101
        
 
102
        if requestHandler is None:
 
103
            requestHandler = XMLRPCRequestHandler
 
104
        SimpleXMLRPCServer.__init__(self, addr,
 
105
                                    (lambda x, y, z:
 
106
                                     requestHandler(allowed, x, y, z)),
 
107
                                    logRequests)
 
108
 
 
109
        flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
 
110
        flags |= fcntl.FD_CLOEXEC
 
111
        fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
 
112
 
 
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)
 
119
 
 
120
    def _marshaled_dispatch(self, data, dispatch_method = None):
 
121
        params, method = xmlrpclib.loads(data)
 
122
        if False:
 
123
            # Enable this block of code to exit immediately without sending
 
124
            # a response.  This allows you to test client-side crash handling.
 
125
            import sys
 
126
            sys.exit(1)
 
127
        try:
 
128
            if dispatch_method is not None:
 
129
                response = dispatch_method(method, params)
 
130
            else:
 
131
                response = self._dispatch(method, params)
 
132
 
 
133
            if self.xenapi and \
 
134
               (response is None or
 
135
                not isinstance(response, dict) or
 
136
                'Status' not in response):
 
137
                log.exception('Internal error handling %s: Invalid result %s',
 
138
                              method, response)
 
139
                response = { "Status": "Failure",
 
140
                             "ErrorDescription":
 
141
                             ['INTERNAL_ERROR',
 
142
                              'Invalid result %s handling %s' %
 
143
                              (response, method)]}
 
144
 
 
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
 
152
            # in xml_pickle.py).
 
153
            if isinstance(response, StringTypes):
 
154
                response = repr(response)[1:-1]
 
155
 
 
156
            response = (response,)
 
157
            response = xmlrpclib.dumps(response,
 
158
                                       methodresponse=1,
 
159
                                       allow_none=1)
 
160
        except Exception, exn:
 
161
            try:
 
162
                if self.xenapi:
 
163
                    if _is_not_supported(exn):
 
164
                         errdesc = ['MESSAGE_METHOD_UNKNOWN', method]
 
165
                    else:
 
166
                         log.exception('Internal error handling %s', method)
 
167
                         errdesc = ['INTERNAL_ERROR', str(exn)]
 
168
 
 
169
                    response = xmlrpclib.dumps(
 
170
                          ({ "Status": "Failure",
 
171
                             "ErrorDescription": errdesc },),
 
172
                          methodresponse = 1)
 
173
                else:
 
174
                    import xen.xend.XendClient
 
175
                    if isinstance(exn, xmlrpclib.Fault):
 
176
                        response = xmlrpclib.dumps(exn)
 
177
                    else:
 
178
                        log.exception('Internal error handling %s', method)
 
179
                        response = xmlrpclib.dumps(
 
180
                            xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn)))
 
181
            except:
 
182
                log.exception('Internal error handling error')
 
183
 
 
184
        return response
 
185
 
 
186
 
 
187
notSupportedRE = re.compile(r'method "(.*)" is not supported')
 
188
def _is_not_supported(exn):
 
189
    try:
 
190
        m = notSupportedRE.search(exn[0])
 
191
        return m is not None
 
192
    except:
 
193
        return False
 
194
 
 
195
 
 
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.
 
199
 
 
200
class UnixXMLRPCRequestHandler(XMLRPCRequestHandler):
 
201
    def address_string(self):
 
202
        try:
 
203
            return XMLRPCRequestHandler.address_string(self)
 
204
        except ValueError, e:
 
205
            return self.client_address[:2]
 
206
 
 
207
class UnixXMLRPCServer(TCPXMLRPCServer):
 
208
    address_family = socket.AF_UNIX
 
209
    allow_address_reuse = True
 
210
 
 
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):
 
214
            os.unlink(addr)
 
215
 
 
216
        TCPXMLRPCServer.__init__(self, addr, allowed, xenapi,
 
217
                                 UnixXMLRPCRequestHandler, logRequests)