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

Create AstakosClient Class

parent 9b81d874
......@@ -39,11 +39,144 @@ 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"""
# ----------------------------------
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:
(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))
# ------------------------
def authenticate(self, usage=False):
"""Check if user is authenticated Astakos user
Keyword arguments:
usage -- return usage information for user (boolean)
In case of success return user information (json parsed format).
Otherwise raise an AstakosClientException.
"""
auth_path = "/im/authenticate"
if usage:
auth_path += "?usage=1"
return self._callAstakos(auth_path)
# ----------------------------------
def getDisplayNames(self, uuids):
"""Return a uuid_catalog dictionary for the given uuids
Keyword arguments:
uuids -- list of user ids (list of strings)
The returned uuid_catalog is a dictionary with uuids as
keys and the corresponding user names as values
"""
req_headers = {'content-type': 'application/json'}
req_body = simplejson.dumps({'uuids': uuids})
req_path = "/user_catalogs"
data = self._callAstakos(req_path, req_headers, req_body, "POST")
# XXX: check if exists
return data.get("uuid_catalog")
def getDisplayName(self, uuid):
"""Return the displayName of a uuid (see getDisplayNames)"""
if not uuid:
m = "No uuid was given"
self.logger.error(m)
raise ValueError(m)
uuid_dict = self.getDisplayNames([uuid])
# XXX: check if exists
return uuid_dict.get(uuid)
# A simple retry decorator
def retry(howmany):
......@@ -68,75 +201,34 @@ def retry(howmany):
return decorator
# ----------------------------
# Authenticate
@retry(3)
def authenticate(token, astakos_url, usage=False, use_pool=False):
"""Check if user is an 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.
"""
authentication_url = urlparse.urljoin(astakos_url, "/im/authenticate")
if usage:
authentication_url += "?usage=1,"
return _callAstakos(token, authentication_url, use_pool=use_pool)
# ----------------------------
# Display Names
@retry(3)
def getDisplayNames(token, uuids, astakos_url, use_pool=False):
"""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
"""
req_headers = {'content-type': 'application/json'}
req_body = simplejson.dumps({'uuids': uuids})
req_url = urlparse.urljoin(astakos_url, "/user_catalogs")
data = _callAstakos(token, req_url, headers=req_headers,
body=req_body, method="POST", use_pool=use_pool)
return data.get("uuid_catalog")
def getDisplayName(token, uuid, astakos_url, use_pool=False):
"""Return the displayname of a uuid (see getDisplayNames)"""
if not uuid:
m = "No uuid was given"
logger.error(m)
raise ValueError(m)
uuid_dict = getDisplayNames(token, [uuid], astakos_url, use_pool)
return uuid_dict.get(uuid)
# --------------------------------------------------------------------
# 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":
return httplib.HTTPConnection
if use_pool:
return _objpoolHttpScheme
else:
return httplib.HTTPConnection
elif scheme == "https":
return httplib.HTTPSConnection
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