Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / __init__.py @ 8c54338a

History | View | Annotate | Download (14.8 kB)

1 43ca98ee Giorgos Verigakis
# Copyright 2011-2012 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 c2b5da2f Stavros Sachtouris
from httplib import ResponseNotReady
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 8c54338a 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 34b88989 Stavros Sachtouris
            self.method,
148 34b88989 Stavros Sachtouris
            self.scheme,
149 34b88989 Stavros Sachtouris
            self.netloc,
150 34b88989 Stavros Sachtouris
            self.path,
151 34b88989 Stavros Sachtouris
            self))
152 21871fb2 Stavros Sachtouris
        for key, val in self.headers.items():
153 5fdccdec Stavros Sachtouris
            if (not self.LOG_TOKEN) and key.lower() == 'x-auth-token':
154 21871fb2 Stavros Sachtouris
                continue
155 5fdccdec Stavros Sachtouris
            sendlog.info('  %s: %s\t[%s]' % (key, val, self))
156 21871fb2 Stavros Sachtouris
        if self.data:
157 5fdccdec Stavros Sachtouris
            sendlog.info('data size:%s\t[%s]' % (len(self.data), self))
158 5fdccdec Stavros Sachtouris
            if self.LOG_DATA:
159 e9db8806 Stavros Sachtouris
                sendlog.info(self.data)
160 21871fb2 Stavros Sachtouris
        else:
161 5fdccdec Stavros Sachtouris
            sendlog.info('data size:0\t[%s]' % self)
162 34b88989 Stavros Sachtouris
        sendlog.info('')
163 21871fb2 Stavros Sachtouris
164 c2b5da2f Stavros Sachtouris
    def perform(self, conn):
165 c2b5da2f Stavros Sachtouris
        """
166 c2b5da2f Stavros Sachtouris
        :param conn: (httplib connection object)
167 c2b5da2f Stavros Sachtouris

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