Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / __init__.py @ b9d07587

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 import HTTPConnectionError
37
from kamaki.clients.connection.kamakicon import KamakiHTTPConnection
38

    
39
sendlog = logging.getLogger('clients.send')
40
recvlog = logging.getLogger('clients.recv')
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
class Client(object):
50

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

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

    
68
    def set_header(self, name, value, iff=True):
69
        """Set a header 'name':'value' provided value is not None and iff is True"""
70
        if value is not None and iff:
71
            self.http_client.set_header(name, value)
72

    
73
    def set_param(self, name, value=None, iff=True):
74
        if iff:
75
            self.http_client.set_param(name, value)
76

    
77
    def set_default_header(self, name, value):
78
        self.http_client.headers.setdefault(name, value)
79

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

    
96
        try:
97
            success = kwargs.pop('success', 200)
98

    
99
            data = kwargs.pop('data', None)
100
            self.set_default_header('X-Auth-Token', self.token)
101

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

    
108
            self.http_client.url = self.base_url + path
109
            r = self.http_client.perform_request(method, data, async_headers, async_params)
110

    
111
            req = self.http_client
112
            sendlog.info('%s %s', method, req.url)
113
            headers = dict(req.headers)
114
            headers.update(async_headers)
115

    
116
            for key, val in headers.items():
117
                sendlog.info('\t%s: %s', key, val)
118
            sendlog.info('')
119
            if data:
120
                sendlog.info('%s', data)
121

    
122
            recvlog.info('%d %s', r.status_code, r.status)
123
            for key, val in r.headers.items():
124
                recvlog.info('%s: %s', key, val)
125
            #if r.content:
126
            #    recvlog.debug(r.content)
127

    
128
            if success is not None:
129
                # Success can either be an in or a collection
130
                success = (success,) if isinstance(success, int) else success
131
                if r.status_code not in success:
132
                    r.release()
133
                    self._raise_for_status(r)
134
        except Exception as err:
135
            self.http_client.reset_headers()
136
            self.http_client.reset_params()
137
            errmsg = getattr(err, 'message', unicode(err))
138
            errdetails = getattr(err, 'details', '')+' (%s)'%type(err)
139
            errstatus = getattr(err, 'status', 0)
140
            raise ClientError(message=errmsg,status=errstatus,details=errdetails)
141

    
142
        self.http_client.reset_headers()
143
        self.http_client.reset_params()
144
        return r
145

    
146
    def delete(self, path, **kwargs):
147
        return self.request('delete', path, **kwargs)
148

    
149
    def get(self, path, **kwargs):
150
        return self.request('get', path, **kwargs)
151

    
152
    def head(self, path, **kwargs):
153
        return self.request('head', path, **kwargs)
154

    
155
    def post(self, path, **kwargs):
156
        return self.request('post', path, **kwargs)
157

    
158
    def put(self, path, **kwargs):
159
        return self.request('put', path, **kwargs)
160

    
161
    def copy(self, path, **kwargs):
162
        return self.request('copy', path, **kwargs)
163

    
164
    def move(self, path, **kwargs):
165
        return self.request('move', path, **kwargs)
166