Commit fcc0cf60 authored by Ilias Tsitsimpis's avatar Ilias Tsitsimpis Committed by Christos Stavrakakis
Browse files

Create AstakosClient Class

parent 9b81d874
......@@ -39,68 +39,121 @@ import simplejson
import objpool.http
logger = logging.getLogger(__name__)
# --------------------------------------------------------------------
# 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)
# --------------------------------------------------------------------
# Astakos client API
# Astakos Client Class
class AstakosClient():
"""AstakosClient Class Implementation"""
# A simple retry decorator
def retry(howmany):
def decorator(func):
def f(*args, **kwargs):
attemps = 0
while True:
# ----------------------------------
def __init__(self, token, astakos_url, use_pool=False, logger=None):
"""Intialize AstakosClient Class
Keyword arguments:
token -- user's token (string)
astakos_url -- i.e https://accounts.example.com (string)
use_pool -- use objpool for http requests (boolean)
logger -- pass a different logger
"""
if logger is None:
logger = logging.getLogger("astakosclient")
logger.debug("Intialize AstakosClient: astakos_url = %s"
"use_pool = %s" % (astakos_url, use_pool))
# Check Input
if not token:
m = "Token not given"
logger.error(m)
raise ValueError(m)
if not astakos_url:
m = "Astakos url not given"
logger.error(m)
raise ValueError(m)
# Check for supported scheme
p = urlparse.urlparse(astakos_url)
conn = _scheme_to_class(p.scheme, use_pool)
if conn is None:
m = "Unsupported scheme: %s" % p.scheme
logger.error(m)
raise ValueError(m)
# Save token and url
self.logger = logger
self.token = token
self.netloc = p.netloc
self.scheme = p.scheme
self.conn = conn
# ----------------------------------
def _callAstakos(self, request_path, headers={}, body={}, method="GET"):
"""Make the actual call to Astakos Service"""
self.logger.debug(
"Make a %s request to %s with headers %s "
"and body %s" % (method, request_path, headers, body))
# Build request's header and body
kwargs = {}
kwargs['headers'] = headers
kwargs['headers']['X-Auth-Token'] = self.token
if body:
kwargs['body'] = body
kwargs['headers'].setdefault(
'content-type', 'application/octet-stream')
kwargs['headers'].setdefault('content-length',
len(body) if body else 0)
# Get the connection object
conn = self.conn(self.netloc)
# Send request
try:
return func(*args, **kwargs)
except Exception as e:
is_last_attempt = attemps == howmany - 1
if is_last_attempt:
raise e
if e.args:
status = e[0]
# In case of Unauthorized response
# or Not Found return immediately
if status == 401 or status == 404:
raise e
attemps += 1
return f
return decorator
(data, status) = _doRequest(conn, method, request_path, **kwargs)
except Exception as err:
self.logger.error("Failed to send request: %s" % err)
raise AstakosClientException(str(err))
finally:
conn.close()
# Return
self.logger.debug("Request returned with status %s" % status)
if status < 200 or status >= 300:
raise AstakosClientException(data, status)
return simplejson.loads(unicode(data))
# ----------------------------
# Authenticate
@retry(3)
def authenticate(token, astakos_url, usage=False, use_pool=False):
"""Check if user is an authenticated Astakos user
# ------------------------
def authenticate(self, usage=False):
"""Check if user is authenticated Astakos user
Keyword arguments:
token -- user's token (string)
astakos_url -- i.e https://accounts.example.com (string)
usage -- return usage information for user (boolean)
use_pool -- use objpool for http requests (boolean)
In case of success return user informations (json parsed format).
Otherwise raise an Exception.
In case of success return user information (json parsed format).
Otherwise raise an AstakosClientException.
"""
authentication_url = urlparse.urljoin(astakos_url, "/im/authenticate")
auth_path = "/im/authenticate"
if usage:
authentication_url += "?usage=1,"
return _callAstakos(token, authentication_url, use_pool=use_pool)
auth_path += "?usage=1"
return self._callAstakos(auth_path)
# ----------------------------
# Display Names
@retry(3)
def getDisplayNames(token, uuids, astakos_url, use_pool=False):
# ----------------------------------
def getDisplayNames(self, uuids):
"""Return a uuid_catalog dictionary for the given uuids
Keyword arguments:
token -- user's token (string)
uuids -- list of user ids (list of strings)
astakos_url -- i.e https://accounts.example.com (string)
use_pool -- use objpool for http requests (boolean)
The returned uuid_catalog is a dictionary with uuids as
keys and the corresponding user names as values
......@@ -108,35 +161,74 @@ def getDisplayNames(token, uuids, astakos_url, use_pool=False):
"""
req_headers = {'content-type': 'application/json'}
req_body = simplejson.dumps({'uuids': uuids})
req_url = urlparse.urljoin(astakos_url, "/user_catalogs")
req_path = "/user_catalogs"
data = _callAstakos(token, req_url, headers=req_headers,
body=req_body, method="POST", use_pool=use_pool)
data = self._callAstakos(req_path, req_headers, req_body, "POST")
# XXX: check if exists
return data.get("uuid_catalog")
def getDisplayName(token, uuid, astakos_url, use_pool=False):
"""Return the displayname of a uuid (see getDisplayNames)"""
def getDisplayName(self, uuid):
"""Return the displayName of a uuid (see getDisplayNames)"""
if not uuid:
m = "No uuid was given"
logger.error(m)
self.logger.error(m)
raise ValueError(m)
uuid_dict = getDisplayNames(token, [uuid], astakos_url, use_pool)
uuid_dict = self.getDisplayNames([uuid])
# XXX: check if exists
return uuid_dict.get(uuid)
# A simple retry decorator
def retry(howmany):
def decorator(func):
def f(*args, **kwargs):
attemps = 0
while True:
try:
return func(*args, **kwargs)
except Exception as e:
is_last_attempt = attemps == howmany - 1
if is_last_attempt:
raise e
if e.args:
status = e[0]
# In case of Unauthorized response
# or Not Found return immediately
if status == 401 or status == 404:
raise e
attemps += 1
return f
return decorator
# --------------------------------------------------------------------
# Private functions
def _scheme_to_class(scheme):
"""Return the appropriate httplib class for given scheme"""
def _scheme_to_class(scheme, use_pool):
"""Return the appropriate conn class for given scheme"""
if scheme == "http":
if use_pool:
return _objpoolHttpScheme
else:
return httplib.HTTPConnection
elif scheme == "https":
if use_pool:
return _objpoolHttpsScheme
else:
return httplib.HTTPSConnection
else:
return None
def _objpoolHttpScheme(netloc):
"""Intialize the appropriate objpool.http class"""
return objpool.http.get_http_connection(netloc, "http")
def _objpoolHttpsScheme(netloc):
"""Intialize the appropriate objpool.http class"""
return objpool.http.get_http_connection(netloc, "https")
def _doRequest(conn, method, url, **kwargs):
"""The actual request. This function can easily be mocked"""
conn.request(method, url, **kwargs)
......@@ -144,63 +236,4 @@ def _doRequest(conn, method, url, **kwargs):
length = response.getheader('content-length', None)
data = response.read(length)
status = int(response.status)
return (status, data)
def _callAstakos(token, url, headers={}, body=None,
method='GET', use_pool=False):
"""Make the actual call to astakos service"""
logger.debug("Make a %s request to %s with headers %s "
"and body %s, %s using the pool" %
(method, url, headers, body,
"without" if not use_pool else ""))
# Check input
if not token:
m = "Token not given"
logger.error(m)
raise ValueError(m)
if not url:
m = "Url not given"
logger.error(m)
raise ValueError(m)
# Build request's header and body
kwargs = {}
kwargs['headers'] = headers
kwargs['headers']['X-Auth-Token'] = token
if body:
kwargs['body'] = body
kwargs['headers'].setdefault(
'content-type', 'application/octet-stream')
kwargs['headers'].setdefault('content-length', len(body) if body else 0)
# Check for supported scheme
p = urlparse.urlparse(url)
connection_class = _scheme_to_class(p.scheme)
if connection_class is None:
m = "Unsupported scheme: %s" % p.scheme
logger.error(m)
raise ValueError(m)
# Get connection object
if use_pool:
conn = objpool.http.get_http_connection(p.netloc, p.scheme)
else:
conn = connection_class(p.netloc)
# Send request
try:
request_url = p.path + '?' + p.query
(status, data) = _doRequest(conn, method, request_url, **kwargs)
except httplib.HTTPException as err:
logger.error("Failed to send request: %s" % err)
raise
finally:
conn.close()
# Return
logger.debug("Request returned with status %s" % status)
if status < 200 or status >= 300:
raise Exception(status, data)
return simplejson.loads(unicode(data))
return (data, status)
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