Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / __init__.py @ e6a61e6a

History | View | Annotate | Download (37.4 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 45c0bcf8 Giorgos Korfiatis
    def api_commissions(self):
218 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "commissions")
219 2c9c147e Ilias Tsitsimpis
220 45c0bcf8 Giorgos Korfiatis
    @property
221 45c0bcf8 Giorgos Korfiatis
    def api_commissions_action(self):
222 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.api_commissions, "action")
223 45c0bcf8 Giorgos Korfiatis
224 45c0bcf8 Giorgos Korfiatis
    @property
225 45c0bcf8 Giorgos Korfiatis
    def api_feedback(self):
226 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "feedback")
227 45c0bcf8 Giorgos Korfiatis
228 45c0bcf8 Giorgos Korfiatis
    @property
229 45c0bcf8 Giorgos Korfiatis
    def api_projects(self):
230 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.account_prefix, "projects")
231 45c0bcf8 Giorgos Korfiatis
232 45c0bcf8 Giorgos Korfiatis
    @property
233 45c0bcf8 Giorgos Korfiatis
    def api_applications(self):
234 45c0bcf8 Giorgos Korfiatis
        return join_urls(self.api_projects, "apps")
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 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``
563 2c9c147e Ilias Tsitsimpis
    def issue_commission(self, request):
564 fd420756 Ilias Tsitsimpis
        """Issue a commission
565 fd420756 Ilias Tsitsimpis

566 fd420756 Ilias Tsitsimpis
        Keyword arguments:
567 fd420756 Ilias Tsitsimpis
        request -- commision request (dict)
568 fd420756 Ilias Tsitsimpis

569 fd420756 Ilias Tsitsimpis
        In case of success return commission's id (int).
570 fd420756 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
571 fd420756 Ilias Tsitsimpis

572 fd420756 Ilias Tsitsimpis
        """
573 fd420756 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
574 19198628 Ilias Tsitsimpis
        req_body = parse_request(request, self.logger)
575 fd420756 Ilias Tsitsimpis
        try:
576 2c9c147e Ilias Tsitsimpis
            response = self._call_astakos(self.api_commissions,
577 2c9c147e Ilias Tsitsimpis
                                          headers=req_headers,
578 2c9c147e Ilias Tsitsimpis
                                          body=req_body,
579 2c9c147e Ilias Tsitsimpis
                                          method="POST")
580 fd420756 Ilias Tsitsimpis
        except AstakosClientException as err:
581 fd420756 Ilias Tsitsimpis
            if err.status == 413:
582 fd420756 Ilias Tsitsimpis
                raise QuotaLimit(err.message, err.details)
583 fd420756 Ilias Tsitsimpis
            else:
584 fd420756 Ilias Tsitsimpis
                raise
585 fd420756 Ilias Tsitsimpis
586 fd420756 Ilias Tsitsimpis
        if "serial" in response:
587 fd420756 Ilias Tsitsimpis
            return response['serial']
588 fd420756 Ilias Tsitsimpis
        else:
589 2c9c147e Ilias Tsitsimpis
            msg = "issue_commission_core request returned %s. " + \
590 2c9c147e Ilias Tsitsimpis
                  "No serial found" % response
591 2c9c147e Ilias Tsitsimpis
            self.logger.error(msg)
592 2c9c147e Ilias Tsitsimpis
            raise AstakosClientException(msg)
593 fd420756 Ilias Tsitsimpis
594 2c9c147e Ilias Tsitsimpis
    def issue_one_commission(self, holder, source, provisions,
595 3a1bed03 Giorgos Korfiatis
                             name="", force=False, auto_accept=False):
596 12eab714 Ilias Tsitsimpis
        """Issue one commission (with specific holder and source)
597 12eab714 Ilias Tsitsimpis

598 12eab714 Ilias Tsitsimpis
        keyword arguments:
599 12eab714 Ilias Tsitsimpis
        holder      -- user's id (string)
600 12eab714 Ilias Tsitsimpis
        source      -- commission's source (ex system) (string)
601 8b68fa76 Giorgos Korfiatis
        provisions  -- resources with their quantity (dict from string to int)
602 3a1bed03 Giorgos Korfiatis
        name        -- description of the commission (string)
603 12eab714 Ilias Tsitsimpis
        force       -- force this commission (boolean)
604 12eab714 Ilias Tsitsimpis
        auto_accept -- auto accept this commission (boolean)
605 12eab714 Ilias Tsitsimpis

606 12eab714 Ilias Tsitsimpis
        In case of success return commission's id (int).
607 12eab714 Ilias Tsitsimpis
        Otherwise raise an AstakosClientException.
608 12eab714 Ilias Tsitsimpis
        (See also issue_commission)
609 12eab714 Ilias Tsitsimpis

610 12eab714 Ilias Tsitsimpis
        """
611 d5f086f2 Ilias Tsitsimpis
        check_input("issue_one_commission", self.logger,
612 d5f086f2 Ilias Tsitsimpis
                    holder=holder, source=source,
613 d5f086f2 Ilias Tsitsimpis
                    provisions=provisions)
614 12eab714 Ilias Tsitsimpis
615 12eab714 Ilias Tsitsimpis
        request = {}
616 12eab714 Ilias Tsitsimpis
        request["force"] = force
617 12eab714 Ilias Tsitsimpis
        request["auto_accept"] = auto_accept
618 3a1bed03 Giorgos Korfiatis
        request["name"] = name
619 12eab714 Ilias Tsitsimpis
        try:
620 12eab714 Ilias Tsitsimpis
            request["provisions"] = []
621 567f49a2 Giorgos Korfiatis
            for resource, quantity in provisions.iteritems():
622 2c9c147e Ilias Tsitsimpis
                prov = {"holder": holder, "source": source,
623 2c9c147e Ilias Tsitsimpis
                        "resource": resource, "quantity": quantity}
624 2c9c147e Ilias Tsitsimpis
                request["provisions"].append(prov)
625 12eab714 Ilias Tsitsimpis
        except Exception as err:
626 12eab714 Ilias Tsitsimpis
            self.logger.error(str(err))
627 12eab714 Ilias Tsitsimpis
            raise BadValue(str(err))
628 12eab714 Ilias Tsitsimpis
629 2c9c147e Ilias Tsitsimpis
        return self.issue_commission(request)
630 12eab714 Ilias Tsitsimpis
631 7a0180ef Ilias Tsitsimpis
    # ----------------------------------
632 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``
633 2c9c147e Ilias Tsitsimpis
    def get_pending_commissions(self):
634 7a0180ef Ilias Tsitsimpis
        """Get Pending Commissions
635 7a0180ef Ilias Tsitsimpis

636 7a0180ef Ilias Tsitsimpis
        In case of success return a list of pending commissions' ids
637 7a0180ef Ilias Tsitsimpis
        (list of integers)
638 7a0180ef Ilias Tsitsimpis

639 7a0180ef Ilias Tsitsimpis
        """
640 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_commissions)
641 7a0180ef Ilias Tsitsimpis
642 994f37b6 Ilias Tsitsimpis
    # ----------------------------------
643 7b5a37fd Ilias Tsitsimpis
    # do a GET to ``API_COMMISSIONS``/<serial>
644 2c9c147e Ilias Tsitsimpis
    def get_commission_info(self, serial):
645 994f37b6 Ilias Tsitsimpis
        """Get Description of a Commission
646 994f37b6 Ilias Tsitsimpis

647 994f37b6 Ilias Tsitsimpis
        Keyword arguments:
648 994f37b6 Ilias Tsitsimpis
        serial  -- commission's id (int)
649 994f37b6 Ilias Tsitsimpis

650 994f37b6 Ilias Tsitsimpis
        In case of success return a dict of dicts containing
651 5c418e94 Ilias Tsitsimpis
        informations (details) about the requested commission
652 994f37b6 Ilias Tsitsimpis

653 994f37b6 Ilias Tsitsimpis
        """
654 10797183 Ilias Tsitsimpis
        check_input("get_commission_info", self.logger, serial=serial)
655 994f37b6 Ilias Tsitsimpis
656 2c9c147e Ilias Tsitsimpis
        path = self.api_commissions.rstrip('/') + "/" + str(serial)
657 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
658 994f37b6 Ilias Tsitsimpis
659 805e294c Ilias Tsitsimpis
    # ----------------------------------
660 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS``/<serial>/action"
661 2c9c147e Ilias Tsitsimpis
    def commission_action(self, serial, action):
662 b5008ef0 Ilias Tsitsimpis
        """Perform a commission action
663 805e294c Ilias Tsitsimpis

664 805e294c Ilias Tsitsimpis
        Keyword arguments:
665 805e294c Ilias Tsitsimpis
        serial  -- commission's id (int)
666 805e294c Ilias Tsitsimpis
        action  -- action to perform, currently accept/reject (string)
667 805e294c Ilias Tsitsimpis

668 805e294c Ilias Tsitsimpis
        In case of success return nothing.
669 805e294c Ilias Tsitsimpis

670 805e294c Ilias Tsitsimpis
        """
671 10797183 Ilias Tsitsimpis
        check_input("commission_action", self.logger,
672 10797183 Ilias Tsitsimpis
                    serial=serial, action=action)
673 805e294c Ilias Tsitsimpis
674 2c9c147e Ilias Tsitsimpis
        path = self.api_commissions.rstrip('/') + "/" + str(serial) + "/action"
675 805e294c Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
676 19198628 Ilias Tsitsimpis
        req_body = parse_request({str(action): ""}, self.logger)
677 2c9c147e Ilias Tsitsimpis
        self._call_astakos(path, headers=req_headers,
678 2c9c147e Ilias Tsitsimpis
                           body=req_body, method="POST")
679 805e294c Ilias Tsitsimpis
680 2c9c147e Ilias Tsitsimpis
    def accept_commission(self, serial):
681 b5008ef0 Ilias Tsitsimpis
        """Accept a commission (see commission_action)"""
682 2c9c147e Ilias Tsitsimpis
        self.commission_action(serial, "accept")
683 805e294c Ilias Tsitsimpis
684 2c9c147e Ilias Tsitsimpis
    def reject_commission(self, serial):
685 b5008ef0 Ilias Tsitsimpis
        """Reject a commission (see commission_action)"""
686 2c9c147e Ilias Tsitsimpis
        self.commission_action(serial, "reject")
687 805e294c Ilias Tsitsimpis
688 81875157 Ilias Tsitsimpis
    # ----------------------------------
689 7b5a37fd Ilias Tsitsimpis
    # do a POST to ``API_COMMISSIONS_ACTION``
690 2c9c147e Ilias Tsitsimpis
    def resolve_commissions(self, accept_serials, reject_serials):
691 81875157 Ilias Tsitsimpis
        """Resolve multiple commissions at once
692 81875157 Ilias Tsitsimpis

693 81875157 Ilias Tsitsimpis
        Keyword arguments:
694 81875157 Ilias Tsitsimpis
        accept_serials  -- commissions to accept (list of ints)
695 81875157 Ilias Tsitsimpis
        reject_serials  -- commissions to reject (list of ints)
696 81875157 Ilias Tsitsimpis

697 81875157 Ilias Tsitsimpis
        In case of success return a dict of dicts describing which
698 81875157 Ilias Tsitsimpis
        commissions accepted, which rejected and which failed to
699 81875157 Ilias Tsitsimpis
        resolved.
700 81875157 Ilias Tsitsimpis

701 81875157 Ilias Tsitsimpis
        """
702 d5f086f2 Ilias Tsitsimpis
        check_input("resolve_commissions", self.logger,
703 d5f086f2 Ilias Tsitsimpis
                    accept_serials=accept_serials,
704 d5f086f2 Ilias Tsitsimpis
                    reject_serials=reject_serials)
705 81875157 Ilias Tsitsimpis
706 81875157 Ilias Tsitsimpis
        req_headers = {'content-type': 'application/json'}
707 81875157 Ilias Tsitsimpis
        req_body = parse_request({"accept": accept_serials,
708 81875157 Ilias Tsitsimpis
                                  "reject": reject_serials},
709 81875157 Ilias Tsitsimpis
                                 self.logger)
710 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_commissions_action,
711 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body,
712 2c9c147e Ilias Tsitsimpis
                                  method="POST")
713 81875157 Ilias Tsitsimpis
714 47bb45c0 Giorgos Korfiatis
    # ----------------------------
715 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_PROJECTS``
716 2c9c147e Ilias Tsitsimpis
    def get_projects(self, name=None, state=None, owner=None):
717 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible projects
718 47bb45c0 Giorgos Korfiatis

719 47bb45c0 Giorgos Korfiatis
        Arguments:
720 47bb45c0 Giorgos Korfiatis
        name  -- filter by name (optional)
721 47bb45c0 Giorgos Korfiatis
        state -- filter by state (optional)
722 47bb45c0 Giorgos Korfiatis
        owner -- filter by owner (optional)
723 47bb45c0 Giorgos Korfiatis

724 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of project descriptions.
725 47bb45c0 Giorgos Korfiatis
        """
726 47bb45c0 Giorgos Korfiatis
        filters = {}
727 47bb45c0 Giorgos Korfiatis
        if name is not None:
728 47bb45c0 Giorgos Korfiatis
            filters["name"] = name
729 47bb45c0 Giorgos Korfiatis
        if state is not None:
730 47bb45c0 Giorgos Korfiatis
            filters["state"] = state
731 47bb45c0 Giorgos Korfiatis
        if owner is not None:
732 47bb45c0 Giorgos Korfiatis
            filters["owner"] = owner
733 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
734 47bb45c0 Giorgos Korfiatis
        req_body = (parse_request({"filter": filters}, self.logger)
735 47bb45c0 Giorgos Korfiatis
                    if filters else None)
736 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_projects,
737 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body)
738 47bb45c0 Giorgos Korfiatis
739 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
740 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_PROJECTS``/<project_id>
741 2c9c147e Ilias Tsitsimpis
    def get_project(self, project_id):
742 47bb45c0 Giorgos Korfiatis
        """Retrieve project description, if accessible
743 47bb45c0 Giorgos Korfiatis

744 47bb45c0 Giorgos Korfiatis
        Arguments:
745 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
746 47bb45c0 Giorgos Korfiatis

747 47bb45c0 Giorgos Korfiatis
        In case of success, return project description.
748 47bb45c0 Giorgos Korfiatis
        """
749 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
750 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
751 47bb45c0 Giorgos Korfiatis
752 47bb45c0 Giorgos Korfiatis
    # -----------------------------
753 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``
754 2c9c147e Ilias Tsitsimpis
    def create_project(self, specs):
755 47bb45c0 Giorgos Korfiatis
        """Submit application to create a new project
756 47bb45c0 Giorgos Korfiatis

757 47bb45c0 Giorgos Korfiatis
        Arguments:
758 47bb45c0 Giorgos Korfiatis
        specs -- dict describing a project
759 47bb45c0 Giorgos Korfiatis

760 47bb45c0 Giorgos Korfiatis
        In case of success, return project and application identifiers.
761 47bb45c0 Giorgos Korfiatis
        """
762 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
763 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(specs, self.logger)
764 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_projects,
765 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body,
766 2c9c147e Ilias Tsitsimpis
                                  method="POST")
767 47bb45c0 Giorgos Korfiatis
768 47bb45c0 Giorgos Korfiatis
    # ------------------------------------------
769 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``/<project_id>
770 2c9c147e Ilias Tsitsimpis
    def modify_project(self, project_id, specs):
771 47bb45c0 Giorgos Korfiatis
        """Submit application to modify an existing project
772 47bb45c0 Giorgos Korfiatis

773 47bb45c0 Giorgos Korfiatis
        Arguments:
774 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
775 47bb45c0 Giorgos Korfiatis
        specs      -- dict describing a project
776 47bb45c0 Giorgos Korfiatis

777 47bb45c0 Giorgos Korfiatis
        In case of success, return project and application identifiers.
778 47bb45c0 Giorgos Korfiatis
        """
779 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
780 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
781 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(specs, self.logger)
782 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
783 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
784 47bb45c0 Giorgos Korfiatis
785 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
786 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_PROJECTS``/<project_id>/action
787 2c9c147e Ilias Tsitsimpis
    def project_action(self, project_id, action, reason=""):
788 47bb45c0 Giorgos Korfiatis
        """Perform action on a project
789 47bb45c0 Giorgos Korfiatis

790 47bb45c0 Giorgos Korfiatis
        Arguments:
791 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
792 47bb45c0 Giorgos Korfiatis
        action     -- action to perform, one of "suspend", "unsuspend",
793 47bb45c0 Giorgos Korfiatis
                      "terminate", "reinstate"
794 47bb45c0 Giorgos Korfiatis
        reason     -- reason of performing the action
795 47bb45c0 Giorgos Korfiatis

796 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
797 47bb45c0 Giorgos Korfiatis
        """
798 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_projects, str(project_id))
799 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
800 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
801 47bb45c0 Giorgos Korfiatis
        req_body = parse_request({action: reason}, self.logger)
802 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
803 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
804 47bb45c0 Giorgos Korfiatis
805 47bb45c0 Giorgos Korfiatis
    # --------------------------------
806 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_APPLICATIONS``
807 2c9c147e Ilias Tsitsimpis
    def get_applications(self, project=None):
808 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible applications
809 47bb45c0 Giorgos Korfiatis

810 47bb45c0 Giorgos Korfiatis
        Arguments:
811 47bb45c0 Giorgos Korfiatis
        project -- filter by project (optional)
812 47bb45c0 Giorgos Korfiatis

813 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of application descriptions.
814 47bb45c0 Giorgos Korfiatis
        """
815 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
816 47bb45c0 Giorgos Korfiatis
        body = {"project": project} if project is not None else None
817 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger) if body else None
818 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_applications,
819 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body)
820 47bb45c0 Giorgos Korfiatis
821 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
822 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_APPLICATIONS``/<app_id>
823 2c9c147e Ilias Tsitsimpis
    def get_application(self, app_id):
824 47bb45c0 Giorgos Korfiatis
        """Retrieve application description, if accessible
825 47bb45c0 Giorgos Korfiatis

826 47bb45c0 Giorgos Korfiatis
        Arguments:
827 47bb45c0 Giorgos Korfiatis
        app_id -- application identifier
828 47bb45c0 Giorgos Korfiatis

829 47bb45c0 Giorgos Korfiatis
        In case of success, return application description.
830 47bb45c0 Giorgos Korfiatis
        """
831 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_applications, str(app_id))
832 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
833 47bb45c0 Giorgos Korfiatis
834 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
835 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_APPLICATIONS``/<app_id>/action
836 2c9c147e Ilias Tsitsimpis
    def application_action(self, app_id, action, reason=""):
837 47bb45c0 Giorgos Korfiatis
        """Perform action on an application
838 47bb45c0 Giorgos Korfiatis

839 47bb45c0 Giorgos Korfiatis
        Arguments:
840 47bb45c0 Giorgos Korfiatis
        app_id -- application identifier
841 47bb45c0 Giorgos Korfiatis
        action -- action to perform, one of "approve", "deny",
842 47bb45c0 Giorgos Korfiatis
                  "dismiss", "cancel"
843 47bb45c0 Giorgos Korfiatis
        reason -- reason of performing the action
844 47bb45c0 Giorgos Korfiatis

845 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
846 47bb45c0 Giorgos Korfiatis
        """
847 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_applications, str(app_id))
848 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
849 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
850 47bb45c0 Giorgos Korfiatis
        req_body = parse_request({action: reason}, self.logger)
851 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
852 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
853 47bb45c0 Giorgos Korfiatis
854 47bb45c0 Giorgos Korfiatis
    # -------------------------------
855 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_MEMBERSHIPS``
856 2c9c147e Ilias Tsitsimpis
    def get_memberships(self, project=None):
857 47bb45c0 Giorgos Korfiatis
        """Retrieve all accessible memberships
858 47bb45c0 Giorgos Korfiatis

859 47bb45c0 Giorgos Korfiatis
        Arguments:
860 47bb45c0 Giorgos Korfiatis
        project -- filter by project (optional)
861 47bb45c0 Giorgos Korfiatis

862 47bb45c0 Giorgos Korfiatis
        In case of success, return a list of membership descriptions.
863 47bb45c0 Giorgos Korfiatis
        """
864 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
865 47bb45c0 Giorgos Korfiatis
        body = {"project": project} if project is not None else None
866 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger) if body else None
867 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships,
868 2c9c147e Ilias Tsitsimpis
                                  headers=req_headers, body=req_body)
869 47bb45c0 Giorgos Korfiatis
870 47bb45c0 Giorgos Korfiatis
    # -----------------------------------------
871 47bb45c0 Giorgos Korfiatis
    # do a GET to ``API_MEMBERSHIPS``/<memb_id>
872 2c9c147e Ilias Tsitsimpis
    def get_membership(self, memb_id):
873 47bb45c0 Giorgos Korfiatis
        """Retrieve membership description, if accessible
874 47bb45c0 Giorgos Korfiatis

875 47bb45c0 Giorgos Korfiatis
        Arguments:
876 47bb45c0 Giorgos Korfiatis
        memb_id -- membership identifier
877 47bb45c0 Giorgos Korfiatis

878 47bb45c0 Giorgos Korfiatis
        In case of success, return membership description.
879 47bb45c0 Giorgos Korfiatis
        """
880 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_memberships, str(memb_id))
881 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path)
882 47bb45c0 Giorgos Korfiatis
883 47bb45c0 Giorgos Korfiatis
    # -------------------------------------------------
884 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``/<memb_id>/action
885 2c9c147e Ilias Tsitsimpis
    def membership_action(self, memb_id, action, reason=""):
886 47bb45c0 Giorgos Korfiatis
        """Perform action on a membership
887 47bb45c0 Giorgos Korfiatis

888 47bb45c0 Giorgos Korfiatis
        Arguments:
889 47bb45c0 Giorgos Korfiatis
        memb_id -- membership identifier
890 47bb45c0 Giorgos Korfiatis
        action  -- action to perform, one of "leave", "cancel", "accept",
891 47bb45c0 Giorgos Korfiatis
                   "reject", "remove"
892 47bb45c0 Giorgos Korfiatis
        reason  -- reason of performing the action
893 47bb45c0 Giorgos Korfiatis

894 47bb45c0 Giorgos Korfiatis
        In case of success, return nothing.
895 47bb45c0 Giorgos Korfiatis
        """
896 2c9c147e Ilias Tsitsimpis
        path = join_urls(self.api_memberships, str(memb_id))
897 47bb45c0 Giorgos Korfiatis
        path = join_urls(path, "action")
898 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
899 47bb45c0 Giorgos Korfiatis
        req_body = parse_request({action: reason}, self.logger)
900 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(path, headers=req_headers,
901 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
902 47bb45c0 Giorgos Korfiatis
903 47bb45c0 Giorgos Korfiatis
    # --------------------------------
904 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``
905 2c9c147e Ilias Tsitsimpis
    def join_project(self, project_id):
906 47bb45c0 Giorgos Korfiatis
        """Join a project
907 47bb45c0 Giorgos Korfiatis

908 47bb45c0 Giorgos Korfiatis
        Arguments:
909 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
910 47bb45c0 Giorgos Korfiatis

911 47bb45c0 Giorgos Korfiatis
        In case of success, return membership identifier.
912 47bb45c0 Giorgos Korfiatis
        """
913 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
914 47bb45c0 Giorgos Korfiatis
        body = {"join": {"project": project_id}}
915 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger)
916 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships, headers=req_headers,
917 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
918 47bb45c0 Giorgos Korfiatis
919 47bb45c0 Giorgos Korfiatis
    # --------------------------------
920 47bb45c0 Giorgos Korfiatis
    # do a POST to ``API_MEMBERSHIPS``
921 2c9c147e Ilias Tsitsimpis
    def enroll_member(self, project_id, email):
922 47bb45c0 Giorgos Korfiatis
        """Enroll a user in a project
923 47bb45c0 Giorgos Korfiatis

924 47bb45c0 Giorgos Korfiatis
        Arguments:
925 47bb45c0 Giorgos Korfiatis
        project_id -- project identifier
926 47bb45c0 Giorgos Korfiatis
        email      -- user identified by email
927 47bb45c0 Giorgos Korfiatis

928 47bb45c0 Giorgos Korfiatis
        In case of success, return membership identifier.
929 47bb45c0 Giorgos Korfiatis
        """
930 47bb45c0 Giorgos Korfiatis
        req_headers = {'content-type': 'application/json'}
931 47bb45c0 Giorgos Korfiatis
        body = {"enroll": {"project": project_id, "user": email}}
932 47bb45c0 Giorgos Korfiatis
        req_body = parse_request(body, self.logger)
933 2c9c147e Ilias Tsitsimpis
        return self._call_astakos(self.api_memberships, headers=req_headers,
934 2c9c147e Ilias Tsitsimpis
                                  body=req_body, method="POST")
935 2c9c147e Ilias Tsitsimpis
936 d2104099 Sofia Papagiannaki
    # --------------------------------
937 fe7d0186 Sofia Papagiannaki
    # do a POST to ``API_OAUTH2_TOKEN``
938 d2104099 Sofia Papagiannaki
    def get_token(self, grant_type, client_id, client_secret, **body_params):
939 75144caa Sofia Papagiannaki
        headers = {'content-type': 'application/x-www-form-urlencoded',
940 d2104099 Sofia Papagiannaki
                   'Authorization': 'Basic %s' % b64encode('%s:%s' %
941 d2104099 Sofia Papagiannaki
                                                           (client_id,
942 d2104099 Sofia Papagiannaki
                                                            client_secret))}
943 d2104099 Sofia Papagiannaki
        body_params['grant_type'] = grant_type
944 d2104099 Sofia Papagiannaki
        body = urllib.urlencode(body_params)
945 fe7d0186 Sofia Papagiannaki
        return self._call_astakos(self.api_oauth2_token, headers=headers,
946 d2104099 Sofia Papagiannaki
                                  body=body, method="POST")
947 d2104099 Sofia Papagiannaki
948 2c9c147e Ilias Tsitsimpis
949 2c9c147e Ilias Tsitsimpis
# --------------------------------------------------------------------
950 2c9c147e Ilias Tsitsimpis
# parse endpoints
951 2c9c147e Ilias Tsitsimpis
def parse_endpoints(endpoints, ep_name=None, ep_type=None,
952 2c9c147e Ilias Tsitsimpis
                    ep_region=None, ep_version_id=None):
953 2c9c147e Ilias Tsitsimpis
    """Parse endpoints server response and extract the ones needed
954 2c9c147e Ilias Tsitsimpis

955 2c9c147e Ilias Tsitsimpis
    Keyword arguments:
956 2c9c147e Ilias Tsitsimpis
    endpoints     -- the endpoints (json response from get_endpoints)
957 2c9c147e Ilias Tsitsimpis
    ep_name       -- return only endpoints with this name (optional)
958 2c9c147e Ilias Tsitsimpis
    ep_type       -- return only endpoints with this type (optional)
959 2c9c147e Ilias Tsitsimpis
    ep_region     -- return only endpoints with this region (optional)
960 2c9c147e Ilias Tsitsimpis
    ep_version_id -- return only endpoints with this versionId (optional)
961 2c9c147e Ilias Tsitsimpis

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

966 2c9c147e Ilias Tsitsimpis
    """
967 2c9c147e Ilias Tsitsimpis
    try:
968 2c9c147e Ilias Tsitsimpis
        catalog = endpoints['access']['serviceCatalog']
969 2c9c147e Ilias Tsitsimpis
        if ep_name is not None:
970 2c9c147e Ilias Tsitsimpis
            catalog = \
971 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['name'] == ep_name]
972 2c9c147e Ilias Tsitsimpis
        if ep_type is not None:
973 2c9c147e Ilias Tsitsimpis
            catalog = \
974 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['type'] == ep_type]
975 2c9c147e Ilias Tsitsimpis
        if ep_region is not None:
976 2c9c147e Ilias Tsitsimpis
            for c in catalog:
977 2c9c147e Ilias Tsitsimpis
                c['endpoints'] = [e for e in c['endpoints']
978 2c9c147e Ilias Tsitsimpis
                                  if e['region'] == ep_region]
979 2c9c147e Ilias Tsitsimpis
            # Remove catalog entries with no endpoints
980 2c9c147e Ilias Tsitsimpis
            catalog = \
981 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['endpoints']]
982 2c9c147e Ilias Tsitsimpis
        if ep_version_id is not None:
983 2c9c147e Ilias Tsitsimpis
            for c in catalog:
984 2c9c147e Ilias Tsitsimpis
                c['endpoints'] = [e for e in c['endpoints']
985 2c9c147e Ilias Tsitsimpis
                                  if e['versionId'] == ep_version_id]
986 2c9c147e Ilias Tsitsimpis
            # Remove catalog entries with no endpoints
987 2c9c147e Ilias Tsitsimpis
            catalog = \
988 2c9c147e Ilias Tsitsimpis
                [c for c in catalog if c['endpoints']]
989 2c9c147e Ilias Tsitsimpis
990 2c9c147e Ilias Tsitsimpis
        if not catalog:
991 2c9c147e Ilias Tsitsimpis
            raise NoEndpoints(ep_name, ep_type,
992 2c9c147e Ilias Tsitsimpis
                              ep_region, ep_version_id)
993 2c9c147e Ilias Tsitsimpis
        else:
994 2c9c147e Ilias Tsitsimpis
            return catalog
995 2c9c147e Ilias Tsitsimpis
    except KeyError:
996 259a5f9a Ilias Tsitsimpis
        raise NoEndpoints(ep_name, ep_type, ep_region, ep_version_id)
997 2c9c147e Ilias Tsitsimpis
998 f54cf5e4 Ilias Tsitsimpis
999 f54cf5e4 Ilias Tsitsimpis
# --------------------------------------------------------------------
1000 cbc0b438 Ilias Tsitsimpis
# Private functions
1001 45c0bcf8 Giorgos Korfiatis
# We want _do_request to be a distinct function
1002 f93cc364 Ilias Tsitsimpis
# so that we can replace it during unit tests.
1003 794c94e6 Ilias Tsitsimpis
def _do_request(conn, method, url, **kwargs):
1004 f8388a90 Ilias Tsitsimpis
    """The actual request. This function can easily be mocked"""
1005 f8388a90 Ilias Tsitsimpis
    conn.request(method, url, **kwargs)
1006 f8388a90 Ilias Tsitsimpis
    response = conn.getresponse()
1007 f8388a90 Ilias Tsitsimpis
    length = response.getheader('content-length', None)
1008 f8388a90 Ilias Tsitsimpis
    data = response.read(length)
1009 f8388a90 Ilias Tsitsimpis
    status = int(response.status)
1010 21190887 Ilias Tsitsimpis
    message = response.reason
1011 21190887 Ilias Tsitsimpis
    return (message, data, status)