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.
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.
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
#============================================================================
25
from xen.xend import sxp
27
from xen.xend import XendOptions
37
xoptions = XendOptions.instance()
40
class XendError(RuntimeError):
41
"""Error class for 'expected errors' when talking to xend.
49
def __init__(self, url, method, args):
50
"""Create a request. Sets up the headers, argument data, and the
53
@param url: the url to request
54
@param method: request method, GET or POST
55
@param args: dict containing request args, if any
57
if url.proto != 'http':
58
raise ValueError('Invalid protocol: ' + url.proto)
59
(hdr, data) = encode_data(args)
60
if args and method == 'GET':
63
if method == "POST" and url.path.endswith('/'):
64
url.path = url.path[:-1]
71
class XendClientProtocol:
72
"""Abstract class for xend clients.
74
def xendRequest(self, url, method, args=None):
75
"""Make a request to xend.
76
Implement in a subclass.
78
@param url: xend request url
79
@param method: http method: POST or GET
80
@param args: request arguments (dict)
82
raise NotImplementedError()
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
89
@param url: xend request url
90
@param data: request arguments (dict)
92
return self.xendRequest(url, "GET", args)
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
100
@param url: xend request url
101
@param args: request arguments (dict)
103
return self.xendRequest(url, "POST", args)
105
def handleStatus(self, _, status, message):
106
"""Handle the status returned from the request.
109
if status in [ HTTP_NO_CONTENT ]:
111
if status not in [ HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED ]:
112
return self.handleException(XendError(message))
115
def handleResponse(self, data):
116
"""Handle the data returned in response to the request.
118
if data is None: return None
119
typ = self.getHeader('Content-Type')
120
if typ != sxp.mime_type:
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)
134
def handleException(self, err):
135
"""Handle an exception during the request.
136
May be overridden in a subclass.
140
def getHeader(self, key):
141
"""Get a header from the response.
142
Case is ignored in the key.
144
@param key: header key
147
raise NotImplementedError()
149
class HttpXendClientProtocol(XendClientProtocol):
150
"""A synchronous xend client. This will make a request, wait for
151
the reply and return the result.
157
def makeConnection(self, url):
158
return httplib.HTTPConnection(url.location())
160
def makeRequest(self, url, method, args):
161
return XendRequest(url, method, args)
163
def xendRequest(self, url, method, args=None):
164
"""Make a request to xend.
166
@param url: xend request url
167
@param method: http method: POST or GET
168
@param args: request arguments (dict)
172
self.request = self.makeRequest(url, method, args)
173
conn = self.makeConnection(url)
175
if DEBUG: conn.set_debuglevel(1)
176
conn.request(method, url.fullpath(), self.request.data,
177
self.request.headers)
179
resp = conn.getresponse()
181
val = self.handleStatus(resp.version, resp.status,
187
val = self.handleResponse(data)
189
except httplib.BadStatusLine:
195
raise XendError("Received invalid response from Xend, twice.")
198
def getHeader(self, key):
199
return self.resp.getheader(key)
202
class UnixConnection(httplib.HTTPConnection):
203
"""Subclass of Python library HTTPConnection that uses a unix-domain socket.
206
def __init__(self, path):
207
httplib.HTTPConnection.__init__(self, 'localhost')
211
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
212
sock.connect(self.path)
215
class UnixXendClientProtocol(HttpXendClientProtocol):
216
"""A synchronous xend client using a unix-domain socket.
219
def __init__(self, path=None):
221
path = xoptions.get_xend_unix_path()
224
def makeConnection(self, _):
225
return UnixConnection(self.path)