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

« back to all changes in this revision

Viewing changes to tools/python/xen/xend/XendProtocol.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) 2004, 2005 Mike Wray <mike.wray@hp.com>
 
16
# Copyright (C) 2005 XenSource Ltd.
 
17
#============================================================================
 
18
 
 
19
import socket
 
20
import httplib
 
21
import time
 
22
import types
 
23
 
 
24
from encode import *
 
25
from xen.xend import sxp
 
26
 
 
27
from xen.xend import XendOptions
 
28
 
 
29
DEBUG = 0
 
30
 
 
31
HTTP_OK                              = 200
 
32
HTTP_CREATED                         = 201
 
33
HTTP_ACCEPTED                        = 202
 
34
HTTP_NO_CONTENT                      = 204
 
35
 
 
36
 
 
37
xoptions = XendOptions.instance()
 
38
 
 
39
 
 
40
class XendError(RuntimeError):
 
41
    """Error class for 'expected errors' when talking to xend.
 
42
    """
 
43
    pass
 
44
 
 
45
class XendRequest:
 
46
    """A request to xend.
 
47
    """
 
48
 
 
49
    def __init__(self, url, method, args):
 
50
        """Create a request. Sets up the headers, argument data, and the
 
51
        url.
 
52
 
 
53
        @param url:    the url to request
 
54
        @param method: request method, GET or POST
 
55
        @param args:   dict containing request args, if any
 
56
        """
 
57
        if url.proto != 'http':
 
58
            raise ValueError('Invalid protocol: ' + url.proto)
 
59
        (hdr, data) = encode_data(args)
 
60
        if args and method == 'GET':
 
61
            url.query = data
 
62
            data = None
 
63
        if method == "POST" and url.path.endswith('/'):
 
64
            url.path = url.path[:-1]
 
65
 
 
66
        self.headers = hdr
 
67
        self.data = data
 
68
        self.url = url
 
69
        self.method = method
 
70
 
 
71
class XendClientProtocol:
 
72
    """Abstract class for xend clients.
 
73
    """
 
74
    def xendRequest(self, url, method, args=None):
 
75
        """Make a request to xend.
 
76
        Implement in a subclass.
 
77
 
 
78
        @param url:    xend request url
 
79
        @param method: http method: POST or GET
 
80
        @param args:   request arguments (dict)
 
81
        """
 
82
        raise NotImplementedError()
 
83
 
 
84
    def xendGet(self, url, args=None):
 
85
        """Make a xend request using HTTP GET.
 
86
        Requests using GET are usually 'safe' and may be repeated without
 
87
        nasty side-effects.
 
88
 
 
89
        @param url:    xend request url
 
90
        @param data:   request arguments (dict)
 
91
        """
 
92
        return self.xendRequest(url, "GET", args)
 
93
 
 
94
    def xendPost(self, url, args):
 
95
        """Make a xend request using HTTP POST.
 
96
        Requests using POST potentially cause side-effects, and should
 
97
        not be repeated unless you really want to repeat the side
 
98
        effect.
 
99
 
 
100
        @param url:    xend request url
 
101
        @param args:   request arguments (dict)
 
102
        """
 
103
        return self.xendRequest(url, "POST", args)
 
104
 
 
105
    def handleStatus(self, _, status, message):
 
106
        """Handle the status returned from the request.
 
107
        """
 
108
        status = int(status)
 
109
        if status in [ HTTP_NO_CONTENT ]:
 
110
            return None
 
111
        if status not in [ HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED ]:
 
112
            return self.handleException(XendError(message))
 
113
        return 'ok'
 
114
 
 
115
    def handleResponse(self, data):
 
116
        """Handle the data returned in response to the request.
 
117
        """
 
118
        if data is None: return None
 
119
        typ = self.getHeader('Content-Type')
 
120
        if typ != sxp.mime_type:
 
121
            return data
 
122
        try:
 
123
            pin = sxp.Parser()
 
124
            pin.input(data);
 
125
            pin.input_eof()
 
126
            val = pin.get_val()
 
127
        except sxp.ParseError, err:
 
128
            return self.handleException(err)
 
129
        if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
 
130
            err = XendError(val[1])
 
131
            return self.handleException(err)
 
132
        return val
 
133
 
 
134
    def handleException(self, err):
 
135
        """Handle an exception during the request.
 
136
        May be overridden in a subclass.
 
137
        """
 
138
        raise err
 
139
 
 
140
    def getHeader(self, key):
 
141
        """Get a header from the response.
 
142
        Case is ignored in the key.
 
143
 
 
144
        @param key: header key
 
145
        @return: header
 
146
        """
 
147
        raise NotImplementedError()
 
148
 
 
149
class HttpXendClientProtocol(XendClientProtocol):
 
150
    """A synchronous xend client. This will make a request, wait for
 
151
    the reply and return the result.
 
152
    """
 
153
 
 
154
    resp = None
 
155
    request = None
 
156
 
 
157
    def makeConnection(self, url):
 
158
        return httplib.HTTPConnection(url.location())
 
159
 
 
160
    def makeRequest(self, url, method, args):
 
161
        return XendRequest(url, method, args)
 
162
 
 
163
    def xendRequest(self, url, method, args=None):
 
164
        """Make a request to xend.
 
165
 
 
166
        @param url:    xend request url
 
167
        @param method: http method: POST or GET
 
168
        @param args:   request arguments (dict)
 
169
        """
 
170
        retries = 0
 
171
        while retries < 2:
 
172
            self.request = self.makeRequest(url, method, args)
 
173
            conn = self.makeConnection(url)
 
174
            try:
 
175
                if DEBUG: conn.set_debuglevel(1)
 
176
                conn.request(method, url.fullpath(), self.request.data,
 
177
                             self.request.headers)
 
178
                try:
 
179
                    resp = conn.getresponse()
 
180
                    self.resp = resp
 
181
                    val = self.handleStatus(resp.version, resp.status,
 
182
                                            resp.reason)
 
183
                    if val is None:
 
184
                        data = None
 
185
                    else:
 
186
                        data = resp.read()
 
187
                    val = self.handleResponse(data)
 
188
                    return val
 
189
                except httplib.BadStatusLine:
 
190
                    retries += 1
 
191
                    time.sleep(5)
 
192
            finally:
 
193
                conn.close()
 
194
 
 
195
        raise XendError("Received invalid response from Xend, twice.")
 
196
 
 
197
 
 
198
    def getHeader(self, key):
 
199
        return self.resp.getheader(key)
 
200
 
 
201
 
 
202
class UnixConnection(httplib.HTTPConnection):
 
203
    """Subclass of Python library HTTPConnection that uses a unix-domain socket.
 
204
    """
 
205
 
 
206
    def __init__(self, path):
 
207
        httplib.HTTPConnection.__init__(self, 'localhost')
 
208
        self.path = path
 
209
 
 
210
    def connect(self):
 
211
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 
212
        sock.connect(self.path)
 
213
        self.sock = sock
 
214
 
 
215
class UnixXendClientProtocol(HttpXendClientProtocol):
 
216
    """A synchronous xend client using a unix-domain socket.
 
217
    """
 
218
 
 
219
    def __init__(self, path=None):
 
220
        if path is None:
 
221
            path = xoptions.get_xend_unix_path()
 
222
        self.path = path
 
223
 
 
224
    def makeConnection(self, _):
 
225
        return UnixConnection(self.path)