Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / __init__.py @ 3dabe5d2

History | View | Annotate | Download (6.1 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, self.list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, self.list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import json
35
import logging
36
from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
37

    
38
sendlog = logging.getLogger('clients.send')
39
recvlog = logging.getLogger('clients.recv')
40

    
41

    
42
class ClientError(Exception):
43
    def __init__(self, message, status=0, details=''):
44
        super(ClientError, self).__init__(message, status, details)
45
        self.message = message
46
        self.status = status
47
        self.details = details
48

    
49

    
50
class Client(object):
51

    
52
    def __init__(self, base_url, token, http_client=KamakiHTTPConnection()):
53
        self.base_url = base_url
54
        self.token = token
55
        self.headers = {}
56
        self.DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
57
            "%A, %d-%b-%y %H:%M:%S GMT",
58
            "%a, %d %b %Y %H:%M:%S GMT"]
59
        self.http_client = http_client
60

    
61
    def _raise_for_status(self, r):
62
        message = "%s" % r.status
63
        try:
64
            details = r.text
65
        except:
66
            details = ''
67
        raise ClientError(message=message,
68
            status=r.status_code,
69
            details=details)
70

    
71
    def set_header(self, name, value, iff=True):
72
        """Set a header 'name':'value'"""
73
        if value is not None and iff:
74
            self.http_client.set_header(name, value)
75

    
76
    def set_param(self, name, value=None, iff=True):
77
        if iff:
78
            self.http_client.set_param(name, value)
79

    
80
    def set_default_header(self, name, value):
81
        self.http_client.headers.setdefault(name, value)
82

    
83
    def request(self,
84
        method,
85
        path,
86
        async_headers={},
87
        async_params={},
88
        **kwargs):
89
        """In threaded/asynchronous requests, headers and params are not safe
90
        Therefore, the standard self.set_header/param system can be used only
91
        for headers and params that are common for all requests. All other
92
        params and headers should passes as
93
        @param async_headers
94
        @async_params
95
        E.g. in most queries the 'X-Auth-Token' header might be the same for
96
        all, but the 'Range' header might be different from request to request.
97
        """
98

    
99
        try:
100
            success = kwargs.pop('success', 200)
101

    
102
            data = kwargs.pop('data', None)
103
            self.set_default_header('X-Auth-Token', self.token)
104

    
105
            if 'json' in kwargs:
106
                data = json.dumps(kwargs.pop('json'))
107
                self.set_default_header('Content-Type', 'application/json')
108
            if data:
109
                self.set_default_header('Content-Length', unicode(len(data)))
110

    
111
            self.http_client.url = self.base_url + path
112
            r = self.http_client.perform_request(method,
113
                data,
114
                async_headers,
115
                async_params)
116

    
117
            req = self.http_client
118
            sendlog.info('%s %s', method, req.url)
119
            headers = dict(req.headers)
120
            headers.update(async_headers)
121

    
122
            for key, val in headers.items():
123
                sendlog.info('\t%s: %s', key, val)
124
            sendlog.info('')
125
            if data:
126
                sendlog.info('%s', data)
127

    
128
            recvlog.info('%d %s', r.status_code, r.status)
129
            for key, val in r.headers.items():
130
                recvlog.info('%s: %s', key, val)
131
            #if r.content:
132
            #    recvlog.debug(r.content)
133

    
134
            if success is not None:
135
                # Success can either be an in or a collection
136
                success = (success,) if isinstance(success, int) else success
137
                if r.status_code not in success:
138
                    r.release()
139
                    self._raise_for_status(r)
140
        except Exception as err:
141
            self.http_client.reset_headers()
142
            self.http_client.reset_params()
143
            errmsg = getattr(err, 'message', unicode(err))
144
            errdetails = '%s %s' % (type(err), getattr(err, 'details', ''))
145
            errstatus = getattr(err, 'status', 0)
146
            raise ClientError(message=errmsg,
147
                status=errstatus,
148
                details=errdetails)
149

    
150
        self.http_client.reset_headers()
151
        self.http_client.reset_params()
152
        return r
153

    
154
    def delete(self, path, **kwargs):
155
        return self.request('delete', path, **kwargs)
156

    
157
    def get(self, path, **kwargs):
158
        return self.request('get', path, **kwargs)
159

    
160
    def head(self, path, **kwargs):
161
        return self.request('head', path, **kwargs)
162

    
163
    def post(self, path, **kwargs):
164
        return self.request('post', path, **kwargs)
165

    
166
    def put(self, path, **kwargs):
167
        return self.request('put', path, **kwargs)
168

    
169
    def copy(self, path, **kwargs):
170
        return self.request('copy', path, **kwargs)
171

    
172
    def move(self, path, **kwargs):
173
        return self.request('move', path, **kwargs)