ia64/xen-unstable

view tools/python/xen/xend/XendProtocol.py @ 12725:36fe7ca48e54

Tidy up the creation of directories that Xend needs. This avoids potential
races in this creation.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Fri Dec 01 11:32:32 2006 +0000 (2006-12-01)
parents 6ffb8705f894
children a18ae238eb53
line source
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 #============================================================================
19 import socket
20 import httplib
21 import time
22 import types
24 from encode import *
25 from xen.xend import sxp
27 from xen.xend import XendRoot
29 DEBUG = 0
31 HTTP_OK = 200
32 HTTP_CREATED = 201
33 HTTP_ACCEPTED = 202
34 HTTP_NO_CONTENT = 204
37 xroot = XendRoot.instance()
40 class XendError(RuntimeError):
41 """Error class for 'expected errors' when talking to xend.
42 """
43 pass
45 class XendRequest:
46 """A request to xend.
47 """
49 def __init__(self, url, method, args):
50 """Create a request. Sets up the headers, argument data, and the
51 url.
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]
66 self.headers = hdr
67 self.data = data
68 self.url = url
69 self.method = method
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.
78 @param url: xend request url
79 @param method: http method: POST or GET
80 @param args: request arguments (dict)
81 """
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
87 nasty side-effects.
89 @param url: xend request url
90 @param data: request arguments (dict)
91 """
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
98 effect.
100 @param url: xend request url
101 @param args: request arguments (dict)
102 """
103 return self.xendRequest(url, "POST", args)
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'
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
134 def handleException(self, err):
135 """Handle an exception during the request.
136 May be overridden in a subclass.
137 """
138 raise err
140 def getHeader(self, key):
141 """Get a header from the response.
142 Case is ignored in the key.
144 @param key: header key
145 @return: header
146 """
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.
152 """
154 resp = None
155 request = None
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)
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()
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.
204 """
206 def __init__(self, path):
207 httplib.HTTPConnection.__init__(self, 'localhost')
208 self.path = path
210 def connect(self):
211 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
212 sock.connect(self.path)
213 self.sock = sock
215 class UnixXendClientProtocol(HttpXendClientProtocol):
216 """A synchronous xend client using a unix-domain socket.
217 """
219 def __init__(self, path=None):
220 if path is None:
221 path = xroot.get_xend_unix_path()
222 self.path = path
224 def makeConnection(self, _):
225 return UnixConnection(self.path)