Revision f93cc364

b/snf-astakos-client/astakosclient/__init__.py
33 33

  
34 34
import logging
35 35
import urlparse
36
import httplib
37 36
import urllib
38 37
import hashlib
39 38
from copy import copy
40 39

  
41 40
import simplejson
42
import objpool.http
43

  
44

  
45
# --------------------------------------------------------------------
46
# Astakos Client Exception
47
class AstakosClientException(Exception):
48
    def __init__(self, message, status=0):
49
        self.message = message
50
        self.status = status
51

  
52
    def __str__(self):
53
        return repr(self.message)
54

  
55

  
56
class AstakosClientEInvalid(AstakosClientException):
57
    def __init__(self, message):
58
        """Invalid X-Auth-Token"""
59
        super(AstakosClientEInvalid, self).__init__(message, 401)
60

  
61

  
62
class AstakosClientEMethod(AstakosClientException):
63
    def __init__(self, message):
64
        """Method not allowed"""
65
        super(AstakosClientEMethod, self).__init__(message, 400)
66

  
67

  
68
class AstakosClientENotFound(AstakosClientException):
69
    def __init__(self, message):
70
        """404 Not Found"""
71
        super(AstakosClientENotFound, self).__init__(message, 404)
41
from astakosclient.utils import retry, scheme_to_class
42
from astakosclient.errors import \
43
    AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden
72 44

  
73 45

  
74 46
# --------------------------------------------------------------------
......
120 92

  
121 93
        # Check for supported scheme
122 94
        p = urlparse.urlparse(astakos_url)
123
        conn_class = _scheme_to_class(p.scheme, use_pool, pool_size)
95
        conn_class = scheme_to_class(p.scheme, use_pool, pool_size)
124 96
        if conn_class is None:
125 97
            m = "Unsupported scheme: %s" % p.scheme
126 98
            logger.error(m)
......
134 106
        self.conn_class = conn_class
135 107

  
136 108
    # ----------------------------------
137
    def retry(func):
138
        def decorator(self, *args, **kwargs):
139
            attemps = 0
140
            while True:
141
                try:
142
                    return func(self, *args, **kwargs)
143
                except AstakosClientException as err:
144
                    is_last_attempt = attemps == self.retry
145
                    if is_last_attempt:
146
                        raise err
147
                    if err.status == 401 or err.status == 404:
148
                        # In case of Unauthorized response
149
                        # or Not Found return immediately
150
                        raise err
151
                    self.logger.info("AstakosClient request failed..retrying")
152
                    attemps += 1
153
        return decorator
154

  
155
    # ----------------------------------
156 109
    @retry
157 110
    def _callAstakos(self, token, request_path,
158 111
                     headers=None, body=None, method="GET"):
......
202 155
        # Return
203 156
        self.logger.debug("Request returned with status %s" % status)
204 157
        if status == 400:
205
            raise AstakosClientEMethod(data)
158
            raise BadRequest(data)
206 159
        if status == 401:
207
            raise AstakosClientEInvalid(data)
160
            raise Unauthorized(data)
161
        if status == 403:
162
            raise Forbidden(data)
208 163
        if status == 404:
209
            raise AstakosClientENotFound(data)
164
            raise NotFound(data)
210 165
        if status < 200 or status >= 300:
211 166
            raise AstakosClientException(data, status)
212 167
        return simplejson.loads(unicode(data))
......
333 288

  
334 289
# --------------------------------------------------------------------
335 290
# Private functions
336
def _scheme_to_class(scheme, use_pool, pool_size):
337
    """Return the appropriate conn class for given scheme"""
338
    def _objpool(netloc):
339
        return objpool.http.get_http_connection(
340
            netloc=netloc, scheme=scheme, pool_size=pool_size)
341

  
342
    if scheme == "http":
343
        if use_pool:
344
            return _objpool
345
        else:
346
            return httplib.HTTPConnection
347
    elif scheme == "https":
348
        if use_pool:
349
            return _objpool
350
        else:
351
            return httplib.HTTPSConnection
352
    else:
353
        return None
354

  
355

  
291
# We want _doRequest to be a distinct function
292
# so that we can replace it during unit tests.
356 293
def _doRequest(conn, method, url, **kwargs):
357 294
    """The actual request. This function can easily be mocked"""
358 295
    conn.request(method, url, **kwargs)
b/snf-astakos-client/astakosclient/errors.py
1
# Copyright (C) 2012, 2013 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, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this 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

  
35
class AstakosClientException(Exception):
36
    def __init__(self, message, status=0):
37
        self.message = message
38
        self.status = status
39

  
40
    def __str__(self):
41
        return repr(self.message)
42

  
43

  
44
class BadRequest(AstakosClientException):
45
    def __init__(self, message):
46
        """400 Bad Request"""
47
        super(BadRequest, self).__init__(message, 400)
48

  
49

  
50
class Unauthorized(AstakosClientException):
51
    def __init__(self, message):
52
        """401 Invalid X-Auth-Token"""
53
        super(Unauthorized, self).__init__(message, 401)
54

  
55

  
56
class Forbidden(AstakosClientException):
57
    def __init__(self, message):
58
        """403 Forbidden"""
59
        super(Forbidden, self).__init__(message, 403)
60

  
61

  
62
class NotFound(AstakosClientException):
63
    def __init__(self, message):
64
        """404 Not Found"""
65
        super(NotFound, self).__init__(message, 404)
b/snf-astakos-client/astakosclient/tests.py
45 45
import simplejson
46 46

  
47 47
import astakosclient
48
from astakosclient import AstakosClient, AstakosClientException, \
49
    AstakosClientEInvalid, AstakosClientEMethod, AstakosClientENotFound
48
from astakosclient import AstakosClient
49
from astakosclient.errors import \
50
    AstakosClientException, Unauthorized, BadRequest, NotFound
50 51

  
51 52
# Use backported unittest functionality if Python < 2.7
52 53
try:
......
296 297
        try:
297 298
            client = AstakosClient("https://example.com", use_pool=pool)
298 299
            client._callAstakos(token, "/im/authenticate")
299
        except AstakosClientEInvalid:
300
        except Unauthorized:
300 301
            pass
301 302
        except Exception:
302 303
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
......
319 320
        try:
320 321
            client = AstakosClient("https://example.com", use_pool=pool)
321 322
            client._callAstakos(token_1, "/im/misspelled")
322
        except AstakosClientENotFound:
323
        except NotFound:
323 324
            pass
324 325
        except Exception:
325 326
            self.fail("Should have returned 404 (Not Found)")
......
387 388
        try:
388 389
            client = AstakosClient("https://example.com", use_pool=pool)
389 390
            client._callAstakos(token_1, "/im/authenticate", method="POST")
390
        except AstakosClientEMethod:
391
        except BadRequest:
391 392
            pass
392 393
        except Exception:
393 394
            self.fail("Should have returned 400 (Method not allowed)")
......
410 411
        try:
411 412
            client = AstakosClient("https://example.com", use_pool=pool)
412 413
            client._callAstakos(token_1, "/user_catalogs")
413
        except AstakosClientEMethod:
414
        except BadRequest:
414 415
            pass
415 416
        except Exception:
416 417
            self.fail("Should have returned 400 (Method not allowed)")
......
451 452
        try:
452 453
            client = AstakosClient("https://example.com", use_pool=pool)
453 454
            client.authenticate(token)
454
        except AstakosClientEInvalid:
455
        except Unauthorized:
455 456
            pass
456 457
        except Exception:
457 458
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
......
538 539
        try:
539 540
            client = AstakosClient("https://example.com")
540 541
            client.getDisplayNames(token, [user_1['uuid']])
541
        except AstakosClientEInvalid:
542
        except Unauthorized:
542 543
            pass
543 544
        except Exception:
544 545
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
......
588 589
        try:
589 590
            client = AstakosClient("https://example.com")
590 591
            client.getUUIDs(token, [user_1['username']])
591
        except AstakosClientEInvalid:
592
        except Unauthorized:
592 593
            pass
593 594
        except Exception:
594 595
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
b/snf-astakos-client/astakosclient/utils.py
1
# Copyright (C) 2012, 2013 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, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this 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 httplib
35

  
36
import objpool.http
37
from astakosclient.errors import AstakosClientException
38

  
39

  
40
def retry(func):
41
    def decorator(self, *args, **kwargs):
42
        attemps = 0
43
        while True:
44
            try:
45
                return func(self, *args, **kwargs)
46
            except AstakosClientException as err:
47
                is_last_attempt = attemps == self.retry
48
                if is_last_attempt:
49
                    raise err
50
                if err.status == 401 or err.status == 404:
51
                    # In case of Unauthorized response
52
                    # or Not Found return immediately
53
                    raise err
54
                self.logger.info("AstakosClient request failed..retrying")
55
                attemps += 1
56
    return decorator
57

  
58

  
59
def scheme_to_class(scheme, use_pool, pool_size):
60
    """Return the appropriate conn class for given scheme"""
61
    def _objpool(netloc):
62
        return objpool.http.get_http_connection(
63
            netloc=netloc, scheme=scheme, pool_size=pool_size)
64

  
65
    if scheme == "http":
66
        if use_pool:
67
            return _objpool
68
        else:
69
            return httplib.HTTPConnection
70
    elif scheme == "https":
71
        if use_pool:
72
            return _objpool
73
        else:
74
            return httplib.HTTPSConnection
75
    else:
76
        return None

Also available in: Unified diff