File: XendProtocol.py

package info (click to toggle)
xen-3.0 3.0.3-0-2
  • links: PTS
  • area: main
  • in suites: etch-m68k
  • size: 31,772 kB
  • ctags: 70,362
  • sloc: ansic: 417,153; python: 28,855; asm: 23,892; sh: 5,157; makefile: 4,830; objc: 613; perl: 372; xml: 351
file content (225 lines) | stat: -rw-r--r-- 7,144 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#============================================================================
# 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) 2004, 2005 Mike Wray <mike.wray@hp.com>
# Copyright (C) 2005 XenSource Ltd.
#============================================================================

import socket
import httplib
import time
import types

from encode import *
import sxp

from xen.xend import XendRoot

DEBUG = 0

HTTP_OK                              = 200
HTTP_CREATED                         = 201
HTTP_ACCEPTED                        = 202
HTTP_NO_CONTENT                      = 204


xroot = XendRoot.instance()


class XendError(RuntimeError):
    """Error class for 'expected errors' when talking to xend.
    """
    pass

class XendRequest:
    """A request to xend.
    """

    def __init__(self, url, method, args):
        """Create a request. Sets up the headers, argument data, and the
        url.

        @param url:    the url to request
        @param method: request method, GET or POST
        @param args:   dict containing request args, if any
        """
        if url.proto != 'http':
            raise ValueError('Invalid protocol: ' + url.proto)
        (hdr, data) = encode_data(args)
        if args and method == 'GET':
            url.query = data
            data = None
        if method == "POST" and url.path.endswith('/'):
            url.path = url.path[:-1]

        self.headers = hdr
        self.data = data
        self.url = url
        self.method = method

class XendClientProtocol:
    """Abstract class for xend clients.
    """
    def xendRequest(self, url, method, args=None):
        """Make a request to xend.
        Implement in a subclass.

        @param url:    xend request url
        @param method: http method: POST or GET
        @param args:   request arguments (dict)
        """
        raise NotImplementedError()

    def xendGet(self, url, args=None):
        """Make a xend request using HTTP GET.
        Requests using GET are usually 'safe' and may be repeated without
        nasty side-effects.

        @param url:    xend request url
        @param data:   request arguments (dict)
        """
        return self.xendRequest(url, "GET", args)

    def xendPost(self, url, args):
        """Make a xend request using HTTP POST.
        Requests using POST potentially cause side-effects, and should
        not be repeated unless you really want to repeat the side
        effect.

        @param url:    xend request url
        @param args:   request arguments (dict)
        """
        return self.xendRequest(url, "POST", args)

    def handleStatus(self, _, status, message):
        """Handle the status returned from the request.
        """
        status = int(status)
        if status in [ HTTP_NO_CONTENT ]:
            return None
        if status not in [ HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED ]:
            return self.handleException(XendError(message))
        return 'ok'

    def handleResponse(self, data):
        """Handle the data returned in response to the request.
        """
        if data is None: return None
        typ = self.getHeader('Content-Type')
        if typ != sxp.mime_type:
            return data
        try:
            pin = sxp.Parser()
            pin.input(data);
            pin.input_eof()
            val = pin.get_val()
        except sxp.ParseError, err:
            return self.handleException(err)
        if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
            err = XendError(val[1])
            return self.handleException(err)
        return val

    def handleException(self, err):
        """Handle an exception during the request.
        May be overridden in a subclass.
        """
        raise err

    def getHeader(self, key):
        """Get a header from the response.
        Case is ignored in the key.

        @param key: header key
        @return: header
        """
        raise NotImplementedError()

class HttpXendClientProtocol(XendClientProtocol):
    """A synchronous xend client. This will make a request, wait for
    the reply and return the result.
    """

    resp = None
    request = None

    def makeConnection(self, url):
        return httplib.HTTPConnection(url.location())

    def makeRequest(self, url, method, args):
        return XendRequest(url, method, args)

    def xendRequest(self, url, method, args=None):
        """Make a request to xend.

        @param url:    xend request url
        @param method: http method: POST or GET
        @param args:   request arguments (dict)
        """
        retries = 0
        while retries < 2:
            self.request = self.makeRequest(url, method, args)
            conn = self.makeConnection(url)
            try:
                if DEBUG: conn.set_debuglevel(1)
                conn.request(method, url.fullpath(), self.request.data,
                             self.request.headers)
                try:
                    resp = conn.getresponse()
                    self.resp = resp
                    val = self.handleStatus(resp.version, resp.status,
                                            resp.reason)
                    if val is None:
                        data = None
                    else:
                        data = resp.read()
                    val = self.handleResponse(data)
                    return val
                except httplib.BadStatusLine:
                    retries += 1
                    time.sleep(5)
            finally:
                conn.close()

        raise XendError("Received invalid response from Xend, twice.")


    def getHeader(self, key):
        return self.resp.getheader(key)


class UnixConnection(httplib.HTTPConnection):
    """Subclass of Python library HTTPConnection that uses a unix-domain socket.
    """

    def __init__(self, path):
        httplib.HTTPConnection.__init__(self, 'localhost')
        self.path = path

    def connect(self):
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect(self.path)
        self.sock = sock

class UnixXendClientProtocol(HttpXendClientProtocol):
    """A synchronous xend client using a unix-domain socket.
    """

    def __init__(self, path=None):
        if path is None:
            path = xroot.get_xend_unix_path()
        self.path = path

    def makeConnection(self, _):
        return UnixConnection(self.path)