Commit 2f17403c authored by Ilias Tsitsimpis's avatar Ilias Tsitsimpis Committed by Christos Stavrakakis

Create utils and errors modules for astakosclient

* Move retry decorator to utils module.
* Rename exceptions to be more intuitive.
* Add 403 Forbidden exception.
* Move exceptions to errors class
parent 1c084e30
......@@ -33,42 +33,14 @@
import logging
import urlparse
import httplib
import urllib
import hashlib
from copy import copy
import simplejson
import objpool.http
# --------------------------------------------------------------------
# Astakos Client Exception
class AstakosClientException(Exception):
def __init__(self, message, status=0):
self.message = message
self.status = status
def __str__(self):
return repr(self.message)
class AstakosClientEInvalid(AstakosClientException):
def __init__(self, message):
"""Invalid X-Auth-Token"""
super(AstakosClientEInvalid, self).__init__(message, 401)
class AstakosClientEMethod(AstakosClientException):
def __init__(self, message):
"""Method not allowed"""
super(AstakosClientEMethod, self).__init__(message, 400)
class AstakosClientENotFound(AstakosClientException):
def __init__(self, message):
"""404 Not Found"""
super(AstakosClientENotFound, self).__init__(message, 404)
from astakosclient.utils import retry, scheme_to_class
from astakosclient.errors import \
AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden
# --------------------------------------------------------------------
......@@ -120,7 +92,7 @@ class AstakosClient():
# Check for supported scheme
p = urlparse.urlparse(astakos_url)
conn_class = _scheme_to_class(p.scheme, use_pool, pool_size)
conn_class = scheme_to_class(p.scheme, use_pool, pool_size)
if conn_class is None:
m = "Unsupported scheme: %s" % p.scheme
logger.error(m)
......@@ -133,25 +105,6 @@ class AstakosClient():
self.scheme = p.scheme
self.conn_class = conn_class
# ----------------------------------
def retry(func):
def decorator(self, *args, **kwargs):
attemps = 0
while True:
try:
return func(self, *args, **kwargs)
except AstakosClientException as err:
is_last_attempt = attemps == self.retry
if is_last_attempt:
raise err
if err.status == 401 or err.status == 404:
# In case of Unauthorized response
# or Not Found return immediately
raise err
self.logger.info("AstakosClient request failed..retrying")
attemps += 1
return decorator
# ----------------------------------
@retry
def _callAstakos(self, token, request_path,
......@@ -202,11 +155,13 @@ class AstakosClient():
# Return
self.logger.debug("Request returned with status %s" % status)
if status == 400:
raise AstakosClientEMethod(data)
raise BadRequest(data)
if status == 401:
raise AstakosClientEInvalid(data)
raise Unauthorized(data)
if status == 403:
raise Forbidden(data)
if status == 404:
raise AstakosClientENotFound(data)
raise NotFound(data)
if status < 200 or status >= 300:
raise AstakosClientException(data, status)
return simplejson.loads(unicode(data))
......@@ -333,26 +288,8 @@ class AstakosClient():
# --------------------------------------------------------------------
# Private functions
def _scheme_to_class(scheme, use_pool, pool_size):
"""Return the appropriate conn class for given scheme"""
def _objpool(netloc):
return objpool.http.get_http_connection(
netloc=netloc, scheme=scheme, pool_size=pool_size)
if scheme == "http":
if use_pool:
return _objpool
else:
return httplib.HTTPConnection
elif scheme == "https":
if use_pool:
return _objpool
else:
return httplib.HTTPSConnection
else:
return None
# We want _doRequest to be a distinct function
# so that we can replace it during unit tests.
def _doRequest(conn, method, url, **kwargs):
"""The actual request. This function can easily be mocked"""
conn.request(method, url, **kwargs)
......
# Copyright (C) 2012, 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
class AstakosClientException(Exception):
def __init__(self, message, status=0):
self.message = message
self.status = status
def __str__(self):
return repr(self.message)
class BadRequest(AstakosClientException):
def __init__(self, message):
"""400 Bad Request"""
super(BadRequest, self).__init__(message, 400)
class Unauthorized(AstakosClientException):
def __init__(self, message):
"""401 Invalid X-Auth-Token"""
super(Unauthorized, self).__init__(message, 401)
class Forbidden(AstakosClientException):
def __init__(self, message):
"""403 Forbidden"""
super(Forbidden, self).__init__(message, 403)
class NotFound(AstakosClientException):
def __init__(self, message):
"""404 Not Found"""
super(NotFound, self).__init__(message, 404)
......@@ -45,8 +45,9 @@ import socket
import simplejson
import astakosclient
from astakosclient import AstakosClient, AstakosClientException, \
AstakosClientEInvalid, AstakosClientEMethod, AstakosClientENotFound
from astakosclient import AstakosClient
from astakosclient.errors import \
AstakosClientException, Unauthorized, BadRequest, NotFound
# Use backported unittest functionality if Python < 2.7
try:
......@@ -296,7 +297,7 @@ class TestCallAstakos(unittest.TestCase):
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._callAstakos(token, "/im/authenticate")
except AstakosClientEInvalid:
except Unauthorized:
pass
except Exception:
self.fail("Should have returned 401 (Invalid X-Auth-Token)")
......@@ -319,7 +320,7 @@ class TestCallAstakos(unittest.TestCase):
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._callAstakos(token_1, "/im/misspelled")
except AstakosClientENotFound:
except NotFound:
pass
except Exception:
self.fail("Should have returned 404 (Not Found)")
......@@ -387,7 +388,7 @@ class TestCallAstakos(unittest.TestCase):
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._callAstakos(token_1, "/im/authenticate", method="POST")
except AstakosClientEMethod:
except BadRequest:
pass
except Exception:
self.fail("Should have returned 400 (Method not allowed)")
......@@ -410,7 +411,7 @@ class TestCallAstakos(unittest.TestCase):
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._callAstakos(token_1, "/user_catalogs")
except AstakosClientEMethod:
except BadRequest:
pass
except Exception:
self.fail("Should have returned 400 (Method not allowed)")
......@@ -451,7 +452,7 @@ class TestAuthenticate(unittest.TestCase):
try:
client = AstakosClient("https://example.com", use_pool=pool)
client.authenticate(token)
except AstakosClientEInvalid:
except Unauthorized:
pass
except Exception:
self.fail("Should have returned 401 (Invalid X-Auth-Token)")
......@@ -538,7 +539,7 @@ class TestDisplayNames(unittest.TestCase):
try:
client = AstakosClient("https://example.com")
client.getDisplayNames(token, [user_1['uuid']])
except AstakosClientEInvalid:
except Unauthorized:
pass
except Exception:
self.fail("Should have returned 401 (Invalid X-Auth-Token)")
......@@ -588,7 +589,7 @@ class TestGetUUIDs(unittest.TestCase):
try:
client = AstakosClient("https://example.com")
client.getUUIDs(token, [user_1['username']])
except AstakosClientEInvalid:
except Unauthorized:
pass
except Exception:
self.fail("Should have returned 401 (Invalid X-Auth-Token)")
......
# Copyright (C) 2012, 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import httplib
import objpool.http
from astakosclient.errors import AstakosClientException
def retry(func):
def decorator(self, *args, **kwargs):
attemps = 0
while True:
try:
return func(self, *args, **kwargs)
except AstakosClientException as err:
is_last_attempt = attemps == self.retry
if is_last_attempt:
raise err
if err.status == 401 or err.status == 404:
# In case of Unauthorized response
# or Not Found return immediately
raise err
self.logger.info("AstakosClient request failed..retrying")
attemps += 1
return decorator
def scheme_to_class(scheme, use_pool, pool_size):
"""Return the appropriate conn class for given scheme"""
def _objpool(netloc):
return objpool.http.get_http_connection(
netloc=netloc, scheme=scheme, pool_size=pool_size)
if scheme == "http":
if use_pool:
return _objpool
else:
return httplib.HTTPConnection
elif scheme == "https":
if use_pool:
return _objpool
else:
return httplib.HTTPSConnection
else:
return None
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment