Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / __init__.py @ d9fca80b

History | View | Annotate | Download (40.3 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 d2104099 Sofia Papagiannaki
from base64 import b64encode
43 8f2d7ede Ilias Tsitsimpis
from copy import copy
44 cbc0b438 Ilias Tsitsimpis
45 cbc0b438 Ilias Tsitsimpis
import simplejson
46 10797183 Ilias Tsitsimpis
from astakosclient.utils import \
47 2c9c147e Ilias Tsitsimpis
    retry_dec, scheme_to_class, parse_request, check_input, join_urls
48 f93cc364 Ilias Tsitsimpis
from astakosclient.errors import \
49 2377e7c2 Ilias Tsitsimpis
    AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden, \
50 2c9c147e Ilias Tsitsimpis
    NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse, NoEndpoints
51 7b5a37fd Ilias Tsitsimpis
52 7b5a37fd Ilias Tsitsimpis
53 7b5a37fd Ilias Tsitsimpis
# --------------------------------------------------------------------
54 4490d7b5 Ilias Tsitsimpis
# Astakos Client Class
55 bc5032a4 Ilias Tsitsimpis
56 794c94e6 Ilias Tsitsimpis
def get_token_from_cookie(request, cookie_name):
57 bc5032a4 Ilias Tsitsimpis
    """Extract token from the cookie name provided
58 bc5032a4 Ilias Tsitsimpis

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

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

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

93 4490d7b5 Ilias Tsitsimpis
        """
94 2c9c147e Ilias Tsitsimpis
95 2c9c147e Ilias Tsitsimpis
        # Get logger
96 4490d7b5 Ilias Tsitsimpis
        if logger is None:
97 4490d7b5 Ilias Tsitsimpis
            logger = logging.getLogger("astakosclient")
98 e6a61e6a Ilias Tsitsimpis
            logger.setLevel(logging.INFO)
99 2c9c147e Ilias Tsitsimpis
        logger.debug("Intialize AstakosClient: auth_url = %s, "
100 2c9c147e Ilias Tsitsimpis
                     "use_pool = %s, pool_size = %s",
101 2c9c147e Ilias Tsitsimpis
                     auth_url, use_pool, pool_size)
102 4490d7b5 Ilias Tsitsimpis
103 2c9c147e Ilias Tsitsimpis
        # Check that token and auth_url (mandatory options) are given
104 2c9c147e Ilias Tsitsimpis
        check_input("__init__", logger, token=token, auth_url=auth_url)
105 4490d7b5 Ilias Tsitsimpis
106 2c9c147e Ilias Tsitsimpis
        # Initialize connection class
107 2c9c147e Ilias Tsitsimpis
        parsed_auth_url = urlparse.urlparse(auth_url)
108 2c9c147e Ilias Tsitsimpis
        conn_class = \
109 2c9c147e Ilias Tsitsimpis
            scheme_to_class(parsed_auth_url.scheme, use_pool, pool_size)
110 e169a337 Ilias Tsitsimpis
        if conn_class is None:
111 2c9c147e Ilias Tsitsimpis
            msg = "Unsupported scheme: %s" % parsed_auth_url.scheme
112 2c9c147e Ilias Tsitsimpis
            logger.error(msg)
113 2c9c147e Ilias Tsitsimpis
            raise BadValue(msg)
114 4490d7b5 Ilias Tsitsimpis
115 2c9c147e Ilias Tsitsimpis
        # Save astakos base url, logger, connection class etc in our class
116 949baf4d Ilias Tsitsimpis
        self.retry = retry
117 4490d7b5 Ilias Tsitsimpis
        self.logger = logger
118 2c9c147e Ilias Tsitsimpis
        self.token = token
119 2c9c147e Ilias Tsitsimpis
        self.astakos_base_url = parsed_auth_url.netloc
120 2c9c147e Ilias Tsitsimpis
        self.scheme = parsed_auth_url.scheme
121 e169a337 Ilias Tsitsimpis
        self.conn_class = conn_class
122 4490d7b5 Ilias Tsitsimpis
123 2c9c147e Ilias Tsitsimpis
        # Initialize astakos api prefixes
124 2c9c147e Ilias Tsitsimpis
        # API urls under auth_url
125 2c9c147e Ilias Tsitsimpis
        self.auth_prefix = parsed_auth_url.path
126 2c9c147e Ilias Tsitsimpis
        self.api_tokens = join_urls(self.auth_prefix, "tokens")
127 2c9c147e Ilias Tsitsimpis
128 6476ceb7 Ilias Tsitsimpis
    def _fill_endpoints(self, endpoints, extra=False):
129 6476ceb7 Ilias Tsitsimpis
        """Fill the endpoints for our AstakosClient
130 6476ceb7 Ilias Tsitsimpis

131 6476ceb7 Ilias Tsitsimpis
        This will be done once (lazily) and the endpoints will be there
132 6476ceb7 Ilias Tsitsimpis
        to be used afterwards.
133 6476ceb7 Ilias Tsitsimpis
        The `extra' parameter is there for compatibility reasons. We are going
134 6476ceb7 Ilias Tsitsimpis
        to fill the oauth2 endpoint only if we need it. This way we are keeping
135 6476ceb7 Ilias Tsitsimpis
        astakosclient compatible with older Astakos version.
136 6476ceb7 Ilias Tsitsimpis

137 6476ceb7 Ilias Tsitsimpis
        """
138 45c0bcf8 Giorgos Korfiatis
        astakos_service_catalog = parse_endpoints(
139 2c9c147e Ilias Tsitsimpis
            endpoints, ep_name="astakos_account", ep_version_id="v1.0")
140 45c0bcf8 Giorgos Korfiatis
        self._account_url = \
141 45c0bcf8 Giorgos Korfiatis
            astakos_service_catalog[0]['endpoints'][0]['publicURL']
142 45c0bcf8 Giorgos Korfiatis
        parsed_account_url = urlparse.urlparse(self._account_url)
143 45c0bcf8 Giorgos Korfiatis
144 45c0bcf8 Giorgos Korfiatis
        self._account_prefix = parsed_account_url.path
145 45c0bcf8 Giorgos Korfiatis
        self.logger.debug("Got account_prefix \"%s\"" % self._account_prefix)
146 45c0bcf8 Giorgos Korfiatis
147 45c0bcf8 Giorgos Korfiatis
        self._ui_url = \
148 45c0bcf8 Giorgos Korfiatis
            astakos_service_catalog[0]['endpoints'][0]['SNF:uiURL']
149 45c0bcf8 Giorgos Korfiatis
        parsed_ui_url = urlparse.urlparse(self._ui_url)
150 45c0bcf8 Giorgos Korfiatis
151 45c0bcf8 Giorgos Korfiatis
        self._ui_prefix = parsed_ui_url.path
152 45c0bcf8 Giorgos Korfiatis
        self.logger.debug("Got ui_prefix \"%s\"" % self._ui_prefix)
153 45c0bcf8 Giorgos Korfiatis
154 6476ceb7 Ilias Tsitsimpis
        if extra:
155 6476ceb7 Ilias Tsitsimpis
            oauth2_service_catalog = \
156 6476ceb7 Ilias Tsitsimpis
                parse_endpoints(endpoints, ep_name="astakos_oauth2")
157 6476ceb7 Ilias Tsitsimpis
            self._oauth2_url = \
158 6476ceb7 Ilias Tsitsimpis
                oauth2_service_catalog[0]['endpoints'][0]['publicURL']
159 6476ceb7 Ilias Tsitsimpis
            parsed_oauth2_url = urlparse.urlparse(self._oauth2_url)
160 6476ceb7 Ilias Tsitsimpis
            self._oauth2_prefix = parsed_oauth2_url.path
161 d2104099 Sofia Papagiannaki
162 6476ceb7 Ilias Tsitsimpis
    def _get_value(self, s, extra=False):
163 45c0bcf8 Giorgos Korfiatis
        assert s in ['_account_url', '_account_prefix',
164 d2104099 Sofia Papagiannaki
                     '_ui_url', '_ui_prefix',
165 fe7d0186 Sofia Papagiannaki
                     '_oauth2_url', '_oauth2_prefix']
166 45c0bcf8 Giorgos Korfiatis
        try:
167 45c0bcf8 Giorgos Korfiatis
            return getattr(self, s)
168 45c0bcf8 Giorgos Korfiatis
        except AttributeError:
169 6476ceb7 Ilias Tsitsimpis
            self.get_endpoints(extra=extra)
170 45c0bcf8 Giorgos Korfiatis
            return getattr(self, s)
171 45c0bcf8 Giorgos Korfiatis
172 45c0bcf8 Giorgos Korfiatis
    @property
173 45c0bcf8 Giorgos Korfiatis
    def account_url(self):
174 45c0bcf8 Giorgos Korfiatis
        return self._get_value('_account_url')
175 45c0bcf8 Giorgos Korfiatis
176 45c0bcf8 Giorgos Korfiatis
    @property
177 45c0bcf8 Giorgos Korfiatis
    def account_prefix(self):
178 45c0bcf8 Giorgos Korfiatis
        return self._get_value('_account_prefix')
179 45c0bcf8 Giorgos Korfiatis
180 45c0bcf8 Giorgos Korfiatis
    @property
181 45c0bcf8 Giorgos Korfiatis
    def ui_url(self):
182 45c0bcf8 Giorgos Korfiatis
        return self._get_value('_ui_url')
183 45c0bcf8 Giorgos Korfiatis
184 45c0bcf8 Giorgos Korfiatis
    @property
185 45c0bcf8 Giorgos Korfiatis
    def ui_prefix(self):
186 45c0bcf8 Giorgos Korfiatis
        return self._get_value('_ui_prefix')
187 45c0bcf8 Giorgos Korfiatis
188 45c0bcf8 Giorgos Korfiatis
    @property
189 fe7d0186 Sofia Papagiannaki
    def oauth2_url(self):
190 6476ceb7 Ilias Tsitsimpis
        return self._get_value('_oauth2_url', extra=True)
191 d2104099 Sofia Papagiannaki
192 d2104099 Sofia Papagiannaki
    @property
193 fe7d0186 Sofia Papagiannaki
    def oauth2_prefix(self):
194 6476ceb7 Ilias Tsitsimpis
        return self._get_value('_oauth2_prefix', extra=True)
195 d2104099 Sofia Papagiannaki
196 d2104099 Sofia Papagiannaki
    @property
197 45c0bcf8 Giorgos Korfiatis
    def api_usercatalogs(self):
198 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "user_catalogs")
199 45c0bcf8 Giorgos Korfiatis
200 45c0bcf8 Giorgos Korfiatis
    @property
201 45c0bcf8 Giorgos Korfiatis
    def api_service_usercatalogs(self):
202 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "service/user_catalogs")
203 45c0bcf8 Giorgos Korfiatis
204 45c0bcf8 Giorgos Korfiatis
    @property
205 45c0bcf8 Giorgos Korfiatis
    def api_resources(self):
206 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "resources")
207 45c0bcf8 Giorgos Korfiatis
208 45c0bcf8 Giorgos Korfiatis
    @property
209 45c0bcf8 Giorgos Korfiatis
    def api_quotas(self):
210 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "quotas")
211 45c0bcf8 Giorgos Korfiatis
212 45c0bcf8 Giorgos Korfiatis
    @property
213 45c0bcf8 Giorgos Korfiatis
    def api_service_quotas(self):
214 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "service_quotas")
215 2c9c147e Ilias Tsitsimpis
216 45c0bcf8 Giorgos Korfiatis
    @property
217 dccd7fa0 Giorgos Korfiatis
    def api_service_project_quotas(self):
218 dccd7fa0 Giorgos Korfiatis
        return join_urls(self.account_prefix, "service_project_quotas")
219 dccd7fa0 Giorgos Korfiatis
220 dccd7fa0 Giorgos Korfiatis
    @property
221 45c0bcf8 Giorgos Korfiatis
    def api_commissions(self):
222 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "commissions")
223 2c9c147e Ilias Tsitsimpis
224 45c0bcf8 Giorgos Korfiatis
    @property
225 45c0bcf8 Giorgos Korfiatis
    def api_commissions_action(self):
226 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.api_commissions, "action")
227 45c0bcf8 Giorgos Korfiatis
228 45c0bcf8 Giorgos Korfiatis
    @property
229 45c0bcf8 Giorgos Korfiatis
    def api_feedback(self):
230 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "feedback")
231 45c0bcf8 Giorgos Korfiatis
232 45c0bcf8 Giorgos Korfiatis
    @property
233 45c0bcf8 Giorgos Korfiatis
    def api_projects(self):
234 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "projects")
235 45c0bcf8 Giorgos Korfiatis
236 45c0bcf8 Giorgos Korfiatis
    @property
237 45c0bcf8 Giorgos Korfiatis
    def api_memberships(self):
238 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.api_projects, "memberships")
239 45c0bcf8 Giorgos Korfiatis
240 45c0bcf8 Giorgos Korfiatis
    @property
241 45c0bcf8 Giorgos Korfiatis
    def api_getservices(self):
242 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.ui_prefix, "get_services")
243 2c9c147e Ilias Tsitsimpis
244 d2104099 Sofia Papagiannaki
    @property
245 fe7d0186 Sofia Papagiannaki
    def api_oauth2_auth(self):
246 fe7d0186 Sofia Papagiannaki
        return join_urls(self.oauth2_prefix, "auth")
247 d2104099 Sofia Papagiannaki
248 d2104099 Sofia Papagiannaki
    @property
249 fe7d0186 Sofia Papagiannaki
    def api_oauth2_token(self):
250 fe7d0186 Sofia Papagiannaki
        return join_urls(self.oauth2_prefix, "token")
251 d2104099 Sofia Papagiannaki
252 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
253 2c9c147e Ilias Tsitsimpis
    @retry_dec
254 2c9c147e Ilias Tsitsimpis
    def _call_astakos(self, request_path, headers=None,
255 25a04cdd Ilias Tsitsimpis
                      body=None, method="GET", log_body=True):
256 4490d7b5 Ilias Tsitsimpis
        """Make the actual call to Astakos Service"""
257 2c9c147e Ilias Tsitsimpis
        hashed_token = hashlib.sha1()
258 2c9c147e Ilias Tsitsimpis
        hashed_token.update(self.token)
259 4490d7b5 Ilias Tsitsimpis
        self.logger.debug(
260 2c9c147e Ilias Tsitsimpis
            "Make a %s request to %s, using token with hash %s, "
261 2c9c147e Ilias Tsitsimpis
            "with headers %s and body %s",
262 2c9c147e Ilias Tsitsimpis
            method, request_path, hashed_token.hexdigest(), headers,
263 2c9c147e Ilias Tsitsimpis
            body if log_body else "(not logged)")
264 4490d7b5 Ilias Tsitsimpis
265 98752f06 Ilias Tsitsimpis
        # Check Input
266 8f2d7ede Ilias Tsitsimpis
        if headers is None:
267 8f2d7ede Ilias Tsitsimpis
            headers = {}
268 8f2d7ede Ilias Tsitsimpis
        if body is None:
269 8f2d7ede Ilias Tsitsimpis
            body = {}
270 2cd636fe Stavros Sachtouris
        # Initialize log_request and log_response attributes
271 2cd636fe Stavros Sachtouris
        self.log_request = None
272 2cd636fe Stavros Sachtouris
        self.log_response = None
273 98752f06 Ilias Tsitsimpis
274 4490d7b5 Ilias Tsitsimpis
        # Build request's header and body
275 4490d7b5 Ilias Tsitsimpis
        kwargs = {}
276 8f2d7ede Ilias Tsitsimpis
        kwargs['headers'] = copy(headers)
277 2c9c147e Ilias Tsitsimpis
        kwargs['headers']['X-Auth-Token'] = self.token
278 4490d7b5 Ilias Tsitsimpis
        if body:
279 8f2d7ede Ilias Tsitsimpis
            kwargs['body'] = copy(body)
280 4490d7b5 Ilias Tsitsimpis
            kwargs['headers'].setdefault(
281 4490d7b5 Ilias Tsitsimpis
                'content-type', 'application/octet-stream')
282 4490d7b5 Ilias Tsitsimpis
        kwargs['headers'].setdefault('content-length',
283 4490d7b5 Ilias Tsitsimpis
                                     len(body) if body else 0)
284 4490d7b5 Ilias Tsitsimpis
285 4490d7b5 Ilias Tsitsimpis
        try:
286 6837f014 Ilias Tsitsimpis
            # Get the connection object
287 2c9c147e Ilias Tsitsimpis
            with self.conn_class(self.astakos_base_url) as conn:
288 2cd636fe Stavros Sachtouris
                # Log the request so other clients (like kamaki)
289 2cd636fe Stavros Sachtouris
                # can use them to produce their own log messages.
290 2cd636fe Stavros Sachtouris
                self.log_request = dict(method=method, path=request_path)
291 2cd636fe Stavros Sachtouris
                self.log_request.update(kwargs)
292 2cd636fe Stavros Sachtouris
293 6837f014 Ilias Tsitsimpis
                # Send request
294 2c9c147e Ilias Tsitsimpis
                # Used * or ** magic. pylint: disable-msg=W0142
295 21190887 Ilias Tsitsimpis
                (message, data, status) = \
296 2c9c147e Ilias Tsitsimpis
                    _do_request(conn, method, request_path, **kwargs)
297 2cd636fe Stavros Sachtouris
298 2cd636fe Stavros Sachtouris
                # Log the response so other clients (like kamaki)
299 2cd636fe Stavros Sachtouris
                # can use them to produce their own log messages.
300 2cd636fe Stavros Sachtouris
                self.log_response = dict(
301 2cd636fe Stavros Sachtouris
                    status=status, message=message, data=data)
302 4490d7b5 Ilias Tsitsimpis
        except Exception as err:
303 996061fa Ilias Tsitsimpis
            self.logger.error("Failed to send request: %s" % repr(err))
304 4490d7b5 Ilias Tsitsimpis
            raise AstakosClientException(str(err))
305 4490d7b5 Ilias Tsitsimpis
306 4490d7b5 Ilias Tsitsimpis
        # Return
307 4490d7b5 Ilias Tsitsimpis
        self.logger.debug("Request returned with status %s" % status)
308 8fe6475a Ilias Tsitsimpis
        if status == 400:
309 21190887 Ilias Tsitsimpis
            raise BadRequest(message, data)
310 be284f6a Christos Stavrakakis
        elif status == 401:
311 21190887 Ilias Tsitsimpis
            raise Unauthorized(message, data)
312 be284f6a Christos Stavrakakis
        elif status == 403:
313 21190887 Ilias Tsitsimpis
            raise Forbidden(message, data)
314 be284f6a Christos Stavrakakis
        elif status == 404:
315 21190887 Ilias Tsitsimpis
            raise NotFound(message, data)
316 be284f6a Christos Stavrakakis
        elif status < 200 or status >= 300:
317 21190887 Ilias Tsitsimpis
            raise AstakosClientException(message, data, status)
318 0a2a342c Ilias Tsitsimpis
319 0a2a342c Ilias Tsitsimpis
        try:
320 0a2a342c Ilias Tsitsimpis
            if data:
321 0a2a342c Ilias Tsitsimpis
                return simplejson.loads(unicode(data))
322 0a2a342c Ilias Tsitsimpis
            else:
323 10797183 Ilias Tsitsimpis
                return None
324 0a2a342c Ilias Tsitsimpis
        except Exception as err:
325 8975f6f6 Christos Stavrakakis
            msg = "Cannot parse response \"%s\" with simplejson: %s"
326 8975f6f6 Christos Stavrakakis
            self.logger.error(msg % (data, str(err)))
327 0a2a342c Ilias Tsitsimpis
            raise InvalidResponse(str(err), data)
328 4490d7b5 Ilias Tsitsimpis
329 4490d7b5 Ilias Tsitsimpis
    # ----------------------------------
330 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
331 c4644612 Ilias Tsitsimpis
    #   with {'uuids': uuids}
332 2c9c147e Ilias Tsitsimpis
    def _uuid_catalog(self, uuids, req_path):
333 2c9c147e Ilias Tsitsimpis
        """Helper function to retrieve uuid catalog"""
334 1c26b500 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
335 19198628 Ilias Tsitsimpis
        req_body = parse_request({'uuids': uuids}, self.logger)
336 2c9c147e Ilias Tsitsimpis
        data = self._call_astakos(req_path, headers=req_headers,
337 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
338 2377e7c2 Ilias Tsitsimpis
        if "uuid_catalog" in data:
339 2377e7c2 Ilias Tsitsimpis
            return data.get("uuid_catalog")
340 2377e7c2 Ilias Tsitsimpis
        else:
341 2c9c147e Ilias Tsitsimpis
            msg = "_uuid_catalog request returned %s. No uuid_catalog found" \
342 2c9c147e Ilias Tsitsimpis
                  % data
343 2c9c147e Ilias Tsitsimpis
            self.logger.error(msg)
344 2c9c147e Ilias Tsitsimpis
            raise AstakosClientException(msg)
345 1c26b500 Ilias Tsitsimpis
346 2c9c147e Ilias Tsitsimpis
    def get_usernames(self, uuids):
347 4490d7b5 Ilias Tsitsimpis
        """Return a uuid_catalog dictionary for the given uuids
348 4490d7b5 Ilias Tsitsimpis

349 4490d7b5 Ilias Tsitsimpis
        Keyword arguments:
350 4490d7b5 Ilias Tsitsimpis
        uuids   -- list of user ids (list of strings)
351 4490d7b5 Ilias Tsitsimpis

352 4490d7b5 Ilias Tsitsimpis
        The returned uuid_catalog is a dictionary with uuids as
353 4490d7b5 Ilias Tsitsimpis
        keys and the corresponding user names as values
354 4490d7b5 Ilias Tsitsimpis

355 4490d7b5 Ilias Tsitsimpis
        """
356 2c9c147e Ilias Tsitsimpis
        return self._uuid_catalog(uuids, self.api_usercatalogs)
357 4490d7b5 Ilias Tsitsimpis
358 2c9c147e Ilias Tsitsimpis
    def get_username(self, uuid):
359 794c94e6 Ilias Tsitsimpis
        """Return the user name of a uuid (see get_usernames)"""
360 10797183 Ilias Tsitsimpis
        check_input("get_username", self.logger, uuid=uuid)
361 2c9c147e Ilias Tsitsimpis
        uuid_dict = self.get_usernames([uuid])
362 2377e7c2 Ilias Tsitsimpis
        if uuid in uuid_dict:
363 2377e7c2 Ilias Tsitsimpis
            return uuid_dict.get(uuid)
364 2377e7c2 Ilias Tsitsimpis
        else:
365 794c94e6 Ilias Tsitsimpis
            raise NoUserName(uuid)
366 4490d7b5 Ilias Tsitsimpis
367 2c9c147e Ilias Tsitsimpis
    def service_get_usernames(self, uuids):
368 1c26b500 Ilias Tsitsimpis
        """Return a uuid_catalog dict using a service's token"""
369 2c9c147e Ilias Tsitsimpis
        return self._uuid_catalog(uuids, self.api_service_usercatalogs)
370 1c26b500 Ilias Tsitsimpis
371 2c9c147e Ilias Tsitsimpis
    def service_get_username(self, uuid):
372 1c26b500 Ilias Tsitsimpis
        """Return the displayName of a uuid using a service's token"""
373 10797183 Ilias Tsitsimpis
        check_input("service_get_username", self.logger, uuid=uuid)
374 2c9c147e Ilias Tsitsimpis
        uuid_dict = self.service_get_usernames([uuid])
375 2377e7c2 Ilias Tsitsimpis
        if uuid in uuid_dict:
376 2377e7c2 Ilias Tsitsimpis
            return uuid_dict.get(uuid)
377 2377e7c2 Ilias Tsitsimpis
        else:
378 794c94e6 Ilias Tsitsimpis
            raise NoUserName(uuid)
379 1c26b500 Ilias Tsitsimpis
380 aaf0a42c Ilias Tsitsimpis
    # ----------------------------------
381 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
382 c4644612 Ilias Tsitsimpis
    #   with {'displaynames': display_names}
383 2c9c147e Ilias Tsitsimpis
    def _displayname_catalog(self, display_names, req_path):
384 2c9c147e Ilias Tsitsimpis
        """Helper function to retrieve display names catalog"""
385 1c26b500 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
386 19198628 Ilias Tsitsimpis
        req_body = parse_request({'displaynames': display_names}, self.logger)
387 2c9c147e Ilias Tsitsimpis
        data = self._call_astakos(req_path, headers=req_headers,
388 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
389 2377e7c2 Ilias Tsitsimpis
        if "displayname_catalog" in data:
390 2377e7c2 Ilias Tsitsimpis
            return data.get("displayname_catalog")
391 2377e7c2 Ilias Tsitsimpis
        else:
392 2c9c147e Ilias Tsitsimpis
            msg = "_displayname_catalog request returned %s. " \
393 2c9c147e Ilias Tsitsimpis
                  "No displayname_catalog found" % data
394 2c9c147e Ilias Tsitsimpis
            self.logger.error(msg)
395 2c9c147e Ilias Tsitsimpis
            raise AstakosClientException(msg)
396 1c26b500 Ilias Tsitsimpis
397 2c9c147e Ilias Tsitsimpis
    def get_uuids(self, display_names):
398 aaf0a42c Ilias Tsitsimpis
        """Return a displayname_catalog for the given names
399 aaf0a42c Ilias Tsitsimpis

400 aaf0a42c Ilias Tsitsimpis
        Keyword arguments:
401 aaf0a42c Ilias Tsitsimpis
        display_names   -- list of user names (list of strings)
402 aaf0a42c Ilias Tsitsimpis

403 aaf0a42c Ilias Tsitsimpis
        The returned displayname_catalog is a dictionary with
404 aaf0a42c Ilias Tsitsimpis
        the names as keys and the corresponding uuids as values
405 aaf0a42c Ilias Tsitsimpis

406 aaf0a42c Ilias Tsitsimpis
        """
407 2c9c147e Ilias Tsitsimpis
        return self._displayname_catalog(
408 2c9c147e Ilias Tsitsimpis
            display_names, self.api_usercatalogs)
409 aaf0a42c Ilias Tsitsimpis
410 2c9c147e Ilias Tsitsimpis
    def get_uuid(self, display_name):
411 aaf0a42c Ilias Tsitsimpis
        """Return the uuid of a name (see getUUIDs)"""
412 10797183 Ilias Tsitsimpis
        check_input("get_uuid", self.logger, display_name=display_name)
413 2c9c147e Ilias Tsitsimpis
        name_dict = self.get_uuids([display_name])
414 2377e7c2 Ilias Tsitsimpis
        if display_name in name_dict:
415 2377e7c2 Ilias Tsitsimpis
            return name_dict.get(display_name)
416 2377e7c2 Ilias Tsitsimpis
        else:
417 2377e7c2 Ilias Tsitsimpis
            raise NoUUID(display_name)
418 aaf0a42c Ilias Tsitsimpis
419 2c9c147e Ilias Tsitsimpis
    def service_get_uuids(self, display_names):
420 1c26b500 Ilias Tsitsimpis
        """Return a display_name catalog using a service's token"""
421 2c9c147e Ilias Tsitsimpis
        return self._displayname_catalog(
422 2c9c147e Ilias Tsitsimpis
            display_names, self.api_service_usercatalogs)
423 1c26b500 Ilias Tsitsimpis
424 2c9c147e Ilias Tsitsimpis
    def service_get_uuid(self, display_name):
425 1c26b500 Ilias Tsitsimpis
        """Return the uuid of a name using a service's token"""
426 10797183 Ilias Tsitsimpis
        check_input("service_get_uuid", self.logger, display_name=display_name)
427 2c9c147e Ilias Tsitsimpis
        name_dict = self.service_get_uuids([display_name])
428 2377e7c2 Ilias Tsitsimpis
        if display_name in name_dict:
429 2377e7c2 Ilias Tsitsimpis
            return name_dict.get(display_name)
430 2377e7c2 Ilias Tsitsimpis
        else:
431 2377e7c2 Ilias Tsitsimpis
            raise NoUUID(display_name)
432 1c26b500 Ilias Tsitsimpis
433 3f8d6b11 Ilias Tsitsimpis
    # ----------------------------------
434 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_GETSERVICES``
435 794c94e6 Ilias Tsitsimpis
    def get_services(self):
436 3f8d6b11 Ilias Tsitsimpis
        """Return a list of dicts with the registered services"""
437 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_getservices)
438 c4644612 Ilias Tsitsimpis
439 c4644612 Ilias Tsitsimpis
    # ----------------------------------
440 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_RESOURCES``
441 c4644612 Ilias Tsitsimpis
    def get_resources(self):
442 c4644612 Ilias Tsitsimpis
        """Return a dict of dicts with the available resources"""
443 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_resources)
444 3f8d6b11 Ilias Tsitsimpis
445 baeb2ba5 Ilias Tsitsimpis
    # ----------------------------------
446 92683993 Ilias Tsitsimpis
    # do a POST to ``API_FEEDBACK``
447 2c9c147e Ilias Tsitsimpis
    def send_feedback(self, message, data):
448 92683993 Ilias Tsitsimpis
        """Send feedback to astakos service
449 92683993 Ilias Tsitsimpis

450 92683993 Ilias Tsitsimpis
        keyword arguments:
451 92683993 Ilias Tsitsimpis
        message     -- Feedback message
452 92683993 Ilias Tsitsimpis
        data        -- Additional information about service client status
453 92683993 Ilias Tsitsimpis

454 92683993 Ilias Tsitsimpis
        In case of success return nothing.
455 92683993 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException
456 92683993 Ilias Tsitsimpis

457 92683993 Ilias Tsitsimpis
        """
458 92683993 Ilias Tsitsimpis
        check_input("send_feedback", self.logger, message=message, data=data)
459 92683993 Ilias Tsitsimpis
        req_body = urllib.urlencode(
460 92683993 Ilias Tsitsimpis
            {'feedback_msg': message, 'feedback_data': data})
461 2c9c147e Ilias Tsitsimpis
        self._call_astakos(self.api_feedback, headers=None,
462 2c9c147e Ilias Tsitsimpis
                           body=req_body, method="POST")
463 108be31f Ilias Tsitsimpis
464 45c0bcf8 Giorgos Korfiatis
    # -----------------------------------------
465 45c0bcf8 Giorgos Korfiatis
    # do a POST to ``API_TOKENS`` with no token
466 6476ceb7 Ilias Tsitsimpis
    def get_endpoints(self, extra=False):
467 45c0bcf8 Giorgos Korfiatis
        """ Get services' endpoints
468 45c0bcf8 Giorgos Korfiatis

469 6476ceb7 Ilias Tsitsimpis
        The extra parameter is to be used by _fill_endpoints.
470 45c0bcf8 Giorgos Korfiatis
        In case of error raise an AstakosClientException.
471 45c0bcf8 Giorgos Korfiatis

472 45c0bcf8 Giorgos Korfiatis
        """
473 45c0bcf8 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
474 45c0bcf8 Giorgos Korfiatis
        req_body = None
475 45c0bcf8 Giorgos Korfiatis
        r = self._call_astakos(self.api_tokens, headers=req_headers,
476 45c0bcf8 Giorgos Korfiatis
                               body=req_body, method="POST",
477 45c0bcf8 Giorgos Korfiatis
                               log_body=False)
478 6476ceb7 Ilias Tsitsimpis
        self._fill_endpoints(r, extra=extra)
479 45c0bcf8 Giorgos Korfiatis
        return r
480 45c0bcf8 Giorgos Korfiatis
481 45c0bcf8 Giorgos Korfiatis
    # --------------------------------------
482 45c0bcf8 Giorgos Korfiatis
    # do a POST to ``API_TOKENS`` with a token
483 45c0bcf8 Giorgos Korfiatis
    def authenticate(self, tenant_name=None):
484 2c9c147e Ilias Tsitsimpis
        """ Authenticate and get services' endpoints
485 25a04cdd Ilias Tsitsimpis

486 25a04cdd Ilias Tsitsimpis
        Keyword arguments:
487 2c9c147e Ilias Tsitsimpis
        tenant_name         -- user's uniq id (optional)
488 25a04cdd Ilias Tsitsimpis

489 25a04cdd Ilias Tsitsimpis
        It returns back the token as well as information about the token
490 45c0bcf8 Giorgos Korfiatis
        holder and the services he/she can access (in json format).
491 2c9c147e Ilias Tsitsimpis

492 2c9c147e Ilias Tsitsimpis
        The tenant_name is optional and if it is given it must match the
493 2c9c147e Ilias Tsitsimpis
        user's uuid.
494 2c9c147e Ilias Tsitsimpis

495 25a04cdd Ilias Tsitsimpis
        In case of error raise an AstakosClientException.
496 25a04cdd Ilias Tsitsimpis

497 25a04cdd Ilias Tsitsimpis
        """
498 25a04cdd Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
499 45c0bcf8 Giorgos Korfiatis
        body = {'auth': {'token': {'id': self.token}}}
500 45c0bcf8 Giorgos Korfiatis
        if tenant_name is not None:
501 45c0bcf8 Giorgos Korfiatis
            body['auth']['tenantName'] = tenant_name
502 45c0bcf8 Giorgos Korfiatis
        req_body = parse_request(body, self.logger)
503 45c0bcf8 Giorgos Korfiatis
        r = self._call_astakos(self.api_tokens, headers=req_headers,
504 45c0bcf8 Giorgos Korfiatis
                               body=req_body, method="POST",
505 45c0bcf8 Giorgos Korfiatis
                               log_body=False)
506 45c0bcf8 Giorgos Korfiatis
        self._fill_endpoints(r)
507 45c0bcf8 Giorgos Korfiatis
        return r
508 25a04cdd Ilias Tsitsimpis
509 b39ca571 Sofia Papagiannaki
    # --------------------------------------
510 b39ca571 Sofia Papagiannaki
    # do a GET to ``API_TOKENS`` with a token
511 259a5f9a Ilias Tsitsimpis
    def validate_token(self, token_id, belongs_to=None):
512 b39ca571 Sofia Papagiannaki
        """ Validate a temporary access token (oath2)
513 b39ca571 Sofia Papagiannaki

514 b39ca571 Sofia Papagiannaki
        Keyword arguments:
515 b39ca571 Sofia Papagiannaki
        belongsTo         -- confirm that token belongs to tenant
516 b39ca571 Sofia Papagiannaki

517 b39ca571 Sofia Papagiannaki
        It returns back the token as well as information about the token
518 b39ca571 Sofia Papagiannaki
        holder.
519 b39ca571 Sofia Papagiannaki

520 259a5f9a Ilias Tsitsimpis
        The belongs_to is optional and if it is given it must be inside the
521 b39ca571 Sofia Papagiannaki
        token's scope.
522 b39ca571 Sofia Papagiannaki

523 b39ca571 Sofia Papagiannaki
        In case of error raise an AstakosClientException.
524 b39ca571 Sofia Papagiannaki

525 b39ca571 Sofia Papagiannaki
        """
526 b39ca571 Sofia Papagiannaki
        path = join_urls(self.api_tokens, str(token_id))
527 259a5f9a Ilias Tsitsimpis
        if belongs_to is not None:
528 259a5f9a Ilias Tsitsimpis
            params = {'belongsTo': belongs_to}
529 b39ca571 Sofia Papagiannaki
            path = '%s?%s' % (path, urllib.urlencode(params))
530 b39ca571 Sofia Papagiannaki
        return self._call_astakos(path, method="GET", log_body=False)
531 b39ca571 Sofia Papagiannaki
532 25a04cdd Ilias Tsitsimpis
    # ----------------------------------
533 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_QUOTAS``
534 2c9c147e Ilias Tsitsimpis
    def get_quotas(self):
535 fd420756 Ilias Tsitsimpis
        """Get user's quotas
536 fd420756 Ilias Tsitsimpis

537 fd420756 Ilias Tsitsimpis
        In case of success return a dict of dicts with user's current quotas.
538 fd420756 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException
539 fd420756 Ilias Tsitsimpis

540 fd420756 Ilias Tsitsimpis
        """
541 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_quotas)
542 baeb2ba5 Ilias Tsitsimpis
543 fd420756 Ilias Tsitsimpis
    # ----------------------------------
544 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_SERVICE_QUOTAS``
545 2c9c147e Ilias Tsitsimpis
    def service_get_quotas(self, user=None):
546 5b33b8e5 Giorgos Korfiatis
        """Get all quotas for resources associated with the service
547 5b33b8e5 Giorgos Korfiatis

548 5b33b8e5 Giorgos Korfiatis
        Keyword arguments:
549 db9f7a2b Giorgos Korfiatis
        user    -- optionally, the uuid of a specific user
550 5b33b8e5 Giorgos Korfiatis

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

555 5b33b8e5 Giorgos Korfiatis
        """
556 2c9c147e Ilias Tsitsimpis
        query = self.api_service_quotas
557 db9f7a2b Giorgos Korfiatis
        if user is not None:
558 db9f7a2b Giorgos Korfiatis
            query += "?user=" + user
559 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(query)
560 5b33b8e5 Giorgos Korfiatis
561 5b33b8e5 Giorgos Korfiatis
    # ----------------------------------
562 dccd7fa0 Giorgos Korfiatis
    # do a GET to ``API_SERVICE_PROJECT_QUOTAS``
563 dccd7fa0 Giorgos Korfiatis
    def service_get_project_quotas(self, project=None):
564 dccd7fa0 Giorgos Korfiatis
        """Get all project quotas for resources associated with the service
565 dccd7fa0 Giorgos Korfiatis

566 dccd7fa0 Giorgos Korfiatis
        Keyword arguments:
567 dccd7fa0 Giorgos Korfiatis
        project    -- optionally, the uuid of a specific project
568 dccd7fa0 Giorgos Korfiatis

569 dccd7fa0 Giorgos Korfiatis
        In case of success return a dict of dicts with current quotas
570 dccd7fa0 Giorgos Korfiatis
        for all projects, or of a specified project, if project argument is set.
571 dccd7fa0 Giorgos Korfiatis
        Otherwise raise an AstakosClientException
572 dccd7fa0 Giorgos Korfiatis

573 dccd7fa0 Giorgos Korfiatis
        """
574 dccd7fa0 Giorgos Korfiatis
        query = self.api_service_project_quotas
575 dccd7fa0 Giorgos Korfiatis
        if project is not None:
576 dccd7fa0 Giorgos Korfiatis
            query += "?project=" + project
577 dccd7fa0 Giorgos Korfiatis
        return self._call_astakos(query)
578 dccd7fa0 Giorgos Korfiatis
579 dccd7fa0 Giorgos Korfiatis
    # ----------------------------------
580 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``
581 da9bcceb Giorgos Korfiatis
    def _issue_commission(self, request):
582 fd420756 Ilias Tsitsimpis
        """Issue a commission
583 fd420756 Ilias Tsitsimpis

584 fd420756 Ilias Tsitsimpis
        Keyword arguments:
585 fd420756 Ilias Tsitsimpis
        request -- commision request (dict)
586 fd420756 Ilias Tsitsimpis

587 fd420756 Ilias Tsitsimpis
        In case of success return commission's id (int).
588 fd420756 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
589 fd420756 Ilias Tsitsimpis

590 fd420756 Ilias Tsitsimpis
        """
591 fd420756 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
592 19198628 Ilias Tsitsimpis
        req_body = parse_request(request, self.logger)
593 fd420756 Ilias Tsitsimpis
        try:
594 2c9c147e Ilias Tsitsimpis
            response = self._call_astakos(self.api_commissions,
595 2c9c147e Ilias Tsitsimpis
                                          headers=req_headers,
596 2c9c147e Ilias Tsitsimpis
                                          body=req_body,
597 2c9c147e Ilias Tsitsimpis
                                          method="POST")
598 fd420756 Ilias Tsitsimpis
        except AstakosClientException as err:
599 fd420756 Ilias Tsitsimpis
            if err.status == 413:
600 fd420756 Ilias Tsitsimpis
                raise QuotaLimit(err.message, err.details)
601 fd420756 Ilias Tsitsimpis
            else:
602 fd420756 Ilias Tsitsimpis
                raise
603 fd420756 Ilias Tsitsimpis
604 fd420756 Ilias Tsitsimpis
        if "serial" in response:
605 fd420756 Ilias Tsitsimpis
            return response['serial']
606 fd420756 Ilias Tsitsimpis
        else:
607 2c9c147e Ilias Tsitsimpis
            msg = "issue_commission_core request returned %s. " + \
608 2c9c147e Ilias Tsitsimpis
                  "No serial found" % response
609 2c9c147e Ilias Tsitsimpis
            self.logger.error(msg)
610 2c9c147e Ilias Tsitsimpis
            raise AstakosClientException(msg)
611 fd420756 Ilias Tsitsimpis
612 da9bcceb Giorgos Korfiatis
    def _mk_user_provision(self, holder, source, resource, quantity):
613 da9bcceb Giorgos Korfiatis
        holder = "user:" + holder
614 da9bcceb Giorgos Korfiatis
        source = "project:" + source
615 da9bcceb Giorgos Korfiatis
        return {"holder": holder, "source": source,
616 da9bcceb Giorgos Korfiatis
                "resource": resource, "quantity": quantity}
617 da9bcceb Giorgos Korfiatis
618 da9bcceb Giorgos Korfiatis
    def _mk_project_provision(self, holder, resource, quantity):
619 da9bcceb Giorgos Korfiatis
        holder = "project:" + holder
620 da9bcceb Giorgos Korfiatis
        return {"holder": holder, "source": None,
621 da9bcceb Giorgos Korfiatis
                "resource": resource, "quantity": quantity}
622 da9bcceb Giorgos Korfiatis
623 da9bcceb Giorgos Korfiatis
    def mk_provisions(self, holder, source, resource, quantity):
624 da9bcceb Giorgos Korfiatis
        return [self._mk_user_provision(holder, source, resource, quantity),
625 da9bcceb Giorgos Korfiatis
                self._mk_project_provision(source, resource, quantity)]
626 da9bcceb Giorgos Korfiatis
627 da9bcceb Giorgos Korfiatis
    def issue_commission_generic(self, user_provisions, project_provisions,
628 da9bcceb Giorgos Korfiatis
                                 name="", force=False, auto_accept=False):
629 da9bcceb Giorgos Korfiatis
        """Issue commission (for multiple holder/source pairs)
630 da9bcceb Giorgos Korfiatis

631 da9bcceb Giorgos Korfiatis
        keyword arguments:
632 da9bcceb Giorgos Korfiatis
        user_provisions  -- dict mapping user holdings
633 da9bcceb Giorgos Korfiatis
                            (user, project, resource) to integer quantities
634 da9bcceb Giorgos Korfiatis
        project_provisions -- dict mapping project holdings
635 da9bcceb Giorgos Korfiatis
                              (project, resource) to integer quantities
636 da9bcceb Giorgos Korfiatis
        name        -- description of the commission (string)
637 da9bcceb Giorgos Korfiatis
        force       -- force this commission (boolean)
638 da9bcceb Giorgos Korfiatis
        auto_accept -- auto accept this commission (boolean)
639 da9bcceb Giorgos Korfiatis

640 da9bcceb Giorgos Korfiatis
        In case of success return commission's id (int).
641 da9bcceb Giorgos Korfiatis
        Otherwise raise an AstakosClientException.
642 da9bcceb Giorgos Korfiatis

643 da9bcceb Giorgos Korfiatis
        """
644 da9bcceb Giorgos Korfiatis
        request = {}
645 da9bcceb Giorgos Korfiatis
        request["force"] = force
646 da9bcceb Giorgos Korfiatis
        request["auto_accept"] = auto_accept
647 da9bcceb Giorgos Korfiatis
        request["name"] = name
648 da9bcceb Giorgos Korfiatis
        try:
649 da9bcceb Giorgos Korfiatis
            request["provisions"] = []
650 da9bcceb Giorgos Korfiatis
            for (holder, source, resource), quantity in \
651 da9bcceb Giorgos Korfiatis
                    user_provisions.iteritems():
652 da9bcceb Giorgos Korfiatis
                p = self._mk_user_provision(holder, source, resource, quantity)
653 da9bcceb Giorgos Korfiatis
                request["provisions"].append(p)
654 da9bcceb Giorgos Korfiatis
            for (holder, resource), quantity in project_provisions.iteritems():
655 da9bcceb Giorgos Korfiatis
                p = self._mk_project_provision(holder, resource, quantity)
656 da9bcceb Giorgos Korfiatis
                request["provisions"].append(p)
657 da9bcceb Giorgos Korfiatis
        except Exception as err:
658 da9bcceb Giorgos Korfiatis
            self.logger.error(str(err))
659 da9bcceb Giorgos Korfiatis
            raise BadValue(str(err))
660 da9bcceb Giorgos Korfiatis
661 da9bcceb Giorgos Korfiatis
        return self._issue_commission(request)
662 da9bcceb Giorgos Korfiatis
663 2c9c147e Ilias Tsitsimpis
    def issue_one_commission(self, holder, source, provisions,
664 3a1bed03 Giorgos Korfiatis
                             name="", force=False, auto_accept=False):
665 12eab714 Ilias Tsitsimpis
        """Issue one commission (with specific holder and source)
666 12eab714 Ilias Tsitsimpis

667 12eab714 Ilias Tsitsimpis
        keyword arguments:
668 12eab714 Ilias Tsitsimpis
        holder      -- user's id (string)
669 12eab714 Ilias Tsitsimpis
        source      -- commission's source (ex system) (string)
670 8b68fa76 Giorgos Korfiatis
        provisions  -- resources with their quantity (dict from string to int)
671 3a1bed03 Giorgos Korfiatis
        name        -- description of the commission (string)
672 12eab714 Ilias Tsitsimpis
        force       -- force this commission (boolean)
673 12eab714 Ilias Tsitsimpis
        auto_accept -- auto accept this commission (boolean)
674 12eab714 Ilias Tsitsimpis

675 12eab714 Ilias Tsitsimpis
        In case of success return commission's id (int).
676 12eab714 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
677 12eab714 Ilias Tsitsimpis

678 12eab714 Ilias Tsitsimpis
        """
679 d5f086f2 Ilias Tsitsimpis
        check_input("issue_one_commission", self.logger,
680 d5f086f2 Ilias Tsitsimpis
                    holder=holder, source=source,
681 d5f086f2 Ilias Tsitsimpis
                    provisions=provisions)
682 12eab714 Ilias Tsitsimpis
683 12eab714 Ilias Tsitsimpis
        request = {}
684 12eab714 Ilias Tsitsimpis
        request["force"] = force
685 12eab714 Ilias Tsitsimpis
        request["auto_accept"] = auto_accept
686 3a1bed03 Giorgos Korfiatis
        request["name"] = name
687 12eab714 Ilias Tsitsimpis
        try:
688 12eab714 Ilias Tsitsimpis
            request["provisions"] = []
689 567f49a2 Giorgos Korfiatis
            for resource, quantity in provisions.iteritems():
690 da9bcceb Giorgos Korfiatis
                ps = self.mk_provisions(holder, source, resource, quantity)
691 da9bcceb Giorgos Korfiatis
                request["provisions"].extend(ps)
692 12eab714 Ilias Tsitsimpis
        except Exception as err:
693 12eab714 Ilias Tsitsimpis
            self.logger.error(str(err))
694 12eab714 Ilias Tsitsimpis
            raise BadValue(str(err))
695 12eab714 Ilias Tsitsimpis
696 da9bcceb Giorgos Korfiatis
        return self._issue_commission(request)
697 12eab714 Ilias Tsitsimpis
698 1f4a46dd Giorgos Korfiatis
    def issue_resource_reassignment(self, holder, from_source,
699 1f4a46dd Giorgos Korfiatis
                                    to_source, provisions, name="",
700 1f4a46dd Giorgos Korfiatis
                                    force=False, auto_accept=False):
701 1f4a46dd Giorgos Korfiatis
        """Change resource assignment to another project
702 1f4a46dd Giorgos Korfiatis
        """
703 1f4a46dd Giorgos Korfiatis
704 1f4a46dd Giorgos Korfiatis
        request = {}
705 1f4a46dd Giorgos Korfiatis
        request["force"] = force
706 1f4a46dd Giorgos Korfiatis
        request["auto_accept"] = auto_accept
707 1f4a46dd Giorgos Korfiatis
        request["name"] = name
708 1f4a46dd Giorgos Korfiatis
709 1f4a46dd Giorgos Korfiatis
        try:
710 1f4a46dd Giorgos Korfiatis
            request["provisions"] = []
711 1f4a46dd Giorgos Korfiatis
            for resource, quantity in provisions.iteritems():
712 1f4a46dd Giorgos Korfiatis
                ps = self.mk_provisions(
713 1f4a46dd Giorgos Korfiatis
                    holder, from_source, resource, -quantity)
714 1f4a46dd Giorgos Korfiatis
                ps += self.mk_provisions(holder, to_source, resource, quantity)
715 1f4a46dd Giorgos Korfiatis
                request["provisions"].extend(ps)
716 1f4a46dd Giorgos Korfiatis
        except Exception as err:
717 1f4a46dd Giorgos Korfiatis
            self.logger.error(str(err))
718 1f4a46dd Giorgos Korfiatis
            raise BadValue(str(err))
719 1f4a46dd Giorgos Korfiatis
720 1f4a46dd Giorgos Korfiatis
        return self._issue_commission(request)
721 1f4a46dd Giorgos Korfiatis
722 7a0180ef Ilias Tsitsimpis
    # ----------------------------------
723 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``
724 2c9c147e Ilias Tsitsimpis
    def get_pending_commissions(self):
725 7a0180ef Ilias Tsitsimpis
        """Get Pending Commissions
726 7a0180ef Ilias Tsitsimpis

727 7a0180ef Ilias Tsitsimpis
        In case of success return a list of pending commissions' ids
728 7a0180ef Ilias Tsitsimpis
        (list of integers)
729 7a0180ef Ilias Tsitsimpis

730 7a0180ef Ilias Tsitsimpis
        """
731 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_commissions)
732 7a0180ef Ilias Tsitsimpis
733 994f37b6 Ilias Tsitsimpis
    # ----------------------------------
734 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``/<serial>
735 2c9c147e Ilias Tsitsimpis
    def get_commission_info(self, serial):
736 994f37b6 Ilias Tsitsimpis
        """Get Description of a Commission
737 994f37b6 Ilias Tsitsimpis

738 994f37b6 Ilias Tsitsimpis
        Keyword arguments:
739 994f37b6 Ilias Tsitsimpis
        serial  -- commission's id (int)
740 994f37b6 Ilias Tsitsimpis

741 994f37b6 Ilias Tsitsimpis
        In case of success return a dict of dicts containing
742 5c418e94 Ilias Tsitsimpis
        informations (details) about the requested commission
743 994f37b6 Ilias Tsitsimpis

744 994f37b6 Ilias Tsitsimpis
        """
745 10797183 Ilias Tsitsimpis
        check_input("get_commission_info", self.logger, serial=serial)
746 994f37b6 Ilias Tsitsimpis
747 2c9c147e Ilias Tsitsimpis
        path = self.api_commissions.rstrip('/') + "/" + str(serial)
748 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
749 994f37b6 Ilias Tsitsimpis
750 805e294c Ilias Tsitsimpis
    # ----------------------------------
751 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``/<serial>/action"
752 2c9c147e Ilias Tsitsimpis
    def commission_action(self, serial, action):
753 b5008ef0 Ilias Tsitsimpis
        """Perform a commission action
754 805e294c Ilias Tsitsimpis

755 805e294c Ilias Tsitsimpis
        Keyword arguments:
756 805e294c Ilias Tsitsimpis
        serial  -- commission's id (int)
757 805e294c Ilias Tsitsimpis
        action  -- action to perform, currently accept/reject (string)
758 805e294c Ilias Tsitsimpis

759 805e294c Ilias Tsitsimpis
        In case of success return nothing.
760 805e294c Ilias Tsitsimpis

761 805e294c Ilias Tsitsimpis
        """
762 10797183 Ilias Tsitsimpis
        check_input("commission_action", self.logger,
763 10797183 Ilias Tsitsimpis
                    serial=serial, action=action)
764 805e294c Ilias Tsitsimpis
765 2c9c147e Ilias Tsitsimpis
        path = self.api_commissions.rstrip('/') + "/" + str(serial) + "/action"
766 805e294c Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
767 19198628 Ilias Tsitsimpis
        req_body = parse_request({str(action): ""}, self.logger)
768 2c9c147e Ilias Tsitsimpis
        self._call_astakos(path, headers=req_headers,
769 2c9c147e Ilias Tsitsimpis
                           body=req_body, method="POST")
770 805e294c Ilias Tsitsimpis
771 2c9c147e Ilias Tsitsimpis
    def accept_commission(self, serial):
772 b5008ef0 Ilias Tsitsimpis
        """Accept a commission (see commission_action)"""
773 2c9c147e Ilias Tsitsimpis
        self.commission_action(serial, "accept")
774 805e294c Ilias Tsitsimpis
775 2c9c147e Ilias Tsitsimpis
    def reject_commission(self, serial):
776 b5008ef0 Ilias Tsitsimpis
        """Reject a commission (see commission_action)"""
777 2c9c147e Ilias Tsitsimpis
        self.commission_action(serial, "reject")
778 805e294c Ilias Tsitsimpis
779 81875157 Ilias Tsitsimpis
    # ----------------------------------
780 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS_ACTION``
781 2c9c147e Ilias Tsitsimpis
    def resolve_commissions(self, accept_serials, reject_serials):
782 81875157 Ilias Tsitsimpis
        """Resolve multiple commissions at once
783 81875157 Ilias Tsitsimpis

784 81875157 Ilias Tsitsimpis
        Keyword arguments:
785 81875157 Ilias Tsitsimpis
        accept_serials  -- commissions to accept (list of ints)
786 81875157 Ilias Tsitsimpis
        reject_serials  -- commissions to reject (list of ints)
787 81875157 Ilias Tsitsimpis

788 81875157 Ilias Tsitsimpis
        In case of success return a dict of dicts describing which
789 81875157 Ilias Tsitsimpis
        commissions accepted, which rejected and which failed to
790 81875157 Ilias Tsitsimpis
        resolved.
791 81875157 Ilias Tsitsimpis

792 81875157 Ilias Tsitsimpis
        """
793 d5f086f2 Ilias Tsitsimpis
        check_input("resolve_commissions", self.logger,
794 d5f086f2 Ilias Tsitsimpis
                    accept_serials=accept_serials,
795 d5f086f2 Ilias Tsitsimpis
                    reject_serials=reject_serials)
796 81875157 Ilias Tsitsimpis
797 81875157 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
798 81875157 Ilias Tsitsimpis
        req_body = parse_request({"accept": accept_serials,
799 81875157 Ilias Tsitsimpis
                                  "reject": reject_serials},
800 81875157 Ilias Tsitsimpis
                                 self.logger)
801 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_commissions_action,
802 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body,
803 2c9c147e Ilias Tsitsimpis
                                  method="POST")
804 81875157 Ilias Tsitsimpis
805 47bb45c0 Giorgos Korfiatis
    # ----------------------------
806 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_PROJECTS``
807 edc51a21 Giorgos Korfiatis
    def get_projects(self, name=None, state=None, owner=None, mode=None):
808 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible projects
809 47bb45c0 Giorgos Korfiatis

810 47bb45c0 Giorgos Korfiatis
        Arguments:
811 47bb45c0 Giorgos Korfiatis
        name  -- filter by name (optional)
812 47bb45c0 Giorgos Korfiatis
        state -- filter by state (optional)
813 47bb45c0 Giorgos Korfiatis
        owner -- filter by owner (optional)
814 edc51a21 Giorgos Korfiatis
        mode  -- if value is 'member', return only active projects in which
815 edc51a21 Giorgos Korfiatis
                 the request user is an active member
816 47bb45c0 Giorgos Korfiatis

817 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of project descriptions.
818 47bb45c0 Giorgos Korfiatis
        """
819 47bb45c0 Giorgos Korfiatis
        filters = {}
820 47bb45c0 Giorgos Korfiatis
        if name is not None:
821 47bb45c0 Giorgos Korfiatis
            filters["name"] = name
822 47bb45c0 Giorgos Korfiatis
        if state is not None:
823 47bb45c0 Giorgos Korfiatis
            filters["state"] = state
824 47bb45c0 Giorgos Korfiatis
        if owner is not None:
825 47bb45c0 Giorgos Korfiatis
            filters["owner"] = owner
826 edc51a21 Giorgos Korfiatis
        if mode is not None:
827 edc51a21 Giorgos Korfiatis
            filters["mode"] = mode
828 a1ff970b Giorgos Korfiatis
        path = self.api_projects
829 a1ff970b Giorgos Korfiatis
        if filters:
830 a1ff970b Giorgos Korfiatis
            path += "?" + urllib.urlencode(filters)
831 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
832 a1ff970b Giorgos Korfiatis
        return self._call_astakos(path, headers=req_headers)
833 47bb45c0 Giorgos Korfiatis
834 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
835 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_PROJECTS``/<project_id>
836 2c9c147e Ilias Tsitsimpis
    def get_project(self, project_id):
837 47bb45c0 Giorgos Korfiatis
        """Retrieve project description, if accessible
838 47bb45c0 Giorgos Korfiatis

839 47bb45c0 Giorgos Korfiatis
        Arguments:
840 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
841 47bb45c0 Giorgos Korfiatis

842 47bb45c0 Giorgos Korfiatis
        In case of success, return project description.
843 47bb45c0 Giorgos Korfiatis
        """
844 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
845 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
846 47bb45c0 Giorgos Korfiatis
847 47bb45c0 Giorgos Korfiatis
    # -----------------------------
848 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``
849 2c9c147e Ilias Tsitsimpis
    def create_project(self, specs):
850 47bb45c0 Giorgos Korfiatis
        """Submit application to create a new project
851 47bb45c0 Giorgos Korfiatis

852 47bb45c0 Giorgos Korfiatis
        Arguments:
853 47bb45c0 Giorgos Korfiatis
        specs -- dict describing a project
854 47bb45c0 Giorgos Korfiatis

855 47bb45c0 Giorgos Korfiatis
        In case of success, return project and application identifiers.
856 47bb45c0 Giorgos Korfiatis
        """
857 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
858 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(specs, self.logger)
859 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_projects,
860 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body,
861 2c9c147e Ilias Tsitsimpis
                                  method="POST")
862 47bb45c0 Giorgos Korfiatis
863 47bb45c0 Giorgos Korfiatis
    # ------------------------------------------
864 d9fca80b Giorgos Korfiatis
    # do a PUT to ``API_PROJECTS``/<project_id>
865 2c9c147e Ilias Tsitsimpis
    def modify_project(self, project_id, specs):
866 47bb45c0 Giorgos Korfiatis
        """Submit application to modify an existing project
867 47bb45c0 Giorgos Korfiatis

868 47bb45c0 Giorgos Korfiatis
        Arguments:
869 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
870 47bb45c0 Giorgos Korfiatis
        specs      -- dict describing a project
871 47bb45c0 Giorgos Korfiatis

872 47bb45c0 Giorgos Korfiatis
        In case of success, return project and application identifiers.
873 47bb45c0 Giorgos Korfiatis
        """
874 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
875 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
876 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(specs, self.logger)
877 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
878 d9fca80b Giorgos Korfiatis
                                  body=req_body, method="PUT")
879 47bb45c0 Giorgos Korfiatis
880 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
881 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``/<project_id>/action
882 2c9c147e Ilias Tsitsimpis
    def project_action(self, project_id, action, reason=""):
883 47bb45c0 Giorgos Korfiatis
        """Perform action on a project
884 47bb45c0 Giorgos Korfiatis

885 47bb45c0 Giorgos Korfiatis
        Arguments:
886 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
887 47bb45c0 Giorgos Korfiatis
        action     -- action to perform, one of "suspend", "unsuspend",
888 47bb45c0 Giorgos Korfiatis
                      "terminate", "reinstate"
889 47bb45c0 Giorgos Korfiatis
        reason     -- reason of performing the action
890 47bb45c0 Giorgos Korfiatis

891 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
892 47bb45c0 Giorgos Korfiatis
        """
893 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
894 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
895 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
896 4334d1c8 Giorgos Korfiatis
        req_body = parse_request({action: {"reason": reason}}, self.logger)
897 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
898 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
899 47bb45c0 Giorgos Korfiatis
900 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
901 4334d1c8 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``/<project_id>/action
902 4334d1c8 Giorgos Korfiatis
    def application_action(self, project_id, app_id, action, reason=""):
903 4334d1c8 Giorgos Korfiatis
        """Perform action on a project application
904 47bb45c0 Giorgos Korfiatis

905 47bb45c0 Giorgos Korfiatis
        Arguments:
906 4334d1c8 Giorgos Korfiatis
        project_id -- project identifier
907 4334d1c8 Giorgos Korfiatis
        app_id     -- application identifier
908 4334d1c8 Giorgos Korfiatis
        action     -- action to perform, one of "approve", "deny",
909 4334d1c8 Giorgos Korfiatis
                      "dismiss", "cancel"
910 4334d1c8 Giorgos Korfiatis
        reason     -- reason of performing the action
911 47bb45c0 Giorgos Korfiatis

912 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
913 47bb45c0 Giorgos Korfiatis
        """
914 4334d1c8 Giorgos Korfiatis
        path = join_urls(self.api_projects, str(project_id))
915 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
916 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
917 4334d1c8 Giorgos Korfiatis
        req_body = parse_request({action: {
918 4334d1c8 Giorgos Korfiatis
                    "reasons": reason,
919 4334d1c8 Giorgos Korfiatis
                    "app_id": app_id}}, self.logger)
920 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
921 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
922 47bb45c0 Giorgos Korfiatis
923 47bb45c0 Giorgos Korfiatis
    # -------------------------------
924 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_MEMBERSHIPS``
925 2c9c147e Ilias Tsitsimpis
    def get_memberships(self, project=None):
926 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible memberships
927 47bb45c0 Giorgos Korfiatis

928 47bb45c0 Giorgos Korfiatis
        Arguments:
929 47bb45c0 Giorgos Korfiatis
        project -- filter by project (optional)
930 47bb45c0 Giorgos Korfiatis

931 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of membership descriptions.
932 47bb45c0 Giorgos Korfiatis
        """
933 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
934 a1ff970b Giorgos Korfiatis
        filters = {}
935 a1ff970b Giorgos Korfiatis
        if project is not None:
936 a1ff970b Giorgos Korfiatis
            filters["project"] = project
937 a1ff970b Giorgos Korfiatis
        path = self.api_memberships
938 a1ff970b Giorgos Korfiatis
        if filters:
939 a1ff970b Giorgos Korfiatis
            path += '?' + urllib.urlencode(filters)
940 a1ff970b Giorgos Korfiatis
        return self._call_astakos(path, headers=req_headers)
941 47bb45c0 Giorgos Korfiatis
942 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
943 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_MEMBERSHIPS``/<memb_id>
944 2c9c147e Ilias Tsitsimpis
    def get_membership(self, memb_id):
945 47bb45c0 Giorgos Korfiatis
        """Retrieve membership description, if accessible
946 47bb45c0 Giorgos Korfiatis

947 47bb45c0 Giorgos Korfiatis
        Arguments:
948 47bb45c0 Giorgos Korfiatis
        memb_id -- membership identifier
949 47bb45c0 Giorgos Korfiatis

950 47bb45c0 Giorgos Korfiatis
        In case of success, return membership description.
951 47bb45c0 Giorgos Korfiatis
        """
952 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_memberships, str(memb_id))
953 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
954 47bb45c0 Giorgos Korfiatis
955 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
956 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``/<memb_id>/action
957 2c9c147e Ilias Tsitsimpis
    def membership_action(self, memb_id, action, reason=""):
958 47bb45c0 Giorgos Korfiatis
        """Perform action on a membership
959 47bb45c0 Giorgos Korfiatis

960 47bb45c0 Giorgos Korfiatis
        Arguments:
961 47bb45c0 Giorgos Korfiatis
        memb_id -- membership identifier
962 47bb45c0 Giorgos Korfiatis
        action  -- action to perform, one of "leave", "cancel", "accept",
963 47bb45c0 Giorgos Korfiatis
                   "reject", "remove"
964 47bb45c0 Giorgos Korfiatis
        reason  -- reason of performing the action
965 47bb45c0 Giorgos Korfiatis

966 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
967 47bb45c0 Giorgos Korfiatis
        """
968 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_memberships, str(memb_id))
969 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
970 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
971 47bb45c0 Giorgos Korfiatis
        req_body = parse_request({action: reason}, self.logger)
972 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
973 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
974 47bb45c0 Giorgos Korfiatis
975 47bb45c0 Giorgos Korfiatis
    # --------------------------------
976 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``
977 2c9c147e Ilias Tsitsimpis
    def join_project(self, project_id):
978 47bb45c0 Giorgos Korfiatis
        """Join a project
979 47bb45c0 Giorgos Korfiatis

980 47bb45c0 Giorgos Korfiatis
        Arguments:
981 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
982 47bb45c0 Giorgos Korfiatis

983 47bb45c0 Giorgos Korfiatis
        In case of success, return membership identifier.
984 47bb45c0 Giorgos Korfiatis
        """
985 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
986 47bb45c0 Giorgos Korfiatis
        body = {"join": {"project": project_id}}
987 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger)
988 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships, headers=req_headers,
989 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
990 47bb45c0 Giorgos Korfiatis
991 47bb45c0 Giorgos Korfiatis
    # --------------------------------
992 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``
993 2c9c147e Ilias Tsitsimpis
    def enroll_member(self, project_id, email):
994 47bb45c0 Giorgos Korfiatis
        """Enroll a user in a project
995 47bb45c0 Giorgos Korfiatis

996 47bb45c0 Giorgos Korfiatis
        Arguments:
997 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
998 47bb45c0 Giorgos Korfiatis
        email      -- user identified by email
999 47bb45c0 Giorgos Korfiatis

1000 47bb45c0 Giorgos Korfiatis
        In case of success, return membership identifier.
1001 47bb45c0 Giorgos Korfiatis
        """
1002 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
1003 47bb45c0 Giorgos Korfiatis
        body = {"enroll": {"project": project_id, "user": email}}
1004 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger)
1005 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships, headers=req_headers,
1006 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
1007 2c9c147e Ilias Tsitsimpis
1008 d2104099 Sofia Papagiannaki
    # --------------------------------
1009 fe7d0186 Sofia Papagiannaki
    # do a POST to ``API_OAUTH2_TOKEN``
1010 d2104099 Sofia Papagiannaki
    def get_token(self, grant_type, client_id, client_secret, **body_params):
1011 75144caa Sofia Papagiannaki
        headers = {'content-type': 'application/x-www-form-urlencoded',
1012 d2104099 Sofia Papagiannaki
                   'Authorization': 'Basic %s' % b64encode('%s:%s' %
1013 d2104099 Sofia Papagiannaki
                                                           (client_id,
1014 d2104099 Sofia Papagiannaki
                                                            client_secret))}
1015 d2104099 Sofia Papagiannaki
        body_params['grant_type'] = grant_type
1016 d2104099 Sofia Papagiannaki
        body = urllib.urlencode(body_params)
1017 fe7d0186 Sofia Papagiannaki
        return self._call_astakos(self.api_oauth2_token, headers=headers,
1018 d2104099 Sofia Papagiannaki
                                  body=body, method="POST")
1019 d2104099 Sofia Papagiannaki
1020 2c9c147e Ilias Tsitsimpis
1021 2c9c147e Ilias Tsitsimpis
# --------------------------------------------------------------------
1022 2c9c147e Ilias Tsitsimpis
# parse endpoints
1023 2c9c147e Ilias Tsitsimpis
def parse_endpoints(endpoints, ep_name=None, ep_type=None,
1024 2c9c147e Ilias Tsitsimpis
                    ep_region=None, ep_version_id=None):
1025 2c9c147e Ilias Tsitsimpis
    """Parse endpoints server response and extract the ones needed
1026 2c9c147e Ilias Tsitsimpis

1027 2c9c147e Ilias Tsitsimpis
    Keyword arguments:
1028 2c9c147e Ilias Tsitsimpis
    endpoints     -- the endpoints (json response from get_endpoints)
1029 2c9c147e Ilias Tsitsimpis
    ep_name       -- return only endpoints with this name (optional)
1030 2c9c147e Ilias Tsitsimpis
    ep_type       -- return only endpoints with this type (optional)
1031 2c9c147e Ilias Tsitsimpis
    ep_region     -- return only endpoints with this region (optional)
1032 2c9c147e Ilias Tsitsimpis
    ep_version_id -- return only endpoints with this versionId (optional)
1033 2c9c147e Ilias Tsitsimpis

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

1038 2c9c147e Ilias Tsitsimpis
    """
1039 2c9c147e Ilias Tsitsimpis
    try:
1040 2c9c147e Ilias Tsitsimpis
        catalog = endpoints['access']['serviceCatalog']
1041 2c9c147e Ilias Tsitsimpis
        if ep_name is not None:
1042 2c9c147e Ilias Tsitsimpis
            catalog = \
1043 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['name'] == ep_name]
1044 2c9c147e Ilias Tsitsimpis
        if ep_type is not None:
1045 2c9c147e Ilias Tsitsimpis
            catalog = \
1046 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['type'] == ep_type]
1047 2c9c147e Ilias Tsitsimpis
        if ep_region is not None:
1048 2c9c147e Ilias Tsitsimpis
            for c in catalog:
1049 2c9c147e Ilias Tsitsimpis
                c['endpoints'] = [e for e in c['endpoints']
1050 2c9c147e Ilias Tsitsimpis
                                  if e['region'] == ep_region]
1051 2c9c147e Ilias Tsitsimpis
            # Remove catalog entries with no endpoints
1052 2c9c147e Ilias Tsitsimpis
            catalog = \
1053 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['endpoints']]
1054 2c9c147e Ilias Tsitsimpis
        if ep_version_id is not None:
1055 2c9c147e Ilias Tsitsimpis
            for c in catalog:
1056 2c9c147e Ilias Tsitsimpis
                c['endpoints'] = [e for e in c['endpoints']
1057 2c9c147e Ilias Tsitsimpis
                                  if e['versionId'] == ep_version_id]
1058 2c9c147e Ilias Tsitsimpis
            # Remove catalog entries with no endpoints
1059 2c9c147e Ilias Tsitsimpis
            catalog = \
1060 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['endpoints']]
1061 2c9c147e Ilias Tsitsimpis
1062 2c9c147e Ilias Tsitsimpis
        if not catalog:
1063 2c9c147e Ilias Tsitsimpis
            raise NoEndpoints(ep_name, ep_type,
1064 2c9c147e Ilias Tsitsimpis
                              ep_region, ep_version_id)
1065 2c9c147e Ilias Tsitsimpis
        else:
1066 2c9c147e Ilias Tsitsimpis
            return catalog
1067 2c9c147e Ilias Tsitsimpis
    except KeyError:
1068 259a5f9a Ilias Tsitsimpis
        raise NoEndpoints(ep_name, ep_type, ep_region, ep_version_id)
1069 2c9c147e Ilias Tsitsimpis
1070 f54cf5e4 Ilias Tsitsimpis
1071 f54cf5e4 Ilias Tsitsimpis
# --------------------------------------------------------------------
1072 cbc0b438 Ilias Tsitsimpis
# Private functions
1073 45c0bcf8 Giorgos Korfiatis
# We want _do_request to be a distinct function
1074 f93cc364 Ilias Tsitsimpis
# so that we can replace it during unit tests.
1075 794c94e6 Ilias Tsitsimpis
def _do_request(conn, method, url, **kwargs):
1076 f8388a90 Ilias Tsitsimpis
    """The actual request. This function can easily be mocked"""
1077 f8388a90 Ilias Tsitsimpis
    conn.request(method, url, **kwargs)
1078 f8388a90 Ilias Tsitsimpis
    response = conn.getresponse()
1079 f8388a90 Ilias Tsitsimpis
    length = response.getheader('content-length', None)
1080 f8388a90 Ilias Tsitsimpis
    data = response.read(length)
1081 f8388a90 Ilias Tsitsimpis
    status = int(response.status)
1082 21190887 Ilias Tsitsimpis
    message = response.reason
1083 21190887 Ilias Tsitsimpis
    return (message, data, status)