Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / __init__.py @ 27a54f35

History | View | Annotate | Download (22 kB)

1 99165736 Christos Stavrakakis
# Copyright (C) 2012, 2013 GRNET S.A. All rights reserved.
2 99165736 Christos Stavrakakis
#
3 99165736 Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 99165736 Christos Stavrakakis
# without modification, are permitted provided that the following
5 99165736 Christos Stavrakakis
# conditions are met:
6 99165736 Christos Stavrakakis
#
7 99165736 Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 99165736 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 99165736 Christos Stavrakakis
#      disclaimer.
10 99165736 Christos Stavrakakis
#
11 99165736 Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 99165736 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 99165736 Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 99165736 Christos Stavrakakis
#      provided with the distribution.
15 99165736 Christos Stavrakakis
#
16 99165736 Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 99165736 Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 99165736 Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 99165736 Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 99165736 Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 99165736 Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 99165736 Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 99165736 Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 99165736 Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 99165736 Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 99165736 Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 99165736 Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 99165736 Christos Stavrakakis
#
29 99165736 Christos Stavrakakis
# The views and conclusions contained in the software and
30 99165736 Christos Stavrakakis
# documentation are those of the authors and should not be
31 99165736 Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 99165736 Christos Stavrakakis
# or implied, of GRNET S.A.
33 cbc0b438 Ilias Tsitsimpis
34 cbc0b438 Ilias Tsitsimpis
import logging
35 cbc0b438 Ilias Tsitsimpis
import urlparse
36 bc5032a4 Ilias Tsitsimpis
import urllib
37 6f64b6d0 Ilias Tsitsimpis
import hashlib
38 8f2d7ede Ilias Tsitsimpis
from copy import copy
39 cbc0b438 Ilias Tsitsimpis
40 cbc0b438 Ilias Tsitsimpis
import simplejson
41 10797183 Ilias Tsitsimpis
from astakosclient.utils import \
42 10797183 Ilias Tsitsimpis
    retry, scheme_to_class, parse_request, check_input
43 f93cc364 Ilias Tsitsimpis
from astakosclient.errors import \
44 2377e7c2 Ilias Tsitsimpis
    AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden, \
45 0a2a342c Ilias Tsitsimpis
    NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse
46 00d2a0ee Georgios D. Tsoukalas
from .keypath import get_path
47 00d2a0ee Georgios D. Tsoukalas
from .services import astakos_services
48 00d2a0ee Georgios D. Tsoukalas
49 00d2a0ee Georgios D. Tsoukalas
50 00d2a0ee Georgios D. Tsoukalas
# Customize astakos_services here?
51 8fe6475a Ilias Tsitsimpis
52 8fe6475a Ilias Tsitsimpis
53 e3ff6830 Georgios D. Tsoukalas
def join_urls(a, b):
54 e3ff6830 Georgios D. Tsoukalas
    """join_urls from synnefo.lib"""
55 e3ff6830 Georgios D. Tsoukalas
    return a.rstrip("/") + "/" + b.lstrip("/")
56 e3ff6830 Georgios D. Tsoukalas
57 cbc0b438 Ilias Tsitsimpis
# --------------------------------------------------------------------
58 7b5a37fd Ilias Tsitsimpis
# Astakos API urls
59 b173906e Ilias Tsitsimpis
UI_PREFIX = get_path(astakos_services, 'astakos_ui.prefix')
60 00d2a0ee Georgios D. Tsoukalas
ACCOUNTS_PREFIX = get_path(astakos_services, 'astakos_account.prefix')
61 389642a0 Kostas Papadimitriou
ACCOUNTS_PREFIX = join_urls(ACCOUNTS_PREFIX, 'v1.0')
62 e3ff6830 Georgios D. Tsoukalas
API_AUTHENTICATE = join_urls(ACCOUNTS_PREFIX, "authenticate")
63 e3ff6830 Georgios D. Tsoukalas
API_USERCATALOGS = join_urls(ACCOUNTS_PREFIX, "user_catalogs")
64 e3ff6830 Georgios D. Tsoukalas
API_SERVICE_USERCATALOGS = join_urls(ACCOUNTS_PREFIX, "service/user_catalogs")
65 b173906e Ilias Tsitsimpis
API_GETSERVICES = join_urls(UI_PREFIX, "get_services")
66 e3ff6830 Georgios D. Tsoukalas
API_RESOURCES = join_urls(ACCOUNTS_PREFIX, "resources")
67 e3ff6830 Georgios D. Tsoukalas
API_QUOTAS = join_urls(ACCOUNTS_PREFIX, "quotas")
68 e3ff6830 Georgios D. Tsoukalas
API_SERVICE_QUOTAS = join_urls(ACCOUNTS_PREFIX, "service_quotas")
69 e3ff6830 Georgios D. Tsoukalas
API_COMMISSIONS = join_urls(ACCOUNTS_PREFIX, "commissions")
70 e3ff6830 Georgios D. Tsoukalas
API_COMMISSIONS_ACTION = join_urls(API_COMMISSIONS, "action")
71 e3ff6830 Georgios D. Tsoukalas
API_FEEDBACK = join_urls(ACCOUNTS_PREFIX, "feedback")
72 e3ff6830 Georgios D. Tsoukalas
73 e3ff6830 Georgios D. Tsoukalas
# --------------------------------------------------------------------
74 e3ff6830 Georgios D. Tsoukalas
# Astakos Keystone API urls
75 923e6582 Georgios D. Tsoukalas
IDENTITY_PREFIX = get_path(astakos_services, 'astakos_identity.prefix')
76 b173906e Ilias Tsitsimpis
IDENTITY_PREFIX = join_urls(IDENTITY_PREFIX, "v2.0")
77 923e6582 Georgios D. Tsoukalas
API_TOKENS = join_urls(IDENTITY_PREFIX, "tokens")
78 7b5a37fd Ilias Tsitsimpis
79 7b5a37fd Ilias Tsitsimpis
80 7b5a37fd Ilias Tsitsimpis
# --------------------------------------------------------------------
81 4490d7b5 Ilias Tsitsimpis
# Astakos Client Class
82 bc5032a4 Ilias Tsitsimpis
83 794c94e6 Ilias Tsitsimpis
def get_token_from_cookie(request, cookie_name):
84 bc5032a4 Ilias Tsitsimpis
    """Extract token from the cookie name provided
85 bc5032a4 Ilias Tsitsimpis

86 bc5032a4 Ilias Tsitsimpis
    Cookie should be in the same form as astakos
87 bc5032a4 Ilias Tsitsimpis
    service sets its cookie contents:
88 bc5032a4 Ilias Tsitsimpis
        <user_uniq>|<user_token>
89 bc5032a4 Ilias Tsitsimpis

90 bc5032a4 Ilias Tsitsimpis
    """
91 bc5032a4 Ilias Tsitsimpis
    try:
92 bc5032a4 Ilias Tsitsimpis
        cookie_content = urllib.unquote(request.COOKIE.get(cookie_name, None))
93 bc5032a4 Ilias Tsitsimpis
        return cookie_content.split("|")[1]
94 bc5032a4 Ilias Tsitsimpis
    except:
95 bc5032a4 Ilias Tsitsimpis
        return None
96 bc5032a4 Ilias Tsitsimpis
97 bc5032a4 Ilias Tsitsimpis
98 4490d7b5 Ilias Tsitsimpis
class AstakosClient():
99 4490d7b5 Ilias Tsitsimpis
    """AstakosClient Class Implementation"""
100 4490d7b5 Ilias Tsitsimpis
101 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
102 996061fa Ilias Tsitsimpis
    def __init__(self, astakos_url, retry=0,
103 996061fa Ilias Tsitsimpis
                 use_pool=False, pool_size=8, logger=None):
104 c4644612 Ilias Tsitsimpis
        """Initialize AstakosClient Class
105 4490d7b5 Ilias Tsitsimpis

106 4490d7b5 Ilias Tsitsimpis
        Keyword arguments:
107 4490d7b5 Ilias Tsitsimpis
        astakos_url -- i.e https://accounts.example.com (string)
108 4490d7b5 Ilias Tsitsimpis
        use_pool    -- use objpool for http requests (boolean)
109 949baf4d Ilias Tsitsimpis
        retry       -- how many time to retry (integer)
110 4490d7b5 Ilias Tsitsimpis
        logger      -- pass a different logger
111 4490d7b5 Ilias Tsitsimpis

112 4490d7b5 Ilias Tsitsimpis
        """
113 4490d7b5 Ilias Tsitsimpis
        if logger is None:
114 7eb32034 Ilias Tsitsimpis
            logging.basicConfig(
115 7eb32034 Ilias Tsitsimpis
                format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
116 7eb32034 Ilias Tsitsimpis
                datefmt='%Y-%m-%d %H:%M:%S',
117 7eb32034 Ilias Tsitsimpis
                level=logging.INFO)
118 4490d7b5 Ilias Tsitsimpis
            logger = logging.getLogger("astakosclient")
119 8f2d7ede Ilias Tsitsimpis
        logger.debug("Intialize AstakosClient: astakos_url = %s, "
120 4490d7b5 Ilias Tsitsimpis
                     "use_pool = %s" % (astakos_url, use_pool))
121 4490d7b5 Ilias Tsitsimpis
122 10797183 Ilias Tsitsimpis
        check_input("__init__", logger, astakos_url=astakos_url)
123 4490d7b5 Ilias Tsitsimpis
124 4490d7b5 Ilias Tsitsimpis
        # Check for supported scheme
125 4490d7b5 Ilias Tsitsimpis
        p = urlparse.urlparse(astakos_url)
126 f93cc364 Ilias Tsitsimpis
        conn_class = scheme_to_class(p.scheme, use_pool, pool_size)
127 e169a337 Ilias Tsitsimpis
        if conn_class is None:
128 4490d7b5 Ilias Tsitsimpis
            m = "Unsupported scheme: %s" % p.scheme
129 4490d7b5 Ilias Tsitsimpis
            logger.error(m)
130 83f9157b Ilias Tsitsimpis
            raise BadValue(m)
131 4490d7b5 Ilias Tsitsimpis
132 98752f06 Ilias Tsitsimpis
        # Save astakos_url etc. in our class
133 949baf4d Ilias Tsitsimpis
        self.retry = retry
134 4490d7b5 Ilias Tsitsimpis
        self.logger = logger
135 4490d7b5 Ilias Tsitsimpis
        self.netloc = p.netloc
136 4490d7b5 Ilias Tsitsimpis
        self.scheme = p.scheme
137 c7027e2e Kostas Papadimitriou
        self.path = p.path.rstrip('/')
138 e169a337 Ilias Tsitsimpis
        self.conn_class = conn_class
139 4490d7b5 Ilias Tsitsimpis
140 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
141 949baf4d Ilias Tsitsimpis
    @retry
142 25a04cdd Ilias Tsitsimpis
    def _call_astakos(self, token, request_path, headers=None,
143 25a04cdd Ilias Tsitsimpis
                      body=None, method="GET", log_body=True):
144 4490d7b5 Ilias Tsitsimpis
        """Make the actual call to Astakos Service"""
145 c4644612 Ilias Tsitsimpis
        if token is not None:
146 c4644612 Ilias Tsitsimpis
            hashed_token = hashlib.sha1()
147 c4644612 Ilias Tsitsimpis
            hashed_token.update(token)
148 c4644612 Ilias Tsitsimpis
            using_token = "using token %s" % (hashed_token.hexdigest())
149 c4644612 Ilias Tsitsimpis
        else:
150 c4644612 Ilias Tsitsimpis
            using_token = "without using token"
151 4490d7b5 Ilias Tsitsimpis
        self.logger.debug(
152 c4644612 Ilias Tsitsimpis
            "Make a %s request to %s %s with headers %s and body %s"
153 25a04cdd Ilias Tsitsimpis
            % (method, request_path, using_token, headers,
154 25a04cdd Ilias Tsitsimpis
               body if log_body else "(not logged)"))
155 4490d7b5 Ilias Tsitsimpis
156 98752f06 Ilias Tsitsimpis
        # Check Input
157 8f2d7ede Ilias Tsitsimpis
        if headers is None:
158 8f2d7ede Ilias Tsitsimpis
            headers = {}
159 8f2d7ede Ilias Tsitsimpis
        if body is None:
160 8f2d7ede Ilias Tsitsimpis
            body = {}
161 9848f747 Ilias Tsitsimpis
        path = self.path + "/" + request_path.strip('/')
162 98752f06 Ilias Tsitsimpis
163 4490d7b5 Ilias Tsitsimpis
        # Build request's header and body
164 4490d7b5 Ilias Tsitsimpis
        kwargs = {}
165 8f2d7ede Ilias Tsitsimpis
        kwargs['headers'] = copy(headers)
166 c4644612 Ilias Tsitsimpis
        if token is not None:
167 c4644612 Ilias Tsitsimpis
            kwargs['headers']['X-Auth-Token'] = token
168 4490d7b5 Ilias Tsitsimpis
        if body:
169 8f2d7ede Ilias Tsitsimpis
            kwargs['body'] = copy(body)
170 4490d7b5 Ilias Tsitsimpis
            kwargs['headers'].setdefault(
171 4490d7b5 Ilias Tsitsimpis
                'content-type', 'application/octet-stream')
172 4490d7b5 Ilias Tsitsimpis
        kwargs['headers'].setdefault('content-length',
173 4490d7b5 Ilias Tsitsimpis
                                     len(body) if body else 0)
174 4490d7b5 Ilias Tsitsimpis
175 4490d7b5 Ilias Tsitsimpis
        try:
176 6837f014 Ilias Tsitsimpis
            # Get the connection object
177 6837f014 Ilias Tsitsimpis
            with self.conn_class(self.netloc) as conn:
178 6837f014 Ilias Tsitsimpis
                # Send request
179 21190887 Ilias Tsitsimpis
                (message, data, status) = \
180 9848f747 Ilias Tsitsimpis
                    _do_request(conn, method, path, **kwargs)
181 4490d7b5 Ilias Tsitsimpis
        except Exception as err:
182 996061fa Ilias Tsitsimpis
            self.logger.error("Failed to send request: %s" % repr(err))
183 4490d7b5 Ilias Tsitsimpis
            raise AstakosClientException(str(err))
184 4490d7b5 Ilias Tsitsimpis
185 4490d7b5 Ilias Tsitsimpis
        # Return
186 4490d7b5 Ilias Tsitsimpis
        self.logger.debug("Request returned with status %s" % status)
187 8fe6475a Ilias Tsitsimpis
        if status == 400:
188 21190887 Ilias Tsitsimpis
            raise BadRequest(message, data)
189 be284f6a Christos Stavrakakis
        elif status == 401:
190 21190887 Ilias Tsitsimpis
            raise Unauthorized(message, data)
191 be284f6a Christos Stavrakakis
        elif status == 403:
192 21190887 Ilias Tsitsimpis
            raise Forbidden(message, data)
193 be284f6a Christos Stavrakakis
        elif status == 404:
194 21190887 Ilias Tsitsimpis
            raise NotFound(message, data)
195 be284f6a Christos Stavrakakis
        elif status < 200 or status >= 300:
196 21190887 Ilias Tsitsimpis
            raise AstakosClientException(message, data, status)
197 0a2a342c Ilias Tsitsimpis
198 0a2a342c Ilias Tsitsimpis
        try:
199 0a2a342c Ilias Tsitsimpis
            if data:
200 0a2a342c Ilias Tsitsimpis
                return simplejson.loads(unicode(data))
201 0a2a342c Ilias Tsitsimpis
            else:
202 10797183 Ilias Tsitsimpis
                return None
203 0a2a342c Ilias Tsitsimpis
        except Exception as err:
204 19198628 Ilias Tsitsimpis
            self.logger.error("Cannot parse response \"%s\" with simplejson: %s"
205 19198628 Ilias Tsitsimpis
                              % (data, str(err)))
206 0a2a342c Ilias Tsitsimpis
            raise InvalidResponse(str(err), data)
207 4490d7b5 Ilias Tsitsimpis
208 4490d7b5 Ilias Tsitsimpis
    # ------------------------
209 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_AUTHENTICATE``
210 794c94e6 Ilias Tsitsimpis
    def get_user_info(self, token, usage=False):
211 f74d2b69 Ilias Tsitsimpis
        """Authenticate user and get user's info as a dictionary
212 4490d7b5 Ilias Tsitsimpis

213 4490d7b5 Ilias Tsitsimpis
        Keyword arguments:
214 98752f06 Ilias Tsitsimpis
        token   -- user's token (string)
215 4490d7b5 Ilias Tsitsimpis
        usage   -- return usage information for user (boolean)
216 4490d7b5 Ilias Tsitsimpis

217 4490d7b5 Ilias Tsitsimpis
        In case of success return user information (json parsed format).
218 4490d7b5 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
219 4490d7b5 Ilias Tsitsimpis

220 4490d7b5 Ilias Tsitsimpis
        """
221 98752f06 Ilias Tsitsimpis
        # Send request
222 7b5a37fd Ilias Tsitsimpis
        auth_path = copy(API_AUTHENTICATE)
223 4490d7b5 Ilias Tsitsimpis
        if usage:
224 4490d7b5 Ilias Tsitsimpis
            auth_path += "?usage=1"
225 794c94e6 Ilias Tsitsimpis
        return self._call_astakos(token, auth_path)
226 4490d7b5 Ilias Tsitsimpis
227 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
228 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
229 c4644612 Ilias Tsitsimpis
    #   with {'uuids': uuids}
230 794c94e6 Ilias Tsitsimpis
    def _uuid_catalog(self, token, uuids, req_path):
231 1c26b500 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
232 19198628 Ilias Tsitsimpis
        req_body = parse_request({'uuids': uuids}, self.logger)
233 794c94e6 Ilias Tsitsimpis
        data = self._call_astakos(
234 1c26b500 Ilias Tsitsimpis
            token, req_path, req_headers, req_body, "POST")
235 2377e7c2 Ilias Tsitsimpis
        if "uuid_catalog" in data:
236 2377e7c2 Ilias Tsitsimpis
            return data.get("uuid_catalog")
237 2377e7c2 Ilias Tsitsimpis
        else:
238 794c94e6 Ilias Tsitsimpis
            m = "_uuid_catalog request returned %s. No uuid_catalog found" \
239 2377e7c2 Ilias Tsitsimpis
                % data
240 2377e7c2 Ilias Tsitsimpis
            self.logger.error(m)
241 2377e7c2 Ilias Tsitsimpis
            raise AstakosClientException(m)
242 1c26b500 Ilias Tsitsimpis
243 794c94e6 Ilias Tsitsimpis
    def get_usernames(self, token, uuids):
244 4490d7b5 Ilias Tsitsimpis
        """Return a uuid_catalog dictionary for the given uuids
245 4490d7b5 Ilias Tsitsimpis

246 4490d7b5 Ilias Tsitsimpis
        Keyword arguments:
247 98752f06 Ilias Tsitsimpis
        token   -- user's token (string)
248 4490d7b5 Ilias Tsitsimpis
        uuids   -- list of user ids (list of strings)
249 4490d7b5 Ilias Tsitsimpis

250 4490d7b5 Ilias Tsitsimpis
        The returned uuid_catalog is a dictionary with uuids as
251 4490d7b5 Ilias Tsitsimpis
        keys and the corresponding user names as values
252 4490d7b5 Ilias Tsitsimpis

253 4490d7b5 Ilias Tsitsimpis
        """
254 7b5a37fd Ilias Tsitsimpis
        req_path = copy(API_USERCATALOGS)
255 794c94e6 Ilias Tsitsimpis
        return self._uuid_catalog(token, uuids, req_path)
256 4490d7b5 Ilias Tsitsimpis
257 794c94e6 Ilias Tsitsimpis
    def get_username(self, token, uuid):
258 794c94e6 Ilias Tsitsimpis
        """Return the user name of a uuid (see get_usernames)"""
259 10797183 Ilias Tsitsimpis
        check_input("get_username", self.logger, uuid=uuid)
260 794c94e6 Ilias Tsitsimpis
        uuid_dict = self.get_usernames(token, [uuid])
261 2377e7c2 Ilias Tsitsimpis
        if uuid in uuid_dict:
262 2377e7c2 Ilias Tsitsimpis
            return uuid_dict.get(uuid)
263 2377e7c2 Ilias Tsitsimpis
        else:
264 794c94e6 Ilias Tsitsimpis
            raise NoUserName(uuid)
265 4490d7b5 Ilias Tsitsimpis
266 794c94e6 Ilias Tsitsimpis
    def service_get_usernames(self, token, uuids):
267 1c26b500 Ilias Tsitsimpis
        """Return a uuid_catalog dict using a service's token"""
268 7b5a37fd Ilias Tsitsimpis
        req_path = copy(API_SERVICE_USERCATALOGS)
269 794c94e6 Ilias Tsitsimpis
        return self._uuid_catalog(token, uuids, req_path)
270 1c26b500 Ilias Tsitsimpis
271 794c94e6 Ilias Tsitsimpis
    def service_get_username(self, token, uuid):
272 1c26b500 Ilias Tsitsimpis
        """Return the displayName of a uuid using a service's token"""
273 10797183 Ilias Tsitsimpis
        check_input("service_get_username", self.logger, uuid=uuid)
274 794c94e6 Ilias Tsitsimpis
        uuid_dict = self.service_get_usernames(token, [uuid])
275 2377e7c2 Ilias Tsitsimpis
        if uuid in uuid_dict:
276 2377e7c2 Ilias Tsitsimpis
            return uuid_dict.get(uuid)
277 2377e7c2 Ilias Tsitsimpis
        else:
278 794c94e6 Ilias Tsitsimpis
            raise NoUserName(uuid)
279 1c26b500 Ilias Tsitsimpis
280 aaf0a42c Ilias Tsitsimpis
    # ----------------------------------
281 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
282 c4644612 Ilias Tsitsimpis
    #   with {'displaynames': display_names}
283 794c94e6 Ilias Tsitsimpis
    def _displayname_catalog(self, token, display_names, req_path):
284 1c26b500 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
285 19198628 Ilias Tsitsimpis
        req_body = parse_request({'displaynames': display_names}, self.logger)
286 794c94e6 Ilias Tsitsimpis
        data = self._call_astakos(
287 1c26b500 Ilias Tsitsimpis
            token, req_path, req_headers, req_body, "POST")
288 2377e7c2 Ilias Tsitsimpis
        if "displayname_catalog" in data:
289 2377e7c2 Ilias Tsitsimpis
            return data.get("displayname_catalog")
290 2377e7c2 Ilias Tsitsimpis
        else:
291 794c94e6 Ilias Tsitsimpis
            m = "_displayname_catalog request returned %s. " \
292 2377e7c2 Ilias Tsitsimpis
                "No displayname_catalog found" % data
293 2377e7c2 Ilias Tsitsimpis
            self.logger.error(m)
294 2377e7c2 Ilias Tsitsimpis
            raise AstakosClientException(m)
295 1c26b500 Ilias Tsitsimpis
296 794c94e6 Ilias Tsitsimpis
    def get_uuids(self, token, display_names):
297 aaf0a42c Ilias Tsitsimpis
        """Return a displayname_catalog for the given names
298 aaf0a42c Ilias Tsitsimpis

299 aaf0a42c Ilias Tsitsimpis
        Keyword arguments:
300 aaf0a42c Ilias Tsitsimpis
        token           -- user's token (string)
301 aaf0a42c Ilias Tsitsimpis
        display_names   -- list of user names (list of strings)
302 aaf0a42c Ilias Tsitsimpis

303 aaf0a42c Ilias Tsitsimpis
        The returned displayname_catalog is a dictionary with
304 aaf0a42c Ilias Tsitsimpis
        the names as keys and the corresponding uuids as values
305 aaf0a42c Ilias Tsitsimpis

306 aaf0a42c Ilias Tsitsimpis
        """
307 7b5a37fd Ilias Tsitsimpis
        req_path = copy(API_USERCATALOGS)
308 794c94e6 Ilias Tsitsimpis
        return self._displayname_catalog(token, display_names, req_path)
309 aaf0a42c Ilias Tsitsimpis
310 794c94e6 Ilias Tsitsimpis
    def get_uuid(self, token, display_name):
311 aaf0a42c Ilias Tsitsimpis
        """Return the uuid of a name (see getUUIDs)"""
312 10797183 Ilias Tsitsimpis
        check_input("get_uuid", self.logger, display_name=display_name)
313 794c94e6 Ilias Tsitsimpis
        name_dict = self.get_uuids(token, [display_name])
314 2377e7c2 Ilias Tsitsimpis
        if display_name in name_dict:
315 2377e7c2 Ilias Tsitsimpis
            return name_dict.get(display_name)
316 2377e7c2 Ilias Tsitsimpis
        else:
317 2377e7c2 Ilias Tsitsimpis
            raise NoUUID(display_name)
318 aaf0a42c Ilias Tsitsimpis
319 794c94e6 Ilias Tsitsimpis
    def service_get_uuids(self, token, display_names):
320 1c26b500 Ilias Tsitsimpis
        """Return a display_name catalog using a service's token"""
321 7b5a37fd Ilias Tsitsimpis
        req_path = copy(API_SERVICE_USERCATALOGS)
322 794c94e6 Ilias Tsitsimpis
        return self._displayname_catalog(token, display_names, req_path)
323 1c26b500 Ilias Tsitsimpis
324 794c94e6 Ilias Tsitsimpis
    def service_get_uuid(self, token, display_name):
325 1c26b500 Ilias Tsitsimpis
        """Return the uuid of a name using a service's token"""
326 10797183 Ilias Tsitsimpis
        check_input("service_get_uuid", self.logger, display_name=display_name)
327 794c94e6 Ilias Tsitsimpis
        name_dict = self.service_get_uuids(token, [display_name])
328 2377e7c2 Ilias Tsitsimpis
        if display_name in name_dict:
329 2377e7c2 Ilias Tsitsimpis
            return name_dict.get(display_name)
330 2377e7c2 Ilias Tsitsimpis
        else:
331 2377e7c2 Ilias Tsitsimpis
            raise NoUUID(display_name)
332 1c26b500 Ilias Tsitsimpis
333 3f8d6b11 Ilias Tsitsimpis
    # ----------------------------------
334 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_GETSERVICES``
335 794c94e6 Ilias Tsitsimpis
    def get_services(self):
336 3f8d6b11 Ilias Tsitsimpis
        """Return a list of dicts with the registered services"""
337 7b5a37fd Ilias Tsitsimpis
        return self._call_astakos(None, copy(API_GETSERVICES))
338 c4644612 Ilias Tsitsimpis
339 c4644612 Ilias Tsitsimpis
    # ----------------------------------
340 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_RESOURCES``
341 c4644612 Ilias Tsitsimpis
    def get_resources(self):
342 c4644612 Ilias Tsitsimpis
        """Return a dict of dicts with the available resources"""
343 7b5a37fd Ilias Tsitsimpis
        return self._call_astakos(None, copy(API_RESOURCES))
344 3f8d6b11 Ilias Tsitsimpis
345 baeb2ba5 Ilias Tsitsimpis
    # ----------------------------------
346 92683993 Ilias Tsitsimpis
    # do a POST to ``API_FEEDBACK``
347 92683993 Ilias Tsitsimpis
    def send_feedback(self, token, message, data):
348 92683993 Ilias Tsitsimpis
        """Send feedback to astakos service
349 92683993 Ilias Tsitsimpis

350 92683993 Ilias Tsitsimpis
        keyword arguments:
351 92683993 Ilias Tsitsimpis
        token       -- user's token (string)
352 92683993 Ilias Tsitsimpis
        message     -- Feedback message
353 92683993 Ilias Tsitsimpis
        data        -- Additional information about service client status
354 92683993 Ilias Tsitsimpis

355 92683993 Ilias Tsitsimpis
        In case of success return nothing.
356 92683993 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException
357 92683993 Ilias Tsitsimpis

358 92683993 Ilias Tsitsimpis
        """
359 92683993 Ilias Tsitsimpis
        check_input("send_feedback", self.logger, message=message, data=data)
360 92683993 Ilias Tsitsimpis
        path = copy(API_FEEDBACK)
361 92683993 Ilias Tsitsimpis
        req_body = urllib.urlencode(
362 92683993 Ilias Tsitsimpis
            {'feedback_msg': message, 'feedback_data': data})
363 108be31f Ilias Tsitsimpis
        self._call_astakos(token, path, None, req_body, "POST")
364 108be31f Ilias Tsitsimpis
365 108be31f Ilias Tsitsimpis
    # ----------------------------------
366 25a04cdd Ilias Tsitsimpis
    # do a POST to ``API_TOKENS``
367 b173906e Ilias Tsitsimpis
    def get_endpoints(self, token, uuid=None):
368 25a04cdd Ilias Tsitsimpis
        """ Fallback call for authenticate
369 25a04cdd Ilias Tsitsimpis

370 25a04cdd Ilias Tsitsimpis
        Keyword arguments:
371 25a04cdd Ilias Tsitsimpis
        token   -- user's token (string)
372 25a04cdd Ilias Tsitsimpis
        uuid    -- user's uniq id
373 25a04cdd Ilias Tsitsimpis

374 25a04cdd Ilias Tsitsimpis
        It returns back the token as well as information about the token
375 25a04cdd Ilias Tsitsimpis
        holder and the services he/she can acess (in json format).
376 25a04cdd Ilias Tsitsimpis
        In case of error raise an AstakosClientException.
377 25a04cdd Ilias Tsitsimpis

378 25a04cdd Ilias Tsitsimpis
        """
379 25a04cdd Ilias Tsitsimpis
        req_path = copy(API_TOKENS)
380 25a04cdd Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
381 25a04cdd Ilias Tsitsimpis
        body = {'auth': {'token': {'id': token}}}
382 25a04cdd Ilias Tsitsimpis
        if uuid is not None:
383 25a04cdd Ilias Tsitsimpis
            body['auth']['tenantName'] = uuid
384 25a04cdd Ilias Tsitsimpis
        req_body = parse_request(body, self.logger)
385 25a04cdd Ilias Tsitsimpis
        return self._call_astakos(token, req_path, req_headers,
386 25a04cdd Ilias Tsitsimpis
                                  req_body, "POST", False)
387 25a04cdd Ilias Tsitsimpis
388 25a04cdd Ilias Tsitsimpis
    # ----------------------------------
389 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_QUOTAS``
390 baeb2ba5 Ilias Tsitsimpis
    def get_quotas(self, token):
391 fd420756 Ilias Tsitsimpis
        """Get user's quotas
392 fd420756 Ilias Tsitsimpis

393 fd420756 Ilias Tsitsimpis
        Keyword arguments:
394 fd420756 Ilias Tsitsimpis
        token   -- user's token (string)
395 fd420756 Ilias Tsitsimpis

396 fd420756 Ilias Tsitsimpis
        In case of success return a dict of dicts with user's current quotas.
397 fd420756 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException
398 fd420756 Ilias Tsitsimpis

399 fd420756 Ilias Tsitsimpis
        """
400 7b5a37fd Ilias Tsitsimpis
        return self._call_astakos(token, copy(API_QUOTAS))
401 baeb2ba5 Ilias Tsitsimpis
402 fd420756 Ilias Tsitsimpis
    # ----------------------------------
403 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_SERVICE_QUOTAS``
404 271d2c4c Giorgos Korfiatis
    def service_get_quotas(self, token, user=None):
405 5b33b8e5 Giorgos Korfiatis
        """Get all quotas for resources associated with the service
406 5b33b8e5 Giorgos Korfiatis

407 5b33b8e5 Giorgos Korfiatis
        Keyword arguments:
408 5b33b8e5 Giorgos Korfiatis
        token   -- service's token (string)
409 db9f7a2b Giorgos Korfiatis
        user    -- optionally, the uuid of a specific user
410 5b33b8e5 Giorgos Korfiatis

411 5b33b8e5 Giorgos Korfiatis
        In case of success return a dict of dicts of dicts with current quotas
412 db9f7a2b Giorgos Korfiatis
        for all users, or of a specified user, if user argument is set.
413 5b33b8e5 Giorgos Korfiatis
        Otherwise raise an AstakosClientException
414 5b33b8e5 Giorgos Korfiatis

415 5b33b8e5 Giorgos Korfiatis
        """
416 7b5a37fd Ilias Tsitsimpis
        query = copy(API_SERVICE_QUOTAS)
417 db9f7a2b Giorgos Korfiatis
        if user is not None:
418 db9f7a2b Giorgos Korfiatis
            query += "?user=" + user
419 db9f7a2b Giorgos Korfiatis
        return self._call_astakos(token, query)
420 5b33b8e5 Giorgos Korfiatis
421 5b33b8e5 Giorgos Korfiatis
    # ----------------------------------
422 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``
423 fd420756 Ilias Tsitsimpis
    def issue_commission(self, token, request):
424 fd420756 Ilias Tsitsimpis
        """Issue a commission
425 fd420756 Ilias Tsitsimpis

426 fd420756 Ilias Tsitsimpis
        Keyword arguments:
427 925d0fee Ilias Tsitsimpis
        token   -- service's token (string)
428 fd420756 Ilias Tsitsimpis
        request -- commision request (dict)
429 fd420756 Ilias Tsitsimpis

430 fd420756 Ilias Tsitsimpis
        In case of success return commission's id (int).
431 fd420756 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
432 fd420756 Ilias Tsitsimpis

433 fd420756 Ilias Tsitsimpis
        """
434 fd420756 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
435 19198628 Ilias Tsitsimpis
        req_body = parse_request(request, self.logger)
436 fd420756 Ilias Tsitsimpis
        try:
437 7b5a37fd Ilias Tsitsimpis
            response = self._call_astakos(token, copy(API_COMMISSIONS),
438 fd420756 Ilias Tsitsimpis
                                          req_headers, req_body, "POST")
439 fd420756 Ilias Tsitsimpis
        except AstakosClientException as err:
440 fd420756 Ilias Tsitsimpis
            if err.status == 413:
441 fd420756 Ilias Tsitsimpis
                raise QuotaLimit(err.message, err.details)
442 fd420756 Ilias Tsitsimpis
            else:
443 fd420756 Ilias Tsitsimpis
                raise
444 fd420756 Ilias Tsitsimpis
445 fd420756 Ilias Tsitsimpis
        if "serial" in response:
446 fd420756 Ilias Tsitsimpis
            return response['serial']
447 fd420756 Ilias Tsitsimpis
        else:
448 fd420756 Ilias Tsitsimpis
            m = "issue_commission_core request returned %s. No serial found" \
449 fd420756 Ilias Tsitsimpis
                % response
450 fd420756 Ilias Tsitsimpis
            self.logger.error(m)
451 fd420756 Ilias Tsitsimpis
            raise AstakosClientException(m)
452 fd420756 Ilias Tsitsimpis
453 12eab714 Ilias Tsitsimpis
    def issue_one_commission(self, token, holder, source, provisions,
454 3a1bed03 Giorgos Korfiatis
                             name="", force=False, auto_accept=False):
455 12eab714 Ilias Tsitsimpis
        """Issue one commission (with specific holder and source)
456 12eab714 Ilias Tsitsimpis

457 12eab714 Ilias Tsitsimpis
        keyword arguments:
458 12eab714 Ilias Tsitsimpis
        token       -- service's token (string)
459 12eab714 Ilias Tsitsimpis
        holder      -- user's id (string)
460 12eab714 Ilias Tsitsimpis
        source      -- commission's source (ex system) (string)
461 8b68fa76 Giorgos Korfiatis
        provisions  -- resources with their quantity (dict from string to int)
462 3a1bed03 Giorgos Korfiatis
        name        -- description of the commission (string)
463 12eab714 Ilias Tsitsimpis
        force       -- force this commission (boolean)
464 12eab714 Ilias Tsitsimpis
        auto_accept -- auto accept this commission (boolean)
465 12eab714 Ilias Tsitsimpis

466 12eab714 Ilias Tsitsimpis
        In case of success return commission's id (int).
467 12eab714 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
468 12eab714 Ilias Tsitsimpis
        (See also issue_commission)
469 12eab714 Ilias Tsitsimpis

470 12eab714 Ilias Tsitsimpis
        """
471 d5f086f2 Ilias Tsitsimpis
        check_input("issue_one_commission", self.logger,
472 d5f086f2 Ilias Tsitsimpis
                    holder=holder, source=source,
473 d5f086f2 Ilias Tsitsimpis
                    provisions=provisions)
474 12eab714 Ilias Tsitsimpis
475 12eab714 Ilias Tsitsimpis
        request = {}
476 12eab714 Ilias Tsitsimpis
        request["force"] = force
477 12eab714 Ilias Tsitsimpis
        request["auto_accept"] = auto_accept
478 3a1bed03 Giorgos Korfiatis
        request["name"] = name
479 12eab714 Ilias Tsitsimpis
        try:
480 12eab714 Ilias Tsitsimpis
            request["provisions"] = []
481 567f49a2 Giorgos Korfiatis
            for resource, quantity in provisions.iteritems():
482 12eab714 Ilias Tsitsimpis
                t = {"holder": holder, "source": source,
483 12eab714 Ilias Tsitsimpis
                     "resource": resource, "quantity": quantity}
484 12eab714 Ilias Tsitsimpis
                request["provisions"].append(t)
485 12eab714 Ilias Tsitsimpis
        except Exception as err:
486 12eab714 Ilias Tsitsimpis
            self.logger.error(str(err))
487 12eab714 Ilias Tsitsimpis
            raise BadValue(str(err))
488 12eab714 Ilias Tsitsimpis
489 12eab714 Ilias Tsitsimpis
        return self.issue_commission(token, request)
490 12eab714 Ilias Tsitsimpis
491 7a0180ef Ilias Tsitsimpis
    # ----------------------------------
492 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``
493 7a0180ef Ilias Tsitsimpis
    def get_pending_commissions(self, token):
494 7a0180ef Ilias Tsitsimpis
        """Get Pending Commissions
495 7a0180ef Ilias Tsitsimpis

496 7a0180ef Ilias Tsitsimpis
        Keyword arguments:
497 925d0fee Ilias Tsitsimpis
        token   -- service's token (string)
498 7a0180ef Ilias Tsitsimpis

499 7a0180ef Ilias Tsitsimpis
        In case of success return a list of pending commissions' ids
500 7a0180ef Ilias Tsitsimpis
        (list of integers)
501 7a0180ef Ilias Tsitsimpis

502 7a0180ef Ilias Tsitsimpis
        """
503 7b5a37fd Ilias Tsitsimpis
        return self._call_astakos(token, copy(API_COMMISSIONS))
504 7a0180ef Ilias Tsitsimpis
505 994f37b6 Ilias Tsitsimpis
    # ----------------------------------
506 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``/<serial>
507 994f37b6 Ilias Tsitsimpis
    def get_commission_info(self, token, serial):
508 994f37b6 Ilias Tsitsimpis
        """Get Description of a Commission
509 994f37b6 Ilias Tsitsimpis

510 994f37b6 Ilias Tsitsimpis
        Keyword arguments:
511 925d0fee Ilias Tsitsimpis
        token   -- service's token (string)
512 994f37b6 Ilias Tsitsimpis
        serial  -- commission's id (int)
513 994f37b6 Ilias Tsitsimpis

514 994f37b6 Ilias Tsitsimpis
        In case of success return a dict of dicts containing
515 5c418e94 Ilias Tsitsimpis
        informations (details) about the requested commission
516 994f37b6 Ilias Tsitsimpis

517 994f37b6 Ilias Tsitsimpis
        """
518 10797183 Ilias Tsitsimpis
        check_input("get_commission_info", self.logger, serial=serial)
519 994f37b6 Ilias Tsitsimpis
520 7b5a37fd Ilias Tsitsimpis
        path = API_COMMISSIONS + "/" + str(serial)
521 994f37b6 Ilias Tsitsimpis
        return self._call_astakos(token, path)
522 994f37b6 Ilias Tsitsimpis
523 805e294c Ilias Tsitsimpis
    # ----------------------------------
524 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``/<serial>/action"
525 b5008ef0 Ilias Tsitsimpis
    def commission_action(self, token, serial, action):
526 b5008ef0 Ilias Tsitsimpis
        """Perform a commission action
527 805e294c Ilias Tsitsimpis

528 805e294c Ilias Tsitsimpis
        Keyword arguments:
529 925d0fee Ilias Tsitsimpis
        token   -- service's token (string)
530 805e294c Ilias Tsitsimpis
        serial  -- commission's id (int)
531 805e294c Ilias Tsitsimpis
        action  -- action to perform, currently accept/reject (string)
532 805e294c Ilias Tsitsimpis

533 805e294c Ilias Tsitsimpis
        In case of success return nothing.
534 805e294c Ilias Tsitsimpis

535 805e294c Ilias Tsitsimpis
        """
536 10797183 Ilias Tsitsimpis
        check_input("commission_action", self.logger,
537 10797183 Ilias Tsitsimpis
                    serial=serial, action=action)
538 805e294c Ilias Tsitsimpis
539 7b5a37fd Ilias Tsitsimpis
        path = API_COMMISSIONS + "/" + str(serial) + "/action"
540 805e294c Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
541 19198628 Ilias Tsitsimpis
        req_body = parse_request({str(action): ""}, self.logger)
542 805e294c Ilias Tsitsimpis
        self._call_astakos(token, path, req_headers, req_body, "POST")
543 805e294c Ilias Tsitsimpis
544 b5008ef0 Ilias Tsitsimpis
    def accept_commission(self, token, serial):
545 b5008ef0 Ilias Tsitsimpis
        """Accept a commission (see commission_action)"""
546 b5008ef0 Ilias Tsitsimpis
        self.commission_action(token, serial, "accept")
547 805e294c Ilias Tsitsimpis
548 b5008ef0 Ilias Tsitsimpis
    def reject_commission(self, token, serial):
549 b5008ef0 Ilias Tsitsimpis
        """Reject a commission (see commission_action)"""
550 b5008ef0 Ilias Tsitsimpis
        self.commission_action(token, serial, "reject")
551 805e294c Ilias Tsitsimpis
552 81875157 Ilias Tsitsimpis
    # ----------------------------------
553 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS_ACTION``
554 81875157 Ilias Tsitsimpis
    def resolve_commissions(self, token, accept_serials, reject_serials):
555 81875157 Ilias Tsitsimpis
        """Resolve multiple commissions at once
556 81875157 Ilias Tsitsimpis

557 81875157 Ilias Tsitsimpis
        Keyword arguments:
558 81875157 Ilias Tsitsimpis
        token           -- service's token (string)
559 81875157 Ilias Tsitsimpis
        accept_serials  -- commissions to accept (list of ints)
560 81875157 Ilias Tsitsimpis
        reject_serials  -- commissions to reject (list of ints)
561 81875157 Ilias Tsitsimpis

562 81875157 Ilias Tsitsimpis
        In case of success return a dict of dicts describing which
563 81875157 Ilias Tsitsimpis
        commissions accepted, which rejected and which failed to
564 81875157 Ilias Tsitsimpis
        resolved.
565 81875157 Ilias Tsitsimpis

566 81875157 Ilias Tsitsimpis
        """
567 d5f086f2 Ilias Tsitsimpis
        check_input("resolve_commissions", self.logger,
568 d5f086f2 Ilias Tsitsimpis
                    accept_serials=accept_serials,
569 d5f086f2 Ilias Tsitsimpis
                    reject_serials=reject_serials)
570 81875157 Ilias Tsitsimpis
571 7b5a37fd Ilias Tsitsimpis
        path = copy(API_COMMISSIONS_ACTION)
572 81875157 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
573 81875157 Ilias Tsitsimpis
        req_body = parse_request({"accept": accept_serials,
574 81875157 Ilias Tsitsimpis
                                  "reject": reject_serials},
575 81875157 Ilias Tsitsimpis
                                 self.logger)
576 81875157 Ilias Tsitsimpis
        return self._call_astakos(token, path, req_headers, req_body, "POST")
577 81875157 Ilias Tsitsimpis
578 f54cf5e4 Ilias Tsitsimpis
579 f54cf5e4 Ilias Tsitsimpis
# --------------------------------------------------------------------
580 cbc0b438 Ilias Tsitsimpis
# Private functions
581 f93cc364 Ilias Tsitsimpis
# We want _doRequest to be a distinct function
582 f93cc364 Ilias Tsitsimpis
# so that we can replace it during unit tests.
583 794c94e6 Ilias Tsitsimpis
def _do_request(conn, method, url, **kwargs):
584 f8388a90 Ilias Tsitsimpis
    """The actual request. This function can easily be mocked"""
585 f8388a90 Ilias Tsitsimpis
    conn.request(method, url, **kwargs)
586 f8388a90 Ilias Tsitsimpis
    response = conn.getresponse()
587 f8388a90 Ilias Tsitsimpis
    length = response.getheader('content-length', None)
588 f8388a90 Ilias Tsitsimpis
    data = response.read(length)
589 f8388a90 Ilias Tsitsimpis
    status = int(response.status)
590 21190887 Ilias Tsitsimpis
    message = response.reason
591 21190887 Ilias Tsitsimpis
    return (message, data, status)