root / kamaki / clients / __init__.py @ b335416e
History | View | Annotate | Download (6.1 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 | 6a0b1658 | Giorgos Verigakis | import json |
35 | 6a0b1658 | Giorgos Verigakis | import logging |
36 | c270fe96 | Stavros Sachtouris | from kamaki.clients.connection import HTTPConnectionError |
37 | c270fe96 | Stavros Sachtouris | from kamaki.clients.connection.kamakicon import KamakiHTTPConnection |
38 | 6a0b1658 | Giorgos Verigakis | |
39 | 6a0b1658 | Giorgos Verigakis | sendlog = logging.getLogger('clients.send')
|
40 | 6a0b1658 | Giorgos Verigakis | recvlog = logging.getLogger('clients.recv')
|
41 | 6a0b1658 | Giorgos Verigakis | |
42 | a1c50326 | Giorgos Verigakis | class ClientError(Exception): |
43 | a1c50326 | Giorgos Verigakis | def __init__(self, message, status=0, details=''): |
44 | 59dec327 | Giorgos Verigakis | super(ClientError, self).__init__(message, status, details) |
45 | a1c50326 | Giorgos Verigakis | self.message = message
|
46 | a1c50326 | Giorgos Verigakis | self.status = status
|
47 | a1c50326 | Giorgos Verigakis | self.details = details
|
48 | a1c50326 | Giorgos Verigakis | |
49 | 6a0b1658 | Giorgos Verigakis | class Client(object): |
50 | a2e8e549 | Stavros Sachtouris | |
51 | 5b263ba2 | Stavros Sachtouris | def __init__(self, base_url, token, http_client=KamakiHTTPConnection()): |
52 | 6a0b1658 | Giorgos Verigakis | self.base_url = base_url
|
53 | 6c62a96d | Giorgos Verigakis | self.token = token
|
54 | e742badc | Stavros Sachtouris | self.headers = {}
|
55 | 16ce7b91 | Stavros Sachtouris | self.DATE_FORMATS = ["%a %b %d %H:%M:%S %Y", |
56 | 16ce7b91 | Stavros Sachtouris | "%A, %d-%b-%y %H:%M:%S GMT",
|
57 | 16ce7b91 | Stavros Sachtouris | "%a, %d %b %Y %H:%M:%S GMT"]
|
58 | a2e8e549 | Stavros Sachtouris | self.http_client = http_client
|
59 | 6c62a96d | Giorgos Verigakis | |
60 | 6ad245d5 | Stavros Sachtouris | def _raise_for_status(self, r): |
61 | adb1dde9 | Stavros Sachtouris | message = "%s" % r.status
|
62 | d804de82 | Stavros Sachtouris | try:
|
63 | d804de82 | Stavros Sachtouris | details = r.text |
64 | d804de82 | Stavros Sachtouris | except:
|
65 | d804de82 | Stavros Sachtouris | details = ''
|
66 | adb1dde9 | Stavros Sachtouris | raise ClientError(message=message, status=r.status_code, details=details)
|
67 | 6a0b1658 | Giorgos Verigakis | |
68 | 4adfa919 | Stavros Sachtouris | def set_header(self, name, value, iff=True): |
69 | 4adfa919 | Stavros Sachtouris | """Set a header 'name':'value' provided value is not None and iff is True"""
|
70 | 4adfa919 | Stavros Sachtouris | if value is not None and iff: |
71 | 2f749e6e | Stavros Sachtouris | self.http_client.set_header(name, value)
|
72 | 2f749e6e | Stavros Sachtouris | |
73 | 2f749e6e | Stavros Sachtouris | def set_param(self, name, value=None, iff=True): |
74 | 2f749e6e | Stavros Sachtouris | if iff:
|
75 | 2f749e6e | Stavros Sachtouris | self.http_client.set_param(name, value)
|
76 | 2f749e6e | Stavros Sachtouris | |
77 | 2f749e6e | Stavros Sachtouris | def set_default_header(self, name, value): |
78 | 2f749e6e | Stavros Sachtouris | self.http_client.headers.setdefault(name, value)
|
79 | e742badc | Stavros Sachtouris | |
80 | 6ce9fc72 | Stavros Sachtouris | def request(self, |
81 | 6ce9fc72 | Stavros Sachtouris | method, |
82 | 6ce9fc72 | Stavros Sachtouris | path, |
83 | 6ce9fc72 | Stavros Sachtouris | async_headers={}, |
84 | 6ce9fc72 | Stavros Sachtouris | async_params={}, |
85 | 6ce9fc72 | Stavros Sachtouris | **kwargs): |
86 | 9a7efb0d | Stavros Sachtouris | """In threaded/asynchronous requests, headers and params are not safe
|
87 | 9a7efb0d | Stavros Sachtouris | Therefore, the standard self.set_header/param system can be used only for
|
88 | 9a7efb0d | Stavros Sachtouris | headers and params that are common for all requests. All other params and
|
89 | 9a7efb0d | Stavros Sachtouris | headers should passes as
|
90 | 9a7efb0d | Stavros Sachtouris | @param async_headers
|
91 | 9a7efb0d | Stavros Sachtouris | @async_params
|
92 | 9a7efb0d | Stavros Sachtouris | E.g. in most queries the 'X-Auth-Token' header might be the same for all, but the
|
93 | 9a7efb0d | Stavros Sachtouris | 'Range' header might be different from request to request.
|
94 | 9a7efb0d | Stavros Sachtouris | """
|
95 | 1785ad41 | Stavros Sachtouris | |
96 | d726b3d0 | Stavros Sachtouris | try:
|
97 | 2f749e6e | Stavros Sachtouris | success = kwargs.pop('success', 200) |
98 | 2f749e6e | Stavros Sachtouris | |
99 | 2f749e6e | Stavros Sachtouris | data = kwargs.pop('data', None) |
100 | 2f749e6e | Stavros Sachtouris | self.set_default_header('X-Auth-Token', self.token) |
101 | 2f749e6e | Stavros Sachtouris | |
102 | 2f749e6e | Stavros Sachtouris | if 'json' in kwargs: |
103 | 2f749e6e | Stavros Sachtouris | data = json.dumps(kwargs.pop('json'))
|
104 | 2f749e6e | Stavros Sachtouris | self.set_default_header('Content-Type', 'application/json') |
105 | 2f749e6e | Stavros Sachtouris | if data:
|
106 | 2f749e6e | Stavros Sachtouris | self.set_default_header('Content-Length', unicode(len(data))) |
107 | 2f749e6e | Stavros Sachtouris | |
108 | 2f749e6e | Stavros Sachtouris | self.http_client.url = self.base_url + path |
109 | 9a7efb0d | Stavros Sachtouris | r = self.http_client.perform_request(method, data, async_headers, async_params)
|
110 | 2f749e6e | Stavros Sachtouris | |
111 | 2f749e6e | Stavros Sachtouris | req = self.http_client
|
112 | 5b263ba2 | Stavros Sachtouris | sendlog.info('%s %s', method, req.url)
|
113 | 1785ad41 | Stavros Sachtouris | headers = dict(req.headers)
|
114 | 1785ad41 | Stavros Sachtouris | headers.update(async_headers) |
115 | 1785ad41 | Stavros Sachtouris | |
116 | 1785ad41 | Stavros Sachtouris | for key, val in headers.items(): |
117 | 1785ad41 | Stavros Sachtouris | sendlog.info('\t%s: %s', key, val)
|
118 | 2f749e6e | Stavros Sachtouris | sendlog.info('')
|
119 | 2f749e6e | Stavros Sachtouris | if data:
|
120 | 2f749e6e | Stavros Sachtouris | sendlog.info('%s', data)
|
121 | 2f749e6e | Stavros Sachtouris | |
122 | 2f749e6e | Stavros Sachtouris | recvlog.info('%d %s', r.status_code, r.status)
|
123 | 2f749e6e | Stavros Sachtouris | for key, val in r.headers.items(): |
124 | 2f749e6e | Stavros Sachtouris | recvlog.info('%s: %s', key, val)
|
125 | 9a7efb0d | Stavros Sachtouris | #if r.content:
|
126 | 9a7efb0d | Stavros Sachtouris | # recvlog.debug(r.content)
|
127 | 2f749e6e | Stavros Sachtouris | |
128 | 2f749e6e | Stavros Sachtouris | if success is not None: |
129 | 2f749e6e | Stavros Sachtouris | # Success can either be an in or a collection
|
130 | 2f749e6e | Stavros Sachtouris | success = (success,) if isinstance(success, int) else success |
131 | 2f749e6e | Stavros Sachtouris | if r.status_code not in success: |
132 | a34888b4 | Stavros Sachtouris | r.release() |
133 | 6ad245d5 | Stavros Sachtouris | self._raise_for_status(r)
|
134 | 58821ef5 | Stavros Sachtouris | except Exception as err: |
135 | 2f749e6e | Stavros Sachtouris | self.http_client.reset_headers()
|
136 | 2f749e6e | Stavros Sachtouris | self.http_client.reset_params()
|
137 | b9d07587 | Stavros Sachtouris | errmsg = getattr(err, 'message', unicode(err)) |
138 | b9d07587 | Stavros Sachtouris | errdetails = getattr(err, 'details', '')+' (%s)'%type(err) |
139 | b9d07587 | Stavros Sachtouris | errstatus = getattr(err, 'status', 0) |
140 | b9d07587 | Stavros Sachtouris | raise ClientError(message=errmsg,status=errstatus,details=errdetails)
|
141 | a52d2256 | Stavros Sachtouris | |
142 | a52d2256 | Stavros Sachtouris | self.http_client.reset_headers()
|
143 | a52d2256 | Stavros Sachtouris | self.http_client.reset_params()
|
144 | d726b3d0 | Stavros Sachtouris | return r
|
145 | a1c50326 | Giorgos Verigakis | |
146 | 6a0b1658 | Giorgos Verigakis | def delete(self, path, **kwargs): |
147 | 6a0b1658 | Giorgos Verigakis | return self.request('delete', path, **kwargs) |
148 | 6a0b1658 | Giorgos Verigakis | |
149 | 6a0b1658 | Giorgos Verigakis | def get(self, path, **kwargs): |
150 | 6a0b1658 | Giorgos Verigakis | return self.request('get', path, **kwargs) |
151 | 6a0b1658 | Giorgos Verigakis | |
152 | 6a0b1658 | Giorgos Verigakis | def head(self, path, **kwargs): |
153 | 6a0b1658 | Giorgos Verigakis | return self.request('head', path, **kwargs) |
154 | 6a0b1658 | Giorgos Verigakis | |
155 | 6a0b1658 | Giorgos Verigakis | def post(self, path, **kwargs): |
156 | 6a0b1658 | Giorgos Verigakis | return self.request('post', path, **kwargs) |
157 | 6a0b1658 | Giorgos Verigakis | |
158 | 6a0b1658 | Giorgos Verigakis | def put(self, path, **kwargs): |
159 | 6a0b1658 | Giorgos Verigakis | return self.request('put', path, **kwargs) |
160 | 6a0b1658 | Giorgos Verigakis | |
161 | 4adfa919 | Stavros Sachtouris | def copy(self, path, **kwargs): |
162 | 4adfa919 | Stavros Sachtouris | return self.request('copy', path, **kwargs) |
163 | 4adfa919 | Stavros Sachtouris | |
164 | 4adfa919 | Stavros Sachtouris | def move(self, path, **kwargs): |
165 | 4adfa919 | Stavros Sachtouris | return self.request('move', path, **kwargs) |