Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / __init__.py @ 837d85bb

History | View | Annotate | Download (34.1 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 2c9c147e Ilias Tsitsimpis
"""
35 2c9c147e Ilias Tsitsimpis
Simple and minimal client for the Astakos authentication service
36 2c9c147e Ilias Tsitsimpis
"""
37 2c9c147e Ilias Tsitsimpis
38 cbc0b438 Ilias Tsitsimpis
import logging
39 cbc0b438 Ilias Tsitsimpis
import urlparse
40 bc5032a4 Ilias Tsitsimpis
import urllib
41 6f64b6d0 Ilias Tsitsimpis
import hashlib
42 8f2d7ede Ilias Tsitsimpis
from copy import copy
43 cbc0b438 Ilias Tsitsimpis
44 cbc0b438 Ilias Tsitsimpis
import simplejson
45 10797183 Ilias Tsitsimpis
from astakosclient.utils import \
46 2c9c147e Ilias Tsitsimpis
    retry_dec, scheme_to_class, parse_request, check_input, join_urls
47 f93cc364 Ilias Tsitsimpis
from astakosclient.errors import \
48 2377e7c2 Ilias Tsitsimpis
    AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden, \
49 2c9c147e Ilias Tsitsimpis
    NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse, NoEndpoints
50 7b5a37fd Ilias Tsitsimpis
51 7b5a37fd Ilias Tsitsimpis
52 7b5a37fd Ilias Tsitsimpis
# --------------------------------------------------------------------
53 4490d7b5 Ilias Tsitsimpis
# Astakos Client Class
54 bc5032a4 Ilias Tsitsimpis
55 794c94e6 Ilias Tsitsimpis
def get_token_from_cookie(request, cookie_name):
56 bc5032a4 Ilias Tsitsimpis
    """Extract token from the cookie name provided
57 bc5032a4 Ilias Tsitsimpis

58 bc5032a4 Ilias Tsitsimpis
    Cookie should be in the same form as astakos
59 bc5032a4 Ilias Tsitsimpis
    service sets its cookie contents:
60 bc5032a4 Ilias Tsitsimpis
        <user_uniq>|<user_token>
61 bc5032a4 Ilias Tsitsimpis

62 bc5032a4 Ilias Tsitsimpis
    """
63 bc5032a4 Ilias Tsitsimpis
    try:
64 bc5032a4 Ilias Tsitsimpis
        cookie_content = urllib.unquote(request.COOKIE.get(cookie_name, None))
65 bc5032a4 Ilias Tsitsimpis
        return cookie_content.split("|")[1]
66 2c9c147e Ilias Tsitsimpis
    except BaseException:
67 bc5032a4 Ilias Tsitsimpis
        return None
68 bc5032a4 Ilias Tsitsimpis
69 bc5032a4 Ilias Tsitsimpis
70 2c9c147e Ilias Tsitsimpis
# Too many instance attributes. pylint: disable-msg=R0902
71 2c9c147e Ilias Tsitsimpis
# Too many public methods. pylint: disable-msg=R0904
72 2c9c147e Ilias Tsitsimpis
class AstakosClient(object):
73 4490d7b5 Ilias Tsitsimpis
    """AstakosClient Class Implementation"""
74 4490d7b5 Ilias Tsitsimpis
75 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
76 2c9c147e Ilias Tsitsimpis
    # Initialize AstakosClient Class
77 2c9c147e Ilias Tsitsimpis
    # Too many arguments. pylint: disable-msg=R0913
78 2c9c147e Ilias Tsitsimpis
    # Too many local variables. pylint: disable-msg=R0914
79 2c9c147e Ilias Tsitsimpis
    # Too many statements. pylint: disable-msg=R0915
80 2c9c147e Ilias Tsitsimpis
    def __init__(self, token, auth_url,
81 2c9c147e Ilias Tsitsimpis
                 retry=0, use_pool=False, pool_size=8, logger=None):
82 c4644612 Ilias Tsitsimpis
        """Initialize AstakosClient Class
83 4490d7b5 Ilias Tsitsimpis

84 4490d7b5 Ilias Tsitsimpis
        Keyword arguments:
85 2c9c147e Ilias Tsitsimpis
        token       -- user's/service's token (string)
86 2c9c147e Ilias Tsitsimpis
        auth_url    -- i.e https://accounts.example.com/identity/v2.0
87 949baf4d Ilias Tsitsimpis
        retry       -- how many time to retry (integer)
88 2c9c147e Ilias Tsitsimpis
        use_pool    -- use objpool for http requests (boolean)
89 2c9c147e Ilias Tsitsimpis
        pool_size   -- if using pool, define the pool size
90 4490d7b5 Ilias Tsitsimpis
        logger      -- pass a different logger
91 4490d7b5 Ilias Tsitsimpis

92 4490d7b5 Ilias Tsitsimpis
        """
93 2c9c147e Ilias Tsitsimpis
94 2c9c147e Ilias Tsitsimpis
        # Get logger
95 4490d7b5 Ilias Tsitsimpis
        if logger is None:
96 7eb32034 Ilias Tsitsimpis
            logging.basicConfig(
97 7eb32034 Ilias Tsitsimpis
                format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
98 7eb32034 Ilias Tsitsimpis
                datefmt='%Y-%m-%d %H:%M:%S',
99 7eb32034 Ilias Tsitsimpis
                level=logging.INFO)
100 4490d7b5 Ilias Tsitsimpis
            logger = logging.getLogger("astakosclient")
101 2c9c147e Ilias Tsitsimpis
        logger.debug("Intialize AstakosClient: auth_url = %s, "
102 2c9c147e Ilias Tsitsimpis
                     "use_pool = %s, pool_size = %s",
103 2c9c147e Ilias Tsitsimpis
                     auth_url, use_pool, pool_size)
104 4490d7b5 Ilias Tsitsimpis
105 2c9c147e Ilias Tsitsimpis
        # Check that token and auth_url (mandatory options) are given
106 2c9c147e Ilias Tsitsimpis
        check_input("__init__", logger, token=token, auth_url=auth_url)
107 4490d7b5 Ilias Tsitsimpis
108 2c9c147e Ilias Tsitsimpis
        # Initialize connection class
109 2c9c147e Ilias Tsitsimpis
        parsed_auth_url = urlparse.urlparse(auth_url)
110 2c9c147e Ilias Tsitsimpis
        conn_class = \
111 2c9c147e Ilias Tsitsimpis
            scheme_to_class(parsed_auth_url.scheme, use_pool, pool_size)
112 e169a337 Ilias Tsitsimpis
        if conn_class is None:
113 2c9c147e Ilias Tsitsimpis
            msg = "Unsupported scheme: %s" % parsed_auth_url.scheme
114 2c9c147e Ilias Tsitsimpis
            logger.error(msg)
115 2c9c147e Ilias Tsitsimpis
            raise BadValue(msg)
116 4490d7b5 Ilias Tsitsimpis
117 2c9c147e Ilias Tsitsimpis
        # Save astakos base url, logger, connection class etc in our class
118 949baf4d Ilias Tsitsimpis
        self.retry = retry
119 4490d7b5 Ilias Tsitsimpis
        self.logger = logger
120 2c9c147e Ilias Tsitsimpis
        self.token = token
121 2c9c147e Ilias Tsitsimpis
        self.astakos_base_url = parsed_auth_url.netloc
122 2c9c147e Ilias Tsitsimpis
        self.scheme = parsed_auth_url.scheme
123 e169a337 Ilias Tsitsimpis
        self.conn_class = conn_class
124 4490d7b5 Ilias Tsitsimpis
125 2c9c147e Ilias Tsitsimpis
        # Initialize astakos api prefixes
126 2c9c147e Ilias Tsitsimpis
        # API urls under auth_url
127 2c9c147e Ilias Tsitsimpis
        self.auth_prefix = parsed_auth_url.path
128 2c9c147e Ilias Tsitsimpis
        self.api_tokens = join_urls(self.auth_prefix, "tokens")
129 2c9c147e Ilias Tsitsimpis
130 2c9c147e Ilias Tsitsimpis
        # ------------------------------
131 2c9c147e Ilias Tsitsimpis
        # API urls under account_url
132 2c9c147e Ilias Tsitsimpis
        # Get account_url from get_endpoints
133 2c9c147e Ilias Tsitsimpis
        # get_endpoints needs self.api_tokens
134 2c9c147e Ilias Tsitsimpis
        endpoints = self.get_endpoints(non_authentication=True)
135 2c9c147e Ilias Tsitsimpis
        account_service_catalog = parse_endpoints(
136 2c9c147e Ilias Tsitsimpis
            endpoints, ep_name="astakos_account", ep_version_id="v1.0")
137 2c9c147e Ilias Tsitsimpis
        self.account_url = \
138 2c9c147e Ilias Tsitsimpis
            account_service_catalog[0]['endpoints'][0]['publicURL']
139 2c9c147e Ilias Tsitsimpis
        parsed_account_url = urlparse.urlparse(self.account_url)
140 2c9c147e Ilias Tsitsimpis
141 2c9c147e Ilias Tsitsimpis
        self.account_prefix = parsed_account_url.path
142 2c9c147e Ilias Tsitsimpis
        self.logger.debug("Got account_prefix \"%s\"" % self.account_prefix)
143 2c9c147e Ilias Tsitsimpis
144 2c9c147e Ilias Tsitsimpis
        self.api_authenticate = join_urls(
145 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "authenticate")
146 2c9c147e Ilias Tsitsimpis
        self.api_usercatalogs = join_urls(
147 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "user_catalogs")
148 2c9c147e Ilias Tsitsimpis
        self.api_service_usercatalogs = join_urls(
149 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "service/user_catalogs")
150 2c9c147e Ilias Tsitsimpis
        self.api_resources = join_urls(
151 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "resources")
152 2c9c147e Ilias Tsitsimpis
        self.api_quotas = join_urls(
153 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "quotas")
154 2c9c147e Ilias Tsitsimpis
        self.api_service_quotas = join_urls(
155 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "service_quotas")
156 2c9c147e Ilias Tsitsimpis
        self.api_commissions = join_urls(
157 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "commissions")
158 2c9c147e Ilias Tsitsimpis
        self.api_commissions_action = join_urls(
159 2c9c147e Ilias Tsitsimpis
            self.api_commissions, "action")
160 2c9c147e Ilias Tsitsimpis
        self.api_feedback = join_urls(
161 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "feedback")
162 2c9c147e Ilias Tsitsimpis
        self.api_projects = join_urls(
163 2c9c147e Ilias Tsitsimpis
            self.account_prefix, "projects")
164 2c9c147e Ilias Tsitsimpis
        self.api_applications = join_urls(
165 2c9c147e Ilias Tsitsimpis
            self.api_projects, "apps")
166 2c9c147e Ilias Tsitsimpis
        self.api_memberships = join_urls(
167 2c9c147e Ilias Tsitsimpis
            self.api_projects, "memberships")
168 2c9c147e Ilias Tsitsimpis
169 2c9c147e Ilias Tsitsimpis
        # ------------------------------
170 2c9c147e Ilias Tsitsimpis
        # API urls under ui_url
171 2c9c147e Ilias Tsitsimpis
        # Get ui url from get_endpoints
172 2c9c147e Ilias Tsitsimpis
        # get_endpoints needs self.api_tokens
173 2c9c147e Ilias Tsitsimpis
        ui_service_catalog = parse_endpoints(
174 2c9c147e Ilias Tsitsimpis
            endpoints, ep_name="astakos_account", ep_version_id="v1.0")
175 2c9c147e Ilias Tsitsimpis
        parsed_ui_url = urlparse.urlparse(
176 2c9c147e Ilias Tsitsimpis
            ui_service_catalog[0]['endpoints'][0]['SNF:uiURL'])
177 2c9c147e Ilias Tsitsimpis
        self.ui_url = \
178 2c9c147e Ilias Tsitsimpis
            ui_service_catalog[0]['endpoints'][0]['SNF:uiURL']
179 2c9c147e Ilias Tsitsimpis
        parsed_ui_url = urlparse.urlparse(self.ui_url)
180 2c9c147e Ilias Tsitsimpis
181 2c9c147e Ilias Tsitsimpis
        self.ui_prefix = parsed_ui_url.path
182 2c9c147e Ilias Tsitsimpis
        self.logger.debug("Got ui_prefix \"%s\"" % self.ui_prefix)
183 2c9c147e Ilias Tsitsimpis
184 2c9c147e Ilias Tsitsimpis
        self.api_getservices = join_urls(self.ui_prefix, "get_services")
185 2c9c147e Ilias Tsitsimpis
186 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
187 2c9c147e Ilias Tsitsimpis
    @retry_dec
188 2c9c147e Ilias Tsitsimpis
    def _call_astakos(self, request_path, headers=None,
189 25a04cdd Ilias Tsitsimpis
                      body=None, method="GET", log_body=True):
190 4490d7b5 Ilias Tsitsimpis
        """Make the actual call to Astakos Service"""
191 2c9c147e Ilias Tsitsimpis
        hashed_token = hashlib.sha1()
192 2c9c147e Ilias Tsitsimpis
        hashed_token.update(self.token)
193 4490d7b5 Ilias Tsitsimpis
        self.logger.debug(
194 2c9c147e Ilias Tsitsimpis
            "Make a %s request to %s, using token with hash %s, "
195 2c9c147e Ilias Tsitsimpis
            "with headers %s and body %s",
196 2c9c147e Ilias Tsitsimpis
            method, request_path, hashed_token.hexdigest(), headers,
197 2c9c147e Ilias Tsitsimpis
            body if log_body else "(not logged)")
198 4490d7b5 Ilias Tsitsimpis
199 98752f06 Ilias Tsitsimpis
        # Check Input
200 8f2d7ede Ilias Tsitsimpis
        if headers is None:
201 8f2d7ede Ilias Tsitsimpis
            headers = {}
202 8f2d7ede Ilias Tsitsimpis
        if body is None:
203 8f2d7ede Ilias Tsitsimpis
            body = {}
204 98752f06 Ilias Tsitsimpis
205 4490d7b5 Ilias Tsitsimpis
        # Build request's header and body
206 4490d7b5 Ilias Tsitsimpis
        kwargs = {}
207 8f2d7ede Ilias Tsitsimpis
        kwargs['headers'] = copy(headers)
208 2c9c147e Ilias Tsitsimpis
        kwargs['headers']['X-Auth-Token'] = self.token
209 4490d7b5 Ilias Tsitsimpis
        if body:
210 8f2d7ede Ilias Tsitsimpis
            kwargs['body'] = copy(body)
211 4490d7b5 Ilias Tsitsimpis
            kwargs['headers'].setdefault(
212 4490d7b5 Ilias Tsitsimpis
                'content-type', 'application/octet-stream')
213 4490d7b5 Ilias Tsitsimpis
        kwargs['headers'].setdefault('content-length',
214 4490d7b5 Ilias Tsitsimpis
                                     len(body) if body else 0)
215 4490d7b5 Ilias Tsitsimpis
216 4490d7b5 Ilias Tsitsimpis
        try:
217 6837f014 Ilias Tsitsimpis
            # Get the connection object
218 2c9c147e Ilias Tsitsimpis
            with self.conn_class(self.astakos_base_url) as conn:
219 6837f014 Ilias Tsitsimpis
                # Send request
220 2c9c147e Ilias Tsitsimpis
                # Used * or ** magic. pylint: disable-msg=W0142
221 21190887 Ilias Tsitsimpis
                (message, data, status) = \
222 2c9c147e Ilias Tsitsimpis
                    _do_request(conn, method, request_path, **kwargs)
223 4490d7b5 Ilias Tsitsimpis
        except Exception as err:
224 996061fa Ilias Tsitsimpis
            self.logger.error("Failed to send request: %s" % repr(err))
225 4490d7b5 Ilias Tsitsimpis
            raise AstakosClientException(str(err))
226 4490d7b5 Ilias Tsitsimpis
227 4490d7b5 Ilias Tsitsimpis
        # Return
228 4490d7b5 Ilias Tsitsimpis
        self.logger.debug("Request returned with status %s" % status)
229 8fe6475a Ilias Tsitsimpis
        if status == 400:
230 21190887 Ilias Tsitsimpis
            raise BadRequest(message, data)
231 be284f6a Christos Stavrakakis
        elif status == 401:
232 21190887 Ilias Tsitsimpis
            raise Unauthorized(message, data)
233 be284f6a Christos Stavrakakis
        elif status == 403:
234 21190887 Ilias Tsitsimpis
            raise Forbidden(message, data)
235 be284f6a Christos Stavrakakis
        elif status == 404:
236 21190887 Ilias Tsitsimpis
            raise NotFound(message, data)
237 be284f6a Christos Stavrakakis
        elif status < 200 or status >= 300:
238 21190887 Ilias Tsitsimpis
            raise AstakosClientException(message, data, status)
239 0a2a342c Ilias Tsitsimpis
240 0a2a342c Ilias Tsitsimpis
        try:
241 0a2a342c Ilias Tsitsimpis
            if data:
242 0a2a342c Ilias Tsitsimpis
                return simplejson.loads(unicode(data))
243 0a2a342c Ilias Tsitsimpis
            else:
244 10797183 Ilias Tsitsimpis
                return None
245 0a2a342c Ilias Tsitsimpis
        except Exception as err:
246 8975f6f6 Christos Stavrakakis
            msg = "Cannot parse response \"%s\" with simplejson: %s"
247 8975f6f6 Christos Stavrakakis
            self.logger.error(msg % (data, str(err)))
248 0a2a342c Ilias Tsitsimpis
            raise InvalidResponse(str(err), data)
249 4490d7b5 Ilias Tsitsimpis
250 4490d7b5 Ilias Tsitsimpis
    # ------------------------
251 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_AUTHENTICATE``
252 837d85bb Ilias Tsitsimpis
    def get_user_info(self):
253 f74d2b69 Ilias Tsitsimpis
        """Authenticate user and get user's info as a dictionary
254 4490d7b5 Ilias Tsitsimpis

255 4490d7b5 Ilias Tsitsimpis
        In case of success return user information (json parsed format).
256 4490d7b5 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
257 4490d7b5 Ilias Tsitsimpis

258 4490d7b5 Ilias Tsitsimpis
        """
259 837d85bb Ilias Tsitsimpis
        return self._call_astakos(self.api_authenticate)
260 4490d7b5 Ilias Tsitsimpis
261 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
262 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
263 c4644612 Ilias Tsitsimpis
    #   with {'uuids': uuids}
264 2c9c147e Ilias Tsitsimpis
    def _uuid_catalog(self, uuids, req_path):
265 2c9c147e Ilias Tsitsimpis
        """Helper function to retrieve uuid catalog"""
266 1c26b500 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
267 19198628 Ilias Tsitsimpis
        req_body = parse_request({'uuids': uuids}, self.logger)
268 2c9c147e Ilias Tsitsimpis
        data = self._call_astakos(req_path, headers=req_headers,
269 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
270 2377e7c2 Ilias Tsitsimpis
        if "uuid_catalog" in data:
271 2377e7c2 Ilias Tsitsimpis
            return data.get("uuid_catalog")
272 2377e7c2 Ilias Tsitsimpis
        else:
273 2c9c147e Ilias Tsitsimpis
            msg = "_uuid_catalog request returned %s. No uuid_catalog found" \
274 2c9c147e Ilias Tsitsimpis
                  % data
275 2c9c147e Ilias Tsitsimpis
            self.logger.error(msg)
276 2c9c147e Ilias Tsitsimpis
            raise AstakosClientException(msg)
277 1c26b500 Ilias Tsitsimpis
278 2c9c147e Ilias Tsitsimpis
    def get_usernames(self, uuids):
279 4490d7b5 Ilias Tsitsimpis
        """Return a uuid_catalog dictionary for the given uuids
280 4490d7b5 Ilias Tsitsimpis

281 4490d7b5 Ilias Tsitsimpis
        Keyword arguments:
282 4490d7b5 Ilias Tsitsimpis
        uuids   -- list of user ids (list of strings)
283 4490d7b5 Ilias Tsitsimpis

284 4490d7b5 Ilias Tsitsimpis
        The returned uuid_catalog is a dictionary with uuids as
285 4490d7b5 Ilias Tsitsimpis
        keys and the corresponding user names as values
286 4490d7b5 Ilias Tsitsimpis

287 4490d7b5 Ilias Tsitsimpis
        """
288 2c9c147e Ilias Tsitsimpis
        return self._uuid_catalog(uuids, self.api_usercatalogs)
289 4490d7b5 Ilias Tsitsimpis
290 2c9c147e Ilias Tsitsimpis
    def get_username(self, uuid):
291 794c94e6 Ilias Tsitsimpis
        """Return the user name of a uuid (see get_usernames)"""
292 10797183 Ilias Tsitsimpis
        check_input("get_username", self.logger, uuid=uuid)
293 2c9c147e Ilias Tsitsimpis
        uuid_dict = self.get_usernames([uuid])
294 2377e7c2 Ilias Tsitsimpis
        if uuid in uuid_dict:
295 2377e7c2 Ilias Tsitsimpis
            return uuid_dict.get(uuid)
296 2377e7c2 Ilias Tsitsimpis
        else:
297 794c94e6 Ilias Tsitsimpis
            raise NoUserName(uuid)
298 4490d7b5 Ilias Tsitsimpis
299 2c9c147e Ilias Tsitsimpis
    def service_get_usernames(self, uuids):
300 1c26b500 Ilias Tsitsimpis
        """Return a uuid_catalog dict using a service's token"""
301 2c9c147e Ilias Tsitsimpis
        return self._uuid_catalog(uuids, self.api_service_usercatalogs)
302 1c26b500 Ilias Tsitsimpis
303 2c9c147e Ilias Tsitsimpis
    def service_get_username(self, uuid):
304 1c26b500 Ilias Tsitsimpis
        """Return the displayName of a uuid using a service's token"""
305 10797183 Ilias Tsitsimpis
        check_input("service_get_username", self.logger, uuid=uuid)
306 2c9c147e Ilias Tsitsimpis
        uuid_dict = self.service_get_usernames([uuid])
307 2377e7c2 Ilias Tsitsimpis
        if uuid in uuid_dict:
308 2377e7c2 Ilias Tsitsimpis
            return uuid_dict.get(uuid)
309 2377e7c2 Ilias Tsitsimpis
        else:
310 794c94e6 Ilias Tsitsimpis
            raise NoUserName(uuid)
311 1c26b500 Ilias Tsitsimpis
312 aaf0a42c Ilias Tsitsimpis
    # ----------------------------------
313 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
314 c4644612 Ilias Tsitsimpis
    #   with {'displaynames': display_names}
315 2c9c147e Ilias Tsitsimpis
    def _displayname_catalog(self, display_names, req_path):
316 2c9c147e Ilias Tsitsimpis
        """Helper function to retrieve display names catalog"""
317 1c26b500 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
318 19198628 Ilias Tsitsimpis
        req_body = parse_request({'displaynames': display_names}, self.logger)
319 2c9c147e Ilias Tsitsimpis
        data = self._call_astakos(req_path, headers=req_headers,
320 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
321 2377e7c2 Ilias Tsitsimpis
        if "displayname_catalog" in data:
322 2377e7c2 Ilias Tsitsimpis
            return data.get("displayname_catalog")
323 2377e7c2 Ilias Tsitsimpis
        else:
324 2c9c147e Ilias Tsitsimpis
            msg = "_displayname_catalog request returned %s. " \
325 2c9c147e Ilias Tsitsimpis
                  "No displayname_catalog found" % data
326 2c9c147e Ilias Tsitsimpis
            self.logger.error(msg)
327 2c9c147e Ilias Tsitsimpis
            raise AstakosClientException(msg)
328 1c26b500 Ilias Tsitsimpis
329 2c9c147e Ilias Tsitsimpis
    def get_uuids(self, display_names):
330 aaf0a42c Ilias Tsitsimpis
        """Return a displayname_catalog for the given names
331 aaf0a42c Ilias Tsitsimpis

332 aaf0a42c Ilias Tsitsimpis
        Keyword arguments:
333 aaf0a42c Ilias Tsitsimpis
        display_names   -- list of user names (list of strings)
334 aaf0a42c Ilias Tsitsimpis

335 aaf0a42c Ilias Tsitsimpis
        The returned displayname_catalog is a dictionary with
336 aaf0a42c Ilias Tsitsimpis
        the names as keys and the corresponding uuids as values
337 aaf0a42c Ilias Tsitsimpis

338 aaf0a42c Ilias Tsitsimpis
        """
339 2c9c147e Ilias Tsitsimpis
        return self._displayname_catalog(
340 2c9c147e Ilias Tsitsimpis
            display_names, self.api_usercatalogs)
341 aaf0a42c Ilias Tsitsimpis
342 2c9c147e Ilias Tsitsimpis
    def get_uuid(self, display_name):
343 aaf0a42c Ilias Tsitsimpis
        """Return the uuid of a name (see getUUIDs)"""
344 10797183 Ilias Tsitsimpis
        check_input("get_uuid", self.logger, display_name=display_name)
345 2c9c147e Ilias Tsitsimpis
        name_dict = self.get_uuids([display_name])
346 2377e7c2 Ilias Tsitsimpis
        if display_name in name_dict:
347 2377e7c2 Ilias Tsitsimpis
            return name_dict.get(display_name)
348 2377e7c2 Ilias Tsitsimpis
        else:
349 2377e7c2 Ilias Tsitsimpis
            raise NoUUID(display_name)
350 aaf0a42c Ilias Tsitsimpis
351 2c9c147e Ilias Tsitsimpis
    def service_get_uuids(self, display_names):
352 1c26b500 Ilias Tsitsimpis
        """Return a display_name catalog using a service's token"""
353 2c9c147e Ilias Tsitsimpis
        return self._displayname_catalog(
354 2c9c147e Ilias Tsitsimpis
            display_names, self.api_service_usercatalogs)
355 1c26b500 Ilias Tsitsimpis
356 2c9c147e Ilias Tsitsimpis
    def service_get_uuid(self, display_name):
357 1c26b500 Ilias Tsitsimpis
        """Return the uuid of a name using a service's token"""
358 10797183 Ilias Tsitsimpis
        check_input("service_get_uuid", self.logger, display_name=display_name)
359 2c9c147e Ilias Tsitsimpis
        name_dict = self.service_get_uuids([display_name])
360 2377e7c2 Ilias Tsitsimpis
        if display_name in name_dict:
361 2377e7c2 Ilias Tsitsimpis
            return name_dict.get(display_name)
362 2377e7c2 Ilias Tsitsimpis
        else:
363 2377e7c2 Ilias Tsitsimpis
            raise NoUUID(display_name)
364 1c26b500 Ilias Tsitsimpis
365 3f8d6b11 Ilias Tsitsimpis
    # ----------------------------------
366 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_GETSERVICES``
367 794c94e6 Ilias Tsitsimpis
    def get_services(self):
368 3f8d6b11 Ilias Tsitsimpis
        """Return a list of dicts with the registered services"""
369 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_getservices)
370 c4644612 Ilias Tsitsimpis
371 c4644612 Ilias Tsitsimpis
    # ----------------------------------
372 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_RESOURCES``
373 c4644612 Ilias Tsitsimpis
    def get_resources(self):
374 c4644612 Ilias Tsitsimpis
        """Return a dict of dicts with the available resources"""
375 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_resources)
376 3f8d6b11 Ilias Tsitsimpis
377 baeb2ba5 Ilias Tsitsimpis
    # ----------------------------------
378 92683993 Ilias Tsitsimpis
    # do a POST to ``API_FEEDBACK``
379 2c9c147e Ilias Tsitsimpis
    def send_feedback(self, message, data):
380 92683993 Ilias Tsitsimpis
        """Send feedback to astakos service
381 92683993 Ilias Tsitsimpis

382 92683993 Ilias Tsitsimpis
        keyword arguments:
383 92683993 Ilias Tsitsimpis
        message     -- Feedback message
384 92683993 Ilias Tsitsimpis
        data        -- Additional information about service client status
385 92683993 Ilias Tsitsimpis

386 92683993 Ilias Tsitsimpis
        In case of success return nothing.
387 92683993 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException
388 92683993 Ilias Tsitsimpis

389 92683993 Ilias Tsitsimpis
        """
390 92683993 Ilias Tsitsimpis
        check_input("send_feedback", self.logger, message=message, data=data)
391 92683993 Ilias Tsitsimpis
        req_body = urllib.urlencode(
392 92683993 Ilias Tsitsimpis
            {'feedback_msg': message, 'feedback_data': data})
393 2c9c147e Ilias Tsitsimpis
        self._call_astakos(self.api_feedback, headers=None,
394 2c9c147e Ilias Tsitsimpis
                           body=req_body, method="POST")
395 108be31f Ilias Tsitsimpis
396 108be31f Ilias Tsitsimpis
    # ----------------------------------
397 25a04cdd Ilias Tsitsimpis
    # do a POST to ``API_TOKENS``
398 2c9c147e Ilias Tsitsimpis
    def get_endpoints(self, tenant_name=None, non_authentication=False):
399 2c9c147e Ilias Tsitsimpis
        """ Authenticate and get services' endpoints
400 25a04cdd Ilias Tsitsimpis

401 25a04cdd Ilias Tsitsimpis
        Keyword arguments:
402 2c9c147e Ilias Tsitsimpis
        tenant_name         -- user's uniq id (optional)
403 2c9c147e Ilias Tsitsimpis
        non_authentication  -- get only non authentication protected info
404 2c9c147e Ilias Tsitsimpis

405 25a04cdd Ilias Tsitsimpis

406 25a04cdd Ilias Tsitsimpis
        It returns back the token as well as information about the token
407 25a04cdd Ilias Tsitsimpis
        holder and the services he/she can acess (in json format).
408 2c9c147e Ilias Tsitsimpis

409 2c9c147e Ilias Tsitsimpis
        The tenant_name is optional and if it is given it must match the
410 2c9c147e Ilias Tsitsimpis
        user's uuid.
411 2c9c147e Ilias Tsitsimpis

412 2c9c147e Ilias Tsitsimpis
        In case on of the `name', `type', `region', `version_id' parameters
413 2c9c147e Ilias Tsitsimpis
        is given, return only the endpoints that match all of these criteria.
414 2c9c147e Ilias Tsitsimpis
        If no match is found then raise NoEndpoints exception.
415 2c9c147e Ilias Tsitsimpis

416 25a04cdd Ilias Tsitsimpis
        In case of error raise an AstakosClientException.
417 25a04cdd Ilias Tsitsimpis

418 25a04cdd Ilias Tsitsimpis
        """
419 25a04cdd Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
420 2c9c147e Ilias Tsitsimpis
        if non_authentication:
421 2c9c147e Ilias Tsitsimpis
            req_body = None
422 2c9c147e Ilias Tsitsimpis
        else:
423 2c9c147e Ilias Tsitsimpis
            body = {'auth': {'token': {'id': self.token}}}
424 2c9c147e Ilias Tsitsimpis
            if tenant_name is not None:
425 2c9c147e Ilias Tsitsimpis
                body['auth']['tenantName'] = tenant_name
426 2c9c147e Ilias Tsitsimpis
            req_body = parse_request(body, self.logger)
427 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_tokens, headers=req_headers,
428 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST",
429 2c9c147e Ilias Tsitsimpis
                                  log_body=False)
430 25a04cdd Ilias Tsitsimpis
431 25a04cdd Ilias Tsitsimpis
    # ----------------------------------
432 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_QUOTAS``
433 2c9c147e Ilias Tsitsimpis
    def get_quotas(self):
434 fd420756 Ilias Tsitsimpis
        """Get user's quotas
435 fd420756 Ilias Tsitsimpis

436 fd420756 Ilias Tsitsimpis
        In case of success return a dict of dicts with user's current quotas.
437 fd420756 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException
438 fd420756 Ilias Tsitsimpis

439 fd420756 Ilias Tsitsimpis
        """
440 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_quotas)
441 baeb2ba5 Ilias Tsitsimpis
442 fd420756 Ilias Tsitsimpis
    # ----------------------------------
443 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_SERVICE_QUOTAS``
444 2c9c147e Ilias Tsitsimpis
    def service_get_quotas(self, user=None):
445 5b33b8e5 Giorgos Korfiatis
        """Get all quotas for resources associated with the service
446 5b33b8e5 Giorgos Korfiatis

447 5b33b8e5 Giorgos Korfiatis
        Keyword arguments:
448 db9f7a2b Giorgos Korfiatis
        user    -- optionally, the uuid of a specific user
449 5b33b8e5 Giorgos Korfiatis

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

454 5b33b8e5 Giorgos Korfiatis
        """
455 2c9c147e Ilias Tsitsimpis
        query = self.api_service_quotas
456 db9f7a2b Giorgos Korfiatis
        if user is not None:
457 db9f7a2b Giorgos Korfiatis
            query += "?user=" + user
458 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(query)
459 5b33b8e5 Giorgos Korfiatis
460 5b33b8e5 Giorgos Korfiatis
    # ----------------------------------
461 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``
462 2c9c147e Ilias Tsitsimpis
    def issue_commission(self, request):
463 fd420756 Ilias Tsitsimpis
        """Issue a commission
464 fd420756 Ilias Tsitsimpis

465 fd420756 Ilias Tsitsimpis
        Keyword arguments:
466 fd420756 Ilias Tsitsimpis
        request -- commision request (dict)
467 fd420756 Ilias Tsitsimpis

468 fd420756 Ilias Tsitsimpis
        In case of success return commission's id (int).
469 fd420756 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
470 fd420756 Ilias Tsitsimpis

471 fd420756 Ilias Tsitsimpis
        """
472 fd420756 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
473 19198628 Ilias Tsitsimpis
        req_body = parse_request(request, self.logger)
474 fd420756 Ilias Tsitsimpis
        try:
475 2c9c147e Ilias Tsitsimpis
            response = self._call_astakos(self.api_commissions,
476 2c9c147e Ilias Tsitsimpis
                                          headers=req_headers,
477 2c9c147e Ilias Tsitsimpis
                                          body=req_body,
478 2c9c147e Ilias Tsitsimpis
                                          method="POST")
479 fd420756 Ilias Tsitsimpis
        except AstakosClientException as err:
480 fd420756 Ilias Tsitsimpis
            if err.status == 413:
481 fd420756 Ilias Tsitsimpis
                raise QuotaLimit(err.message, err.details)
482 fd420756 Ilias Tsitsimpis
            else:
483 fd420756 Ilias Tsitsimpis
                raise
484 fd420756 Ilias Tsitsimpis
485 fd420756 Ilias Tsitsimpis
        if "serial" in response:
486 fd420756 Ilias Tsitsimpis
            return response['serial']
487 fd420756 Ilias Tsitsimpis
        else:
488 2c9c147e Ilias Tsitsimpis
            msg = "issue_commission_core request returned %s. " + \
489 2c9c147e Ilias Tsitsimpis
                  "No serial found" % response
490 2c9c147e Ilias Tsitsimpis
            self.logger.error(msg)
491 2c9c147e Ilias Tsitsimpis
            raise AstakosClientException(msg)
492 fd420756 Ilias Tsitsimpis
493 2c9c147e Ilias Tsitsimpis
    def issue_one_commission(self, holder, source, provisions,
494 3a1bed03 Giorgos Korfiatis
                             name="", force=False, auto_accept=False):
495 12eab714 Ilias Tsitsimpis
        """Issue one commission (with specific holder and source)
496 12eab714 Ilias Tsitsimpis

497 12eab714 Ilias Tsitsimpis
        keyword arguments:
498 12eab714 Ilias Tsitsimpis
        holder      -- user's id (string)
499 12eab714 Ilias Tsitsimpis
        source      -- commission's source (ex system) (string)
500 8b68fa76 Giorgos Korfiatis
        provisions  -- resources with their quantity (dict from string to int)
501 3a1bed03 Giorgos Korfiatis
        name        -- description of the commission (string)
502 12eab714 Ilias Tsitsimpis
        force       -- force this commission (boolean)
503 12eab714 Ilias Tsitsimpis
        auto_accept -- auto accept this commission (boolean)
504 12eab714 Ilias Tsitsimpis

505 12eab714 Ilias Tsitsimpis
        In case of success return commission's id (int).
506 12eab714 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
507 12eab714 Ilias Tsitsimpis
        (See also issue_commission)
508 12eab714 Ilias Tsitsimpis

509 12eab714 Ilias Tsitsimpis
        """
510 d5f086f2 Ilias Tsitsimpis
        check_input("issue_one_commission", self.logger,
511 d5f086f2 Ilias Tsitsimpis
                    holder=holder, source=source,
512 d5f086f2 Ilias Tsitsimpis
                    provisions=provisions)
513 12eab714 Ilias Tsitsimpis
514 12eab714 Ilias Tsitsimpis
        request = {}
515 12eab714 Ilias Tsitsimpis
        request["force"] = force
516 12eab714 Ilias Tsitsimpis
        request["auto_accept"] = auto_accept
517 3a1bed03 Giorgos Korfiatis
        request["name"] = name
518 12eab714 Ilias Tsitsimpis
        try:
519 12eab714 Ilias Tsitsimpis
            request["provisions"] = []
520 567f49a2 Giorgos Korfiatis
            for resource, quantity in provisions.iteritems():
521 2c9c147e Ilias Tsitsimpis
                prov = {"holder": holder, "source": source,
522 2c9c147e Ilias Tsitsimpis
                        "resource": resource, "quantity": quantity}
523 2c9c147e Ilias Tsitsimpis
                request["provisions"].append(prov)
524 12eab714 Ilias Tsitsimpis
        except Exception as err:
525 12eab714 Ilias Tsitsimpis
            self.logger.error(str(err))
526 12eab714 Ilias Tsitsimpis
            raise BadValue(str(err))
527 12eab714 Ilias Tsitsimpis
528 2c9c147e Ilias Tsitsimpis
        return self.issue_commission(request)
529 12eab714 Ilias Tsitsimpis
530 7a0180ef Ilias Tsitsimpis
    # ----------------------------------
531 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``
532 2c9c147e Ilias Tsitsimpis
    def get_pending_commissions(self):
533 7a0180ef Ilias Tsitsimpis
        """Get Pending Commissions
534 7a0180ef Ilias Tsitsimpis

535 7a0180ef Ilias Tsitsimpis
        In case of success return a list of pending commissions' ids
536 7a0180ef Ilias Tsitsimpis
        (list of integers)
537 7a0180ef Ilias Tsitsimpis

538 7a0180ef Ilias Tsitsimpis
        """
539 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_commissions)
540 7a0180ef Ilias Tsitsimpis
541 994f37b6 Ilias Tsitsimpis
    # ----------------------------------
542 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``/<serial>
543 2c9c147e Ilias Tsitsimpis
    def get_commission_info(self, serial):
544 994f37b6 Ilias Tsitsimpis
        """Get Description of a Commission
545 994f37b6 Ilias Tsitsimpis

546 994f37b6 Ilias Tsitsimpis
        Keyword arguments:
547 994f37b6 Ilias Tsitsimpis
        serial  -- commission's id (int)
548 994f37b6 Ilias Tsitsimpis

549 994f37b6 Ilias Tsitsimpis
        In case of success return a dict of dicts containing
550 5c418e94 Ilias Tsitsimpis
        informations (details) about the requested commission
551 994f37b6 Ilias Tsitsimpis

552 994f37b6 Ilias Tsitsimpis
        """
553 10797183 Ilias Tsitsimpis
        check_input("get_commission_info", self.logger, serial=serial)
554 994f37b6 Ilias Tsitsimpis
555 2c9c147e Ilias Tsitsimpis
        path = self.api_commissions.rstrip('/') + "/" + str(serial)
556 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
557 994f37b6 Ilias Tsitsimpis
558 805e294c Ilias Tsitsimpis
    # ----------------------------------
559 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``/<serial>/action"
560 2c9c147e Ilias Tsitsimpis
    def commission_action(self, serial, action):
561 b5008ef0 Ilias Tsitsimpis
        """Perform a commission action
562 805e294c Ilias Tsitsimpis

563 805e294c Ilias Tsitsimpis
        Keyword arguments:
564 805e294c Ilias Tsitsimpis
        serial  -- commission's id (int)
565 805e294c Ilias Tsitsimpis
        action  -- action to perform, currently accept/reject (string)
566 805e294c Ilias Tsitsimpis

567 805e294c Ilias Tsitsimpis
        In case of success return nothing.
568 805e294c Ilias Tsitsimpis

569 805e294c Ilias Tsitsimpis
        """
570 10797183 Ilias Tsitsimpis
        check_input("commission_action", self.logger,
571 10797183 Ilias Tsitsimpis
                    serial=serial, action=action)
572 805e294c Ilias Tsitsimpis
573 2c9c147e Ilias Tsitsimpis
        path = self.api_commissions.rstrip('/') + "/" + str(serial) + "/action"
574 805e294c Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
575 19198628 Ilias Tsitsimpis
        req_body = parse_request({str(action): ""}, self.logger)
576 2c9c147e Ilias Tsitsimpis
        self._call_astakos(path, headers=req_headers,
577 2c9c147e Ilias Tsitsimpis
                           body=req_body, method="POST")
578 805e294c Ilias Tsitsimpis
579 2c9c147e Ilias Tsitsimpis
    def accept_commission(self, serial):
580 b5008ef0 Ilias Tsitsimpis
        """Accept a commission (see commission_action)"""
581 2c9c147e Ilias Tsitsimpis
        self.commission_action(serial, "accept")
582 805e294c Ilias Tsitsimpis
583 2c9c147e Ilias Tsitsimpis
    def reject_commission(self, serial):
584 b5008ef0 Ilias Tsitsimpis
        """Reject a commission (see commission_action)"""
585 2c9c147e Ilias Tsitsimpis
        self.commission_action(serial, "reject")
586 805e294c Ilias Tsitsimpis
587 81875157 Ilias Tsitsimpis
    # ----------------------------------
588 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS_ACTION``
589 2c9c147e Ilias Tsitsimpis
    def resolve_commissions(self, accept_serials, reject_serials):
590 81875157 Ilias Tsitsimpis
        """Resolve multiple commissions at once
591 81875157 Ilias Tsitsimpis

592 81875157 Ilias Tsitsimpis
        Keyword arguments:
593 81875157 Ilias Tsitsimpis
        accept_serials  -- commissions to accept (list of ints)
594 81875157 Ilias Tsitsimpis
        reject_serials  -- commissions to reject (list of ints)
595 81875157 Ilias Tsitsimpis

596 81875157 Ilias Tsitsimpis
        In case of success return a dict of dicts describing which
597 81875157 Ilias Tsitsimpis
        commissions accepted, which rejected and which failed to
598 81875157 Ilias Tsitsimpis
        resolved.
599 81875157 Ilias Tsitsimpis

600 81875157 Ilias Tsitsimpis
        """
601 d5f086f2 Ilias Tsitsimpis
        check_input("resolve_commissions", self.logger,
602 d5f086f2 Ilias Tsitsimpis
                    accept_serials=accept_serials,
603 d5f086f2 Ilias Tsitsimpis
                    reject_serials=reject_serials)
604 81875157 Ilias Tsitsimpis
605 81875157 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
606 81875157 Ilias Tsitsimpis
        req_body = parse_request({"accept": accept_serials,
607 81875157 Ilias Tsitsimpis
                                  "reject": reject_serials},
608 81875157 Ilias Tsitsimpis
                                 self.logger)
609 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_commissions_action,
610 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body,
611 2c9c147e Ilias Tsitsimpis
                                  method="POST")
612 81875157 Ilias Tsitsimpis
613 47bb45c0 Giorgos Korfiatis
    # ----------------------------
614 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_PROJECTS``
615 2c9c147e Ilias Tsitsimpis
    def get_projects(self, name=None, state=None, owner=None):
616 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible projects
617 47bb45c0 Giorgos Korfiatis

618 47bb45c0 Giorgos Korfiatis
        Arguments:
619 47bb45c0 Giorgos Korfiatis
        name  -- filter by name (optional)
620 47bb45c0 Giorgos Korfiatis
        state -- filter by state (optional)
621 47bb45c0 Giorgos Korfiatis
        owner -- filter by owner (optional)
622 47bb45c0 Giorgos Korfiatis

623 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of project descriptions.
624 47bb45c0 Giorgos Korfiatis
        """
625 47bb45c0 Giorgos Korfiatis
        filters = {}
626 47bb45c0 Giorgos Korfiatis
        if name is not None:
627 47bb45c0 Giorgos Korfiatis
            filters["name"] = name
628 47bb45c0 Giorgos Korfiatis
        if state is not None:
629 47bb45c0 Giorgos Korfiatis
            filters["state"] = state
630 47bb45c0 Giorgos Korfiatis
        if owner is not None:
631 47bb45c0 Giorgos Korfiatis
            filters["owner"] = owner
632 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
633 47bb45c0 Giorgos Korfiatis
        req_body = (parse_request({"filter": filters}, self.logger)
634 47bb45c0 Giorgos Korfiatis
                    if filters else None)
635 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_projects,
636 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body)
637 47bb45c0 Giorgos Korfiatis
638 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
639 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_PROJECTS``/<project_id>
640 2c9c147e Ilias Tsitsimpis
    def get_project(self, project_id):
641 47bb45c0 Giorgos Korfiatis
        """Retrieve project description, if accessible
642 47bb45c0 Giorgos Korfiatis

643 47bb45c0 Giorgos Korfiatis
        Arguments:
644 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
645 47bb45c0 Giorgos Korfiatis

646 47bb45c0 Giorgos Korfiatis
        In case of success, return project description.
647 47bb45c0 Giorgos Korfiatis
        """
648 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
649 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
650 47bb45c0 Giorgos Korfiatis
651 47bb45c0 Giorgos Korfiatis
    # -----------------------------
652 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``
653 2c9c147e Ilias Tsitsimpis
    def create_project(self, specs):
654 47bb45c0 Giorgos Korfiatis
        """Submit application to create a new project
655 47bb45c0 Giorgos Korfiatis

656 47bb45c0 Giorgos Korfiatis
        Arguments:
657 47bb45c0 Giorgos Korfiatis
        specs -- dict describing a project
658 47bb45c0 Giorgos Korfiatis

659 47bb45c0 Giorgos Korfiatis
        In case of success, return project and application identifiers.
660 47bb45c0 Giorgos Korfiatis
        """
661 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
662 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(specs, self.logger)
663 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_projects,
664 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body,
665 2c9c147e Ilias Tsitsimpis
                                  method="POST")
666 47bb45c0 Giorgos Korfiatis
667 47bb45c0 Giorgos Korfiatis
    # ------------------------------------------
668 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``/<project_id>
669 2c9c147e Ilias Tsitsimpis
    def modify_project(self, project_id, specs):
670 47bb45c0 Giorgos Korfiatis
        """Submit application to modify an existing project
671 47bb45c0 Giorgos Korfiatis

672 47bb45c0 Giorgos Korfiatis
        Arguments:
673 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
674 47bb45c0 Giorgos Korfiatis
        specs      -- dict describing a project
675 47bb45c0 Giorgos Korfiatis

676 47bb45c0 Giorgos Korfiatis
        In case of success, return project and application identifiers.
677 47bb45c0 Giorgos Korfiatis
        """
678 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
679 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
680 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(specs, self.logger)
681 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
682 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
683 47bb45c0 Giorgos Korfiatis
684 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
685 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``/<project_id>/action
686 2c9c147e Ilias Tsitsimpis
    def project_action(self, project_id, action, reason=""):
687 47bb45c0 Giorgos Korfiatis
        """Perform action on a project
688 47bb45c0 Giorgos Korfiatis

689 47bb45c0 Giorgos Korfiatis
        Arguments:
690 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
691 47bb45c0 Giorgos Korfiatis
        action     -- action to perform, one of "suspend", "unsuspend",
692 47bb45c0 Giorgos Korfiatis
                      "terminate", "reinstate"
693 47bb45c0 Giorgos Korfiatis
        reason     -- reason of performing the action
694 47bb45c0 Giorgos Korfiatis

695 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
696 47bb45c0 Giorgos Korfiatis
        """
697 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
698 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
699 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
700 47bb45c0 Giorgos Korfiatis
        req_body = parse_request({action: reason}, self.logger)
701 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
702 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
703 47bb45c0 Giorgos Korfiatis
704 47bb45c0 Giorgos Korfiatis
    # --------------------------------
705 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_APPLICATIONS``
706 2c9c147e Ilias Tsitsimpis
    def get_applications(self, project=None):
707 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible applications
708 47bb45c0 Giorgos Korfiatis

709 47bb45c0 Giorgos Korfiatis
        Arguments:
710 47bb45c0 Giorgos Korfiatis
        project -- filter by project (optional)
711 47bb45c0 Giorgos Korfiatis

712 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of application descriptions.
713 47bb45c0 Giorgos Korfiatis
        """
714 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
715 47bb45c0 Giorgos Korfiatis
        body = {"project": project} if project is not None else None
716 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger) if body else None
717 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_applications,
718 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body)
719 47bb45c0 Giorgos Korfiatis
720 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
721 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_APPLICATIONS``/<app_id>
722 2c9c147e Ilias Tsitsimpis
    def get_application(self, app_id):
723 47bb45c0 Giorgos Korfiatis
        """Retrieve application description, if accessible
724 47bb45c0 Giorgos Korfiatis

725 47bb45c0 Giorgos Korfiatis
        Arguments:
726 47bb45c0 Giorgos Korfiatis
        app_id -- application identifier
727 47bb45c0 Giorgos Korfiatis

728 47bb45c0 Giorgos Korfiatis
        In case of success, return application description.
729 47bb45c0 Giorgos Korfiatis
        """
730 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_applications, str(app_id))
731 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
732 47bb45c0 Giorgos Korfiatis
733 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
734 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_APPLICATIONS``/<app_id>/action
735 2c9c147e Ilias Tsitsimpis
    def application_action(self, app_id, action, reason=""):
736 47bb45c0 Giorgos Korfiatis
        """Perform action on an application
737 47bb45c0 Giorgos Korfiatis

738 47bb45c0 Giorgos Korfiatis
        Arguments:
739 47bb45c0 Giorgos Korfiatis
        app_id -- application identifier
740 47bb45c0 Giorgos Korfiatis
        action -- action to perform, one of "approve", "deny",
741 47bb45c0 Giorgos Korfiatis
                  "dismiss", "cancel"
742 47bb45c0 Giorgos Korfiatis
        reason -- reason of performing the action
743 47bb45c0 Giorgos Korfiatis

744 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
745 47bb45c0 Giorgos Korfiatis
        """
746 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_applications, str(app_id))
747 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
748 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
749 47bb45c0 Giorgos Korfiatis
        req_body = parse_request({action: reason}, self.logger)
750 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
751 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
752 47bb45c0 Giorgos Korfiatis
753 47bb45c0 Giorgos Korfiatis
    # -------------------------------
754 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_MEMBERSHIPS``
755 2c9c147e Ilias Tsitsimpis
    def get_memberships(self, project=None):
756 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible memberships
757 47bb45c0 Giorgos Korfiatis

758 47bb45c0 Giorgos Korfiatis
        Arguments:
759 47bb45c0 Giorgos Korfiatis
        project -- filter by project (optional)
760 47bb45c0 Giorgos Korfiatis

761 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of membership descriptions.
762 47bb45c0 Giorgos Korfiatis
        """
763 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
764 47bb45c0 Giorgos Korfiatis
        body = {"project": project} if project is not None else None
765 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger) if body else None
766 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships,
767 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body)
768 47bb45c0 Giorgos Korfiatis
769 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
770 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_MEMBERSHIPS``/<memb_id>
771 2c9c147e Ilias Tsitsimpis
    def get_membership(self, memb_id):
772 47bb45c0 Giorgos Korfiatis
        """Retrieve membership description, if accessible
773 47bb45c0 Giorgos Korfiatis

774 47bb45c0 Giorgos Korfiatis
        Arguments:
775 47bb45c0 Giorgos Korfiatis
        memb_id -- membership identifier
776 47bb45c0 Giorgos Korfiatis

777 47bb45c0 Giorgos Korfiatis
        In case of success, return membership description.
778 47bb45c0 Giorgos Korfiatis
        """
779 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_memberships, str(memb_id))
780 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
781 47bb45c0 Giorgos Korfiatis
782 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
783 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``/<memb_id>/action
784 2c9c147e Ilias Tsitsimpis
    def membership_action(self, memb_id, action, reason=""):
785 47bb45c0 Giorgos Korfiatis
        """Perform action on a membership
786 47bb45c0 Giorgos Korfiatis

787 47bb45c0 Giorgos Korfiatis
        Arguments:
788 47bb45c0 Giorgos Korfiatis
        memb_id -- membership identifier
789 47bb45c0 Giorgos Korfiatis
        action  -- action to perform, one of "leave", "cancel", "accept",
790 47bb45c0 Giorgos Korfiatis
                   "reject", "remove"
791 47bb45c0 Giorgos Korfiatis
        reason  -- reason of performing the action
792 47bb45c0 Giorgos Korfiatis

793 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
794 47bb45c0 Giorgos Korfiatis
        """
795 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_memberships, str(memb_id))
796 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
797 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
798 47bb45c0 Giorgos Korfiatis
        req_body = parse_request({action: reason}, self.logger)
799 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
800 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
801 47bb45c0 Giorgos Korfiatis
802 47bb45c0 Giorgos Korfiatis
    # --------------------------------
803 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``
804 2c9c147e Ilias Tsitsimpis
    def join_project(self, project_id):
805 47bb45c0 Giorgos Korfiatis
        """Join a project
806 47bb45c0 Giorgos Korfiatis

807 47bb45c0 Giorgos Korfiatis
        Arguments:
808 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
809 47bb45c0 Giorgos Korfiatis

810 47bb45c0 Giorgos Korfiatis
        In case of success, return membership identifier.
811 47bb45c0 Giorgos Korfiatis
        """
812 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
813 47bb45c0 Giorgos Korfiatis
        body = {"join": {"project": project_id}}
814 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger)
815 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships, headers=req_headers,
816 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
817 47bb45c0 Giorgos Korfiatis
818 47bb45c0 Giorgos Korfiatis
    # --------------------------------
819 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``
820 2c9c147e Ilias Tsitsimpis
    def enroll_member(self, project_id, email):
821 47bb45c0 Giorgos Korfiatis
        """Enroll a user in a project
822 47bb45c0 Giorgos Korfiatis

823 47bb45c0 Giorgos Korfiatis
        Arguments:
824 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
825 47bb45c0 Giorgos Korfiatis
        email      -- user identified by email
826 47bb45c0 Giorgos Korfiatis

827 47bb45c0 Giorgos Korfiatis
        In case of success, return membership identifier.
828 47bb45c0 Giorgos Korfiatis
        """
829 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
830 47bb45c0 Giorgos Korfiatis
        body = {"enroll": {"project": project_id, "user": email}}
831 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger)
832 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships, headers=req_headers,
833 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
834 2c9c147e Ilias Tsitsimpis
835 2c9c147e Ilias Tsitsimpis
836 2c9c147e Ilias Tsitsimpis
# --------------------------------------------------------------------
837 2c9c147e Ilias Tsitsimpis
# parse endpoints
838 2c9c147e Ilias Tsitsimpis
def parse_endpoints(endpoints, ep_name=None, ep_type=None,
839 2c9c147e Ilias Tsitsimpis
                    ep_region=None, ep_version_id=None):
840 2c9c147e Ilias Tsitsimpis
    """Parse endpoints server response and extract the ones needed
841 2c9c147e Ilias Tsitsimpis

842 2c9c147e Ilias Tsitsimpis
    Keyword arguments:
843 2c9c147e Ilias Tsitsimpis
    endpoints     -- the endpoints (json response from get_endpoints)
844 2c9c147e Ilias Tsitsimpis
    ep_name       -- return only endpoints with this name (optional)
845 2c9c147e Ilias Tsitsimpis
    ep_type       -- return only endpoints with this type (optional)
846 2c9c147e Ilias Tsitsimpis
    ep_region     -- return only endpoints with this region (optional)
847 2c9c147e Ilias Tsitsimpis
    ep_version_id -- return only endpoints with this versionId (optional)
848 2c9c147e Ilias Tsitsimpis

849 9c3cbd0d Ilias Tsitsimpis
    In case one of the `name', `type', `region', `version_id' parameters
850 2c9c147e Ilias Tsitsimpis
    is given, return only the endpoints that match all of these criteria.
851 2c9c147e Ilias Tsitsimpis
    If no match is found then raise NoEndpoints exception.
852 2c9c147e Ilias Tsitsimpis

853 2c9c147e Ilias Tsitsimpis
    """
854 2c9c147e Ilias Tsitsimpis
    try:
855 2c9c147e Ilias Tsitsimpis
        catalog = endpoints['access']['serviceCatalog']
856 2c9c147e Ilias Tsitsimpis
        if ep_name is not None:
857 2c9c147e Ilias Tsitsimpis
            catalog = \
858 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['name'] == ep_name]
859 2c9c147e Ilias Tsitsimpis
        if ep_type is not None:
860 2c9c147e Ilias Tsitsimpis
            catalog = \
861 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['type'] == ep_type]
862 2c9c147e Ilias Tsitsimpis
        if ep_region is not None:
863 2c9c147e Ilias Tsitsimpis
            for c in catalog:
864 2c9c147e Ilias Tsitsimpis
                c['endpoints'] = [e for e in c['endpoints']
865 2c9c147e Ilias Tsitsimpis
                                  if e['region'] == ep_region]
866 2c9c147e Ilias Tsitsimpis
            # Remove catalog entries with no endpoints
867 2c9c147e Ilias Tsitsimpis
            catalog = \
868 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['endpoints']]
869 2c9c147e Ilias Tsitsimpis
        if ep_version_id is not None:
870 2c9c147e Ilias Tsitsimpis
            for c in catalog:
871 2c9c147e Ilias Tsitsimpis
                c['endpoints'] = [e for e in c['endpoints']
872 2c9c147e Ilias Tsitsimpis
                                  if e['versionId'] == ep_version_id]
873 2c9c147e Ilias Tsitsimpis
            # Remove catalog entries with no endpoints
874 2c9c147e Ilias Tsitsimpis
            catalog = \
875 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['endpoints']]
876 2c9c147e Ilias Tsitsimpis
877 2c9c147e Ilias Tsitsimpis
        if not catalog:
878 2c9c147e Ilias Tsitsimpis
            raise NoEndpoints(ep_name, ep_type,
879 2c9c147e Ilias Tsitsimpis
                              ep_region, ep_version_id)
880 2c9c147e Ilias Tsitsimpis
        else:
881 2c9c147e Ilias Tsitsimpis
            return catalog
882 2c9c147e Ilias Tsitsimpis
    except KeyError:
883 2c9c147e Ilias Tsitsimpis
        raise NoEndpoints()
884 2c9c147e Ilias Tsitsimpis
885 f54cf5e4 Ilias Tsitsimpis
886 f54cf5e4 Ilias Tsitsimpis
# --------------------------------------------------------------------
887 cbc0b438 Ilias Tsitsimpis
# Private functions
888 f93cc364 Ilias Tsitsimpis
# We want _doRequest to be a distinct function
889 f93cc364 Ilias Tsitsimpis
# so that we can replace it during unit tests.
890 794c94e6 Ilias Tsitsimpis
def _do_request(conn, method, url, **kwargs):
891 f8388a90 Ilias Tsitsimpis
    """The actual request. This function can easily be mocked"""
892 f8388a90 Ilias Tsitsimpis
    conn.request(method, url, **kwargs)
893 f8388a90 Ilias Tsitsimpis
    response = conn.getresponse()
894 f8388a90 Ilias Tsitsimpis
    length = response.getheader('content-length', None)
895 f8388a90 Ilias Tsitsimpis
    data = response.read(length)
896 f8388a90 Ilias Tsitsimpis
    status = int(response.status)
897 21190887 Ilias Tsitsimpis
    message = response.reason
898 21190887 Ilias Tsitsimpis
    return (message, data, status)