Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / __init__.py @ 776b275c

History | View | Annotate | Download (15.7 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 a1c50326 Giorgos Verigakis
#
3 a1c50326 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 a1c50326 Giorgos Verigakis
# without modification, are permitted provided that the following
5 a1c50326 Giorgos Verigakis
# conditions are met:
6 a1c50326 Giorgos Verigakis
#
7 a1c50326 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 e742badc Stavros Sachtouris
#      copyright notice, self.list of conditions and the following
9 a1c50326 Giorgos Verigakis
#      disclaimer.
10 a1c50326 Giorgos Verigakis
#
11 a1c50326 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 e742badc Stavros Sachtouris
#      copyright notice, self.list of conditions and the following
13 a1c50326 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 a1c50326 Giorgos Verigakis
#      provided with the distribution.
15 a1c50326 Giorgos Verigakis
#
16 a1c50326 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a1c50326 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a1c50326 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a1c50326 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a1c50326 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a1c50326 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a1c50326 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a1c50326 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a1c50326 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a1c50326 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a1c50326 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a1c50326 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 a1c50326 Giorgos Verigakis
#
29 a1c50326 Giorgos Verigakis
# The views and conclusions contained in the software and
30 a1c50326 Giorgos Verigakis
# documentation are those of the authors and should not be
31 a1c50326 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 a1c50326 Giorgos Verigakis
# or implied, of GRNET S.A.
33 a1c50326 Giorgos Verigakis
34 545c6c29 Stavros Sachtouris
from urllib2 import quote, unquote
35 c2b5da2f Stavros Sachtouris
from urlparse import urlparse
36 2b74ab4a Stavros Sachtouris
from threading import Thread
37 de4f08ef Stavros Sachtouris
from json import dumps, loads
38 cad39033 Stavros Sachtouris
from time import time
39 ec928235 Stavros Sachtouris
from httplib import ResponseNotReady, HTTPException
40 c2b5da2f Stavros Sachtouris
from time import sleep
41 c2b5da2f Stavros Sachtouris
from random import random
42 c4d51ec9 Stavros Sachtouris
from logging import getLogger
43 c2b5da2f Stavros Sachtouris
44 c2b5da2f Stavros Sachtouris
from objpool.http import PooledHTTPConnection
45 61ca0ecd Stavros Sachtouris
46 6a0b1658 Giorgos Verigakis
47 21871fb2 Stavros Sachtouris
TIMEOUT = 60.0   # seconds
48 21871fb2 Stavros Sachtouris
HTTP_METHODS = ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'COPY', 'MOVE']
49 2406db97 Stavros Sachtouris
50 c4d51ec9 Stavros Sachtouris
log = getLogger(__name__)
51 c4d51ec9 Stavros Sachtouris
sendlog = getLogger('%s.send' % __name__)
52 c4d51ec9 Stavros Sachtouris
recvlog = getLogger('%s.recv' % __name__)
53 e8af27f4 Stavros Sachtouris
54 c2b5da2f Stavros Sachtouris
55 c2b5da2f Stavros Sachtouris
def _encode(v):
56 c2b5da2f Stavros Sachtouris
    if v and isinstance(v, unicode):
57 c2b5da2f Stavros Sachtouris
        return quote(v.encode('utf-8'))
58 c2b5da2f Stavros Sachtouris
    return v
59 c2b5da2f Stavros Sachtouris
60 3dabe5d2 Stavros Sachtouris
61 a1c50326 Giorgos Verigakis
class ClientError(Exception):
62 4f989909 Stavros Sachtouris
    def __init__(self, message, status=0, details=None):
63 e9db8806 Stavros Sachtouris
        log.debug('ClientError: msg[%s], sts[%s], dtl[%s]' % (
64 008a5db5 Stavros Sachtouris
            message,
65 008a5db5 Stavros Sachtouris
            status,
66 008a5db5 Stavros Sachtouris
            details))
67 de4f08ef Stavros Sachtouris
        try:
68 1f417830 Stavros Sachtouris
            message += '' if message and message[-1] == '\n' else '\n'
69 062b1d0a Stavros Sachtouris
            serv_stat, sep, new_msg = message.partition('{')
70 f4de4c91 Stavros Sachtouris
            new_msg = sep + new_msg[:-1 if new_msg.endswith('\n') else 0]
71 062b1d0a Stavros Sachtouris
            json_msg = loads(new_msg)
72 de4f08ef Stavros Sachtouris
            key = json_msg.keys()[0]
73 f4de4c91 Stavros Sachtouris
            serv_stat = serv_stat.strip()
74 062b1d0a Stavros Sachtouris
75 de4f08ef Stavros Sachtouris
            json_msg = json_msg[key]
76 f4de4c91 Stavros Sachtouris
            message = '%s %s (%s)\n' % (
77 f4de4c91 Stavros Sachtouris
                serv_stat,
78 f4de4c91 Stavros Sachtouris
                key,
79 f4de4c91 Stavros Sachtouris
                json_msg['message']) if (
80 f4de4c91 Stavros Sachtouris
                    'message' in json_msg) else '%s %s' % (serv_stat, key)
81 f4de4c91 Stavros Sachtouris
            status = json_msg.get('code', status)
82 de4f08ef Stavros Sachtouris
            if 'details' in json_msg:
83 de4f08ef Stavros Sachtouris
                if not details:
84 de4f08ef Stavros Sachtouris
                    details = []
85 f4de4c91 Stavros Sachtouris
                if not isinstance(details, list):
86 de4f08ef Stavros Sachtouris
                    details = [details]
87 de4f08ef Stavros Sachtouris
                if json_msg['details']:
88 de4f08ef Stavros Sachtouris
                    details.append(json_msg['details'])
89 f4de4c91 Stavros Sachtouris
        except Exception:
90 de4f08ef Stavros Sachtouris
            pass
91 f4de4c91 Stavros Sachtouris
        finally:
92 5dde4c83 Stavros Sachtouris
            while message.endswith('\n\n'):
93 5dde4c83 Stavros Sachtouris
                message = message[:-1]
94 f4de4c91 Stavros Sachtouris
            super(ClientError, self).__init__(message)
95 f4de4c91 Stavros Sachtouris
            self.status = status if isinstance(status, int) else 0
96 f4de4c91 Stavros Sachtouris
            self.details = details if details else []
97 a1c50326 Giorgos Verigakis
98 3dabe5d2 Stavros Sachtouris
99 5fdccdec Stavros Sachtouris
class Logged(object):
100 5fdccdec Stavros Sachtouris
101 5fdccdec Stavros Sachtouris
    LOG_TOKEN = False
102 5fdccdec Stavros Sachtouris
    LOG_DATA = False
103 5fdccdec Stavros Sachtouris
104 5fdccdec Stavros Sachtouris
105 5fdccdec Stavros Sachtouris
class RequestManager(Logged):
106 c2b5da2f Stavros Sachtouris
    """Handle http request information"""
107 c2b5da2f Stavros Sachtouris
108 c2b5da2f Stavros Sachtouris
    def _connection_info(self, url, path, params={}):
109 c2b5da2f Stavros Sachtouris
        """ Set self.url to scheme://netloc/?params
110 c2b5da2f Stavros Sachtouris
        :param url: (str or unicode) The service url
111 c2b5da2f Stavros Sachtouris

112 c2b5da2f Stavros Sachtouris
        :param path: (str or unicode) The service path (url/path)
113 c2b5da2f Stavros Sachtouris

114 c2b5da2f Stavros Sachtouris
        :param params: (dict) Parameters to add to final url
115 c2b5da2f Stavros Sachtouris

116 c2b5da2f Stavros Sachtouris
        :returns: (scheme, netloc)
117 c2b5da2f Stavros Sachtouris
        """
118 819311d3 Stavros Sachtouris
        url = _encode(str(url)) if url else 'http://127.0.0.1/'
119 c2b5da2f Stavros Sachtouris
        url += '' if url.endswith('/') else '/'
120 c2b5da2f Stavros Sachtouris
        if path:
121 c2b5da2f Stavros Sachtouris
            url += _encode(path[1:] if path.startswith('/') else path)
122 7fa5c263 Stavros Sachtouris
        delim = '?'
123 7fa5c263 Stavros Sachtouris
        for key, val in params.items():
124 c2b5da2f Stavros Sachtouris
            val = _encode(val)
125 7fa5c263 Stavros Sachtouris
            url += '%s%s%s' % (delim, key, ('=%s' % val) if val else '')
126 7fa5c263 Stavros Sachtouris
            delim = '&'
127 c2b5da2f Stavros Sachtouris
        parsed = urlparse(url)
128 c2b5da2f Stavros Sachtouris
        self.url = url
129 c2b5da2f Stavros Sachtouris
        self.path = parsed.path or '/'
130 c2b5da2f Stavros Sachtouris
        if parsed.query:
131 c2b5da2f Stavros Sachtouris
            self.path += '?%s' % parsed.query
132 c2b5da2f Stavros Sachtouris
        return (parsed.scheme, parsed.netloc)
133 c2b5da2f Stavros Sachtouris
134 c2b5da2f Stavros Sachtouris
    def __init__(
135 c2b5da2f Stavros Sachtouris
            self, method, url, path,
136 c2b5da2f Stavros Sachtouris
            data=None, headers={}, params={}):
137 c2b5da2f Stavros Sachtouris
        method = method.upper()
138 c2b5da2f Stavros Sachtouris
        assert method in HTTP_METHODS, 'Invalid http method %s' % method
139 c2b5da2f Stavros Sachtouris
        if headers:
140 c2b5da2f Stavros Sachtouris
            assert isinstance(headers, dict)
141 7fa5c263 Stavros Sachtouris
        self.headers = dict(headers)
142 c2b5da2f Stavros Sachtouris
        self.method, self.data = method, data
143 c2b5da2f Stavros Sachtouris
        self.scheme, self.netloc = self._connection_info(url, path, params)
144 c2b5da2f Stavros Sachtouris
145 c4563114 Stavros Sachtouris
    def dump_log(self):
146 5fdccdec Stavros Sachtouris
        sendlog.info('%s %s://%s%s\t[%s]' % (
147 201baa17 Stavros Sachtouris
            self.method, self.scheme, self.netloc, self.path, self))
148 21871fb2 Stavros Sachtouris
        for key, val in self.headers.items():
149 5fdccdec Stavros Sachtouris
            if (not self.LOG_TOKEN) and key.lower() == 'x-auth-token':
150 21871fb2 Stavros Sachtouris
                continue
151 5fdccdec Stavros Sachtouris
            sendlog.info('  %s: %s\t[%s]' % (key, val, self))
152 21871fb2 Stavros Sachtouris
        if self.data:
153 5fdccdec Stavros Sachtouris
            sendlog.info('data size:%s\t[%s]' % (len(self.data), self))
154 5fdccdec Stavros Sachtouris
            if self.LOG_DATA:
155 e9db8806 Stavros Sachtouris
                sendlog.info(self.data)
156 21871fb2 Stavros Sachtouris
        else:
157 5fdccdec Stavros Sachtouris
            sendlog.info('data size:0\t[%s]' % self)
158 34b88989 Stavros Sachtouris
        sendlog.info('')
159 21871fb2 Stavros Sachtouris
160 c2b5da2f Stavros Sachtouris
    def perform(self, conn):
161 c2b5da2f Stavros Sachtouris
        """
162 c2b5da2f Stavros Sachtouris
        :param conn: (httplib connection object)
163 c2b5da2f Stavros Sachtouris

164 c2b5da2f Stavros Sachtouris
        :returns: (HTTPResponse)
165 c2b5da2f Stavros Sachtouris
        """
166 c2b5da2f Stavros Sachtouris
        conn.request(
167 c2b5da2f Stavros Sachtouris
            method=str(self.method.upper()),
168 c2b5da2f Stavros Sachtouris
            url=str(self.path),
169 c2b5da2f Stavros Sachtouris
            headers=self.headers,
170 c2b5da2f Stavros Sachtouris
            body=self.data)
171 c4563114 Stavros Sachtouris
        self.dump_log()
172 f47417e7 Stavros Sachtouris
        keep_trying = TIMEOUT
173 f8eea8ec Stavros Sachtouris
        while keep_trying > 0:
174 c2b5da2f Stavros Sachtouris
            try:
175 c2b5da2f Stavros Sachtouris
                return conn.getresponse()
176 c2b5da2f Stavros Sachtouris
            except ResponseNotReady:
177 f8eea8ec Stavros Sachtouris
                wait = 0.03 * random()
178 f8eea8ec Stavros Sachtouris
                sleep(wait)
179 f8eea8ec Stavros Sachtouris
                keep_trying -= wait
180 21871fb2 Stavros Sachtouris
        logmsg = 'Kamaki Timeout %s %s\t[%s]' % (self.method, self.path, self)
181 34b88989 Stavros Sachtouris
        recvlog.debug(logmsg)
182 f8eea8ec Stavros Sachtouris
        raise ClientError('HTTPResponse takes too long - kamaki timeout')
183 c2b5da2f Stavros Sachtouris
184 c2b5da2f Stavros Sachtouris
185 5fdccdec Stavros Sachtouris
class ResponseManager(Logged):
186 c2b5da2f Stavros Sachtouris
    """Manage the http request and handle the response data, headers, etc."""
187 c2b5da2f Stavros Sachtouris
188 ec928235 Stavros Sachtouris
    def __init__(self, request, poolsize=None, connection_retry_limit=0):
189 c2b5da2f Stavros Sachtouris
        """
190 c2b5da2f Stavros Sachtouris
        :param request: (RequestManager)
191 ec928235 Stavros Sachtouris

192 ec928235 Stavros Sachtouris
        :param poolsize: (int) the size of the connection pool
193 ec928235 Stavros Sachtouris

194 ec928235 Stavros Sachtouris
        :param connection_retry_limit: (int)
195 c2b5da2f Stavros Sachtouris
        """
196 ec928235 Stavros Sachtouris
        self.CONNECTION_TRY_LIMIT = 1 + connection_retry_limit
197 c2b5da2f Stavros Sachtouris
        self.request = request
198 c2b5da2f Stavros Sachtouris
        self._request_performed = False
199 c2b5da2f Stavros Sachtouris
        self.poolsize = poolsize
200 c2b5da2f Stavros Sachtouris
201 c2b5da2f Stavros Sachtouris
    def _get_response(self):
202 c2b5da2f Stavros Sachtouris
        if self._request_performed:
203 c2b5da2f Stavros Sachtouris
            return
204 c2b5da2f Stavros Sachtouris
205 c2b5da2f Stavros Sachtouris
        pool_kw = dict(size=self.poolsize) if self.poolsize else dict()
206 ec928235 Stavros Sachtouris
        for retries in range(1, self.CONNECTION_TRY_LIMIT + 1):
207 ec928235 Stavros Sachtouris
            try:
208 ec928235 Stavros Sachtouris
                with PooledHTTPConnection(
209 ec928235 Stavros Sachtouris
                        self.request.netloc, self.request.scheme,
210 ec928235 Stavros Sachtouris
                        **pool_kw) as connection:
211 ec928235 Stavros Sachtouris
                    self.request.LOG_TOKEN = self.LOG_TOKEN
212 ec928235 Stavros Sachtouris
                    self.request.LOG_DATA = self.LOG_DATA
213 ec928235 Stavros Sachtouris
                    r = self.request.perform(connection)
214 ec928235 Stavros Sachtouris
                    recvlog.info('\n%s <-- %s <-- [req: %s]\n' % (
215 ec928235 Stavros Sachtouris
                        self, r, self.request))
216 ec928235 Stavros Sachtouris
                    self._request_performed = True
217 ec928235 Stavros Sachtouris
                    self._status_code, self._status = r.status, unquote(
218 ec928235 Stavros Sachtouris
                        r.reason)
219 ec928235 Stavros Sachtouris
                    recvlog.info(
220 ec928235 Stavros Sachtouris
                        '%d %s\t[p: %s]' % (
221 ec928235 Stavros Sachtouris
                            self.status_code, self.status, self))
222 ec928235 Stavros Sachtouris
                    self._headers = dict()
223 ec928235 Stavros Sachtouris
                    for k, v in r.getheaders():
224 ec928235 Stavros Sachtouris
                        if (not self.LOG_TOKEN) and (
225 ec928235 Stavros Sachtouris
                                k.lower() == 'x-auth-token'):
226 ec928235 Stavros Sachtouris
                            continue
227 ec928235 Stavros Sachtouris
                        v = unquote(v)
228 ec928235 Stavros Sachtouris
                        self._headers[k] = v
229 ec928235 Stavros Sachtouris
                        recvlog.info('  %s: %s\t[p: %s]' % (k, v, self))
230 ec928235 Stavros Sachtouris
                    self._content = r.read()
231 ec928235 Stavros Sachtouris
                    recvlog.info('data size: %s\t[p: %s]' % (
232 ec928235 Stavros Sachtouris
                        len(self._content) if self._content else 0,
233 ec928235 Stavros Sachtouris
                        self))
234 ec928235 Stavros Sachtouris
                    if self.LOG_DATA and self._content:
235 ec928235 Stavros Sachtouris
                        recvlog.info('%s\t[p: %s]' % (self._content, self))
236 ec928235 Stavros Sachtouris
                break
237 ec928235 Stavros Sachtouris
            except Exception as err:
238 ec928235 Stavros Sachtouris
                if isinstance(err, HTTPException):
239 ec928235 Stavros Sachtouris
                    if retries >= self.CONNECTION_TRY_LIMIT:
240 ec928235 Stavros Sachtouris
                        raise ClientError(
241 ec928235 Stavros Sachtouris
                            'Connection to %s failed %s times (%s: %s )' % (
242 ec928235 Stavros Sachtouris
                                self.request.url, retries, type(err), err))
243 ec928235 Stavros Sachtouris
                else:
244 ec928235 Stavros Sachtouris
                    from traceback import format_stack
245 ec928235 Stavros Sachtouris
                    recvlog.debug(
246 ec928235 Stavros Sachtouris
                        '\n'.join(['%s' % type(err)] + format_stack()))
247 ec928235 Stavros Sachtouris
                    raise ClientError(
248 ec928235 Stavros Sachtouris
                        'Failed while http-connecting to %s (%s)' % (
249 ec928235 Stavros Sachtouris
                            self.request.url,
250 ec928235 Stavros Sachtouris
                            err))
251 c2b5da2f Stavros Sachtouris
252 c2b5da2f Stavros Sachtouris
    @property
253 c2b5da2f Stavros Sachtouris
    def status_code(self):
254 c2b5da2f Stavros Sachtouris
        self._get_response()
255 c2b5da2f Stavros Sachtouris
        return self._status_code
256 c2b5da2f Stavros Sachtouris
257 c2b5da2f Stavros Sachtouris
    @property
258 c2b5da2f Stavros Sachtouris
    def status(self):
259 c2b5da2f Stavros Sachtouris
        self._get_response()
260 c2b5da2f Stavros Sachtouris
        return self._status
261 c2b5da2f Stavros Sachtouris
262 c2b5da2f Stavros Sachtouris
    @property
263 c2b5da2f Stavros Sachtouris
    def headers(self):
264 c2b5da2f Stavros Sachtouris
        self._get_response()
265 c2b5da2f Stavros Sachtouris
        return self._headers
266 c2b5da2f Stavros Sachtouris
267 c2b5da2f Stavros Sachtouris
    @property
268 c2b5da2f Stavros Sachtouris
    def content(self):
269 c2b5da2f Stavros Sachtouris
        self._get_response()
270 c2b5da2f Stavros Sachtouris
        return self._content
271 c2b5da2f Stavros Sachtouris
272 c2b5da2f Stavros Sachtouris
    @property
273 c2b5da2f Stavros Sachtouris
    def text(self):
274 c2b5da2f Stavros Sachtouris
        """
275 c2b5da2f Stavros Sachtouris
        :returns: (str) content
276 c2b5da2f Stavros Sachtouris
        """
277 c2b5da2f Stavros Sachtouris
        self._get_response()
278 c2b5da2f Stavros Sachtouris
        return '%s' % self._content
279 c2b5da2f Stavros Sachtouris
280 c2b5da2f Stavros Sachtouris
    @property
281 c2b5da2f Stavros Sachtouris
    def json(self):
282 c2b5da2f Stavros Sachtouris
        """
283 c2b5da2f Stavros Sachtouris
        :returns: (dict) squeezed from json-formated content
284 c2b5da2f Stavros Sachtouris
        """
285 c2b5da2f Stavros Sachtouris
        self._get_response()
286 c2b5da2f Stavros Sachtouris
        try:
287 c2b5da2f Stavros Sachtouris
            return loads(self._content)
288 c2b5da2f Stavros Sachtouris
        except ValueError as err:
289 f8eea8ec Stavros Sachtouris
            raise ClientError('Response not formated in JSON - %s' % err)
290 c2b5da2f Stavros Sachtouris
291 c2b5da2f Stavros Sachtouris
292 2b74ab4a Stavros Sachtouris
class SilentEvent(Thread):
293 34b88989 Stavros Sachtouris
    """Thread-run method(*args, **kwargs)"""
294 2b74ab4a Stavros Sachtouris
    def __init__(self, method, *args, **kwargs):
295 2b74ab4a Stavros Sachtouris
        super(self.__class__, self).__init__()
296 2b74ab4a Stavros Sachtouris
        self.method = method
297 2b74ab4a Stavros Sachtouris
        self.args = args
298 2b74ab4a Stavros Sachtouris
        self.kwargs = kwargs
299 2b74ab4a Stavros Sachtouris
300 2b74ab4a Stavros Sachtouris
    @property
301 2b74ab4a Stavros Sachtouris
    def exception(self):
302 2b74ab4a Stavros Sachtouris
        return getattr(self, '_exception', False)
303 2b74ab4a Stavros Sachtouris
304 2b74ab4a Stavros Sachtouris
    @property
305 2b74ab4a Stavros Sachtouris
    def value(self):
306 2b74ab4a Stavros Sachtouris
        return getattr(self, '_value', None)
307 2b74ab4a Stavros Sachtouris
308 2b74ab4a Stavros Sachtouris
    def run(self):
309 2b74ab4a Stavros Sachtouris
        try:
310 2b74ab4a Stavros Sachtouris
            self._value = self.method(*(self.args), **(self.kwargs))
311 2b74ab4a Stavros Sachtouris
        except Exception as e:
312 7644c38e Stavros Sachtouris
            recvlog.debug('Thread %s got exception %s\n<%s %s' % (
313 7644c38e Stavros Sachtouris
                self,
314 7644c38e Stavros Sachtouris
                type(e),
315 7644c38e Stavros Sachtouris
                e.status if isinstance(e, ClientError) else '',
316 7644c38e Stavros Sachtouris
                e))
317 2b74ab4a Stavros Sachtouris
            self._exception = e
318 2b74ab4a Stavros Sachtouris
319 6069b53b Stavros Sachtouris
320 6a0b1658 Giorgos Verigakis
class Client(object):
321 cad39033 Stavros Sachtouris
322 34b88989 Stavros Sachtouris
    MAX_THREADS = 7
323 b773795c Stavros Sachtouris
    DATE_FORMATS = ['%a %b %d %H:%M:%S %Y', ]
324 5fdccdec Stavros Sachtouris
    LOG_TOKEN = False
325 776b275c Stavros Sachtouris
    LOG_DATA = False
326 ec928235 Stavros Sachtouris
    CONNECTION_RETRY_LIMIT = 0
327 34b88989 Stavros Sachtouris
328 c2b5da2f Stavros Sachtouris
    def __init__(self, base_url, token):
329 528550d9 Stavros Sachtouris
        assert base_url, 'No base_url for client %s' % self
330 6a0b1658 Giorgos Verigakis
        self.base_url = base_url
331 6c62a96d Giorgos Verigakis
        self.token = token
332 c2b5da2f Stavros Sachtouris
        self.headers, self.params = dict(), dict()
333 6c62a96d Giorgos Verigakis
334 cad39033 Stavros Sachtouris
    def _init_thread_limit(self, limit=1):
335 9c6c3d69 Stavros Sachtouris
        assert isinstance(limit, int) and limit > 0, 'Thread limit not a +int'
336 cad39033 Stavros Sachtouris
        self._thread_limit = limit
337 cad39033 Stavros Sachtouris
        self._elapsed_old = 0.0
338 cad39033 Stavros Sachtouris
        self._elapsed_new = 0.0
339 cad39033 Stavros Sachtouris
340 cad39033 Stavros Sachtouris
    def _watch_thread_limit(self, threadlist):
341 9c6c3d69 Stavros Sachtouris
        self._thread_limit = getattr(self, '_thread_limit', 1)
342 9c6c3d69 Stavros Sachtouris
        self._elapsed_new = getattr(self, '_elapsed_new', 0.0)
343 9c6c3d69 Stavros Sachtouris
        self._elapsed_old = getattr(self, '_elapsed_old', 0.0)
344 16c895db Stavros Sachtouris
        recvlog.debug('# running threads: %s' % len(threadlist))
345 9c6c3d69 Stavros Sachtouris
        if self._elapsed_old and self._elapsed_old >= self._elapsed_new and (
346 16b0afe6 Stavros Sachtouris
                self._thread_limit < self.MAX_THREADS):
347 2005b18e Stavros Sachtouris
            self._thread_limit += 1
348 9c6c3d69 Stavros Sachtouris
        elif self._elapsed_old <= self._elapsed_new and self._thread_limit > 1:
349 cad39033 Stavros Sachtouris
            self._thread_limit -= 1
350 cad39033 Stavros Sachtouris
351 cad39033 Stavros Sachtouris
        self._elapsed_old = self._elapsed_new
352 cad39033 Stavros Sachtouris
        if len(threadlist) >= self._thread_limit:
353 cad39033 Stavros Sachtouris
            self._elapsed_new = 0.0
354 cad39033 Stavros Sachtouris
            for thread in threadlist:
355 cad39033 Stavros Sachtouris
                begin_time = time()
356 cad39033 Stavros Sachtouris
                thread.join()
357 cad39033 Stavros Sachtouris
                self._elapsed_new += time() - begin_time
358 cad39033 Stavros Sachtouris
            self._elapsed_new = self._elapsed_new / len(threadlist)
359 cad39033 Stavros Sachtouris
            return []
360 cad39033 Stavros Sachtouris
        return threadlist
361 cad39033 Stavros Sachtouris
362 6ad245d5 Stavros Sachtouris
    def _raise_for_status(self, r):
363 e9db8806 Stavros Sachtouris
        log.debug('raise err from [%s] of type[%s]' % (r, type(r)))
364 5dde4c83 Stavros Sachtouris
        status_msg = getattr(r, 'status', None) or ''
365 d804de82 Stavros Sachtouris
        try:
366 7966ffb8 Stavros Sachtouris
            message = '%s %s\n' % (status_msg, r.text)
367 d804de82 Stavros Sachtouris
        except:
368 7966ffb8 Stavros Sachtouris
            message = '%s %s\n' % (status_msg, r)
369 7966ffb8 Stavros Sachtouris
        status = getattr(r, 'status_code', getattr(r, 'status', 0))
370 7966ffb8 Stavros Sachtouris
        raise ClientError(message, status=status)
371 6a0b1658 Giorgos Verigakis
372 4adfa919 Stavros Sachtouris
    def set_header(self, name, value, iff=True):
373 3dabe5d2 Stavros Sachtouris
        """Set a header 'name':'value'"""
374 4adfa919 Stavros Sachtouris
        if value is not None and iff:
375 82903313 Stavros Sachtouris
            self.headers[name] = unicode(value)
376 2f749e6e Stavros Sachtouris
377 2f749e6e Stavros Sachtouris
    def set_param(self, name, value=None, iff=True):
378 2f749e6e Stavros Sachtouris
        if iff:
379 82903313 Stavros Sachtouris
            self.params[name] = unicode(value)
380 2f749e6e Stavros Sachtouris
381 de73876b Stavros Sachtouris
    def request(
382 c2b5da2f Stavros Sachtouris
            self, method, path,
383 c2b5da2f Stavros Sachtouris
            async_headers=dict(), async_params=dict(),
384 24ff0a35 Stavros Sachtouris
            **kwargs):
385 34b88989 Stavros Sachtouris
        """Commit an HTTP request to base_url/path
386 34b88989 Stavros Sachtouris
        Requests are commited to and performed by Request/ResponseManager
387 34b88989 Stavros Sachtouris
        These classes perform a lazy http request. Present method, by default,
388 34b88989 Stavros Sachtouris
        enforces them to perform the http call. Hint: call present method with
389 34b88989 Stavros Sachtouris
        success=None to get a non-performed ResponseManager object.
390 34b88989 Stavros Sachtouris
        """
391 6a6175c0 Stavros Sachtouris
        assert isinstance(method, str) or isinstance(method, unicode)
392 6a6175c0 Stavros Sachtouris
        assert method
393 6a6175c0 Stavros Sachtouris
        assert isinstance(path, str) or isinstance(path, unicode)
394 d726b3d0 Stavros Sachtouris
        try:
395 c2b5da2f Stavros Sachtouris
            headers = dict(self.headers)
396 c2b5da2f Stavros Sachtouris
            headers.update(async_headers)
397 c2b5da2f Stavros Sachtouris
            params = dict(self.params)
398 c2b5da2f Stavros Sachtouris
            params.update(async_params)
399 2f749e6e Stavros Sachtouris
            success = kwargs.pop('success', 200)
400 2f749e6e Stavros Sachtouris
            data = kwargs.pop('data', None)
401 c2b5da2f Stavros Sachtouris
            headers.setdefault('X-Auth-Token', self.token)
402 2f749e6e Stavros Sachtouris
            if 'json' in kwargs:
403 de4f08ef Stavros Sachtouris
                data = dumps(kwargs.pop('json'))
404 c2b5da2f Stavros Sachtouris
                headers.setdefault('Content-Type', 'application/json')
405 2f749e6e Stavros Sachtouris
            if data:
406 c2b5da2f Stavros Sachtouris
                headers.setdefault('Content-Length', '%s' % len(data))
407 c2b5da2f Stavros Sachtouris
408 a863029c Stavros Sachtouris
            sendlog.debug('\n\nCMT %s@%s\t[%s]', method, self.base_url, self)
409 c2b5da2f Stavros Sachtouris
            req = RequestManager(
410 c2b5da2f Stavros Sachtouris
                method, self.base_url, path,
411 c2b5da2f Stavros Sachtouris
                data=data, headers=headers, params=params)
412 21871fb2 Stavros Sachtouris
            #  req.log()
413 ec928235 Stavros Sachtouris
            r = ResponseManager(
414 ec928235 Stavros Sachtouris
                req, connection_retry_limit=self.CONNECTION_RETRY_LIMIT)
415 5fdccdec Stavros Sachtouris
            r.LOG_TOKEN, r.LOG_DATA = self.LOG_TOKEN, self.LOG_DATA
416 6a6175c0 Stavros Sachtouris
        finally:
417 c2b5da2f Stavros Sachtouris
            self.headers = dict()
418 c2b5da2f Stavros Sachtouris
            self.params = dict()
419 0238c167 Stavros Sachtouris
420 0238c167 Stavros Sachtouris
        if success is not None:
421 a037fd61 Stavros Sachtouris
            # Success can either be an int or a collection
422 0238c167 Stavros Sachtouris
            success = (success,) if isinstance(success, int) else success
423 0238c167 Stavros Sachtouris
            if r.status_code not in success:
424 0238c167 Stavros Sachtouris
                self._raise_for_status(r)
425 d726b3d0 Stavros Sachtouris
        return r
426 a1c50326 Giorgos Verigakis
427 6a0b1658 Giorgos Verigakis
    def delete(self, path, **kwargs):
428 6a0b1658 Giorgos Verigakis
        return self.request('delete', path, **kwargs)
429 6a0b1658 Giorgos Verigakis
430 6a0b1658 Giorgos Verigakis
    def get(self, path, **kwargs):
431 6a0b1658 Giorgos Verigakis
        return self.request('get', path, **kwargs)
432 6a0b1658 Giorgos Verigakis
433 6a0b1658 Giorgos Verigakis
    def head(self, path, **kwargs):
434 6a0b1658 Giorgos Verigakis
        return self.request('head', path, **kwargs)
435 6a0b1658 Giorgos Verigakis
436 6a0b1658 Giorgos Verigakis
    def post(self, path, **kwargs):
437 6a0b1658 Giorgos Verigakis
        return self.request('post', path, **kwargs)
438 6a0b1658 Giorgos Verigakis
439 6a0b1658 Giorgos Verigakis
    def put(self, path, **kwargs):
440 6a0b1658 Giorgos Verigakis
        return self.request('put', path, **kwargs)
441 6a0b1658 Giorgos Verigakis
442 4adfa919 Stavros Sachtouris
    def copy(self, path, **kwargs):
443 4adfa919 Stavros Sachtouris
        return self.request('copy', path, **kwargs)
444 4adfa919 Stavros Sachtouris
445 4adfa919 Stavros Sachtouris
    def move(self, path, **kwargs):
446 4adfa919 Stavros Sachtouris
        return self.request('move', path, **kwargs)