Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / astakos / __init__.py @ f8636965

History | View | Annotate | Download (11.1 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2012-2013 GRNET S.A. All rights reserved.
2 43ca98ee Giorgos Verigakis
#
3 43ca98ee Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 43ca98ee Giorgos Verigakis
# without modification, are permitted provided that the following
5 43ca98ee Giorgos Verigakis
# conditions are met:
6 43ca98ee Giorgos Verigakis
#
7 43ca98ee Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 43ca98ee Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 43ca98ee Giorgos Verigakis
#      disclaimer.
10 43ca98ee Giorgos Verigakis
#
11 43ca98ee Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 43ca98ee Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 43ca98ee Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 43ca98ee Giorgos Verigakis
#      provided with the distribution.
15 43ca98ee Giorgos Verigakis
#
16 43ca98ee Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 43ca98ee Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 43ca98ee Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 43ca98ee Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 43ca98ee Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 43ca98ee Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 43ca98ee Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 43ca98ee Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 43ca98ee Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 43ca98ee Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 43ca98ee Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 43ca98ee Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 43ca98ee Giorgos Verigakis
#
29 43ca98ee Giorgos Verigakis
# The views and conclusions contained in the software and
30 43ca98ee Giorgos Verigakis
# documentation are those of the authors and should not be
31 43ca98ee Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 43ca98ee Giorgos Verigakis
# or implied, of GRNET S.A.
33 43ca98ee Giorgos Verigakis
34 528550d9 Stavros Sachtouris
from logging import getLogger
35 14b5cd0e Stavros Sachtouris
from astakosclient import AstakosClient as OriginalAstakosClient
36 14b5cd0e Stavros Sachtouris
from astakosclient import AstakosClientException, parse_endpoints
37 9d3cd179 Stavros Sachtouris
38 20211cbb Stavros Sachtouris
from kamaki.clients import Client, ClientError, RequestManager, recvlog
39 528550d9 Stavros Sachtouris
40 528550d9 Stavros Sachtouris
41 14b5cd0e Stavros Sachtouris
class AstakosClient(OriginalAstakosClient):
42 14b5cd0e Stavros Sachtouris
    """Wrap Original AstakosClient to ensure compatibility in kamaki clients"""
43 14b5cd0e Stavros Sachtouris
44 14b5cd0e Stavros Sachtouris
    def __init__(self, *args, **kwargs):
45 14b5cd0e Stavros Sachtouris
        if args:
46 14b5cd0e Stavros Sachtouris
            args = list(args)
47 14b5cd0e Stavros Sachtouris
            url = args.pop(0)
48 14b5cd0e Stavros Sachtouris
            token = args.pop(0) if args else kwargs.pop('token', None)
49 14b5cd0e Stavros Sachtouris
            args = tuple([token, url] + args)
50 14b5cd0e Stavros Sachtouris
        elif 'base_url' in kwargs:
51 14b5cd0e Stavros Sachtouris
            kwargs['auth_url'] = kwargs.get('auth_url', kwargs['base_url'])
52 14b5cd0e Stavros Sachtouris
        super(AstakosClient, self).__init__(*args, **kwargs)
53 14b5cd0e Stavros Sachtouris
54 eac1777c Stavros Sachtouris
    def get_service_endpoints(self, service_type, version=None):
55 eac1777c Stavros Sachtouris
        services = parse_endpoints(
56 eac1777c Stavros Sachtouris
            self.get_endpoints(), ep_type=service_type, ep_version_id=version)
57 eac1777c Stavros Sachtouris
        return services[0]['endpoints'][0] if services else []
58 eac1777c Stavros Sachtouris
59 f8636965 Stavros Sachtouris
    @property
60 f8636965 Stavros Sachtouris
    def user_info(self):
61 f8636965 Stavros Sachtouris
        return self.authenticate()['access']['user']
62 f8636965 Stavros Sachtouris
63 f8636965 Stavros Sachtouris
    def user_term(self, term):
64 f8636965 Stavros Sachtouris
        return self.user_info[term]
65 f8636965 Stavros Sachtouris
66 ec8cd658 Stavros Sachtouris
67 fa7d08b6 Stavros Sachtouris
def _astakos_error(foo):
68 fa7d08b6 Stavros Sachtouris
    def wrap(self, *args, **kwargs):
69 fa7d08b6 Stavros Sachtouris
        try:
70 fa7d08b6 Stavros Sachtouris
            return foo(self, *args, **kwargs)
71 36cb577e Stavros Sachtouris
        except AstakosClientException as sace:
72 fa7d08b6 Stavros Sachtouris
            self._raise_for_status(sace)
73 fa7d08b6 Stavros Sachtouris
    return wrap
74 fa7d08b6 Stavros Sachtouris
75 fa7d08b6 Stavros Sachtouris
76 14b5cd0e Stavros Sachtouris
class LoggedAstakosClient(AstakosClient):
77 14b5cd0e Stavros Sachtouris
    """An AstakosClient wrapper with modified logging
78 14b5cd0e Stavros Sachtouris

79 14b5cd0e Stavros Sachtouris
    Logs are adjusted to appear similar to the ones of kamaki clients.
80 14b5cd0e Stavros Sachtouris
    No other changes are made to the parent class.
81 14b5cd0e Stavros Sachtouris
    """
82 36cb577e Stavros Sachtouris
83 36cb577e Stavros Sachtouris
    LOG_TOKEN = False
84 36cb577e Stavros Sachtouris
    LOG_DATA = False
85 85115c12 Stavros Sachtouris
86 85115c12 Stavros Sachtouris
    def _dump_response(self, request, status, message, data):
87 20211cbb Stavros Sachtouris
        recvlog.info('\n%d %s' % (status, message))
88 20211cbb Stavros Sachtouris
        recvlog.info('data size: %s' % len(data))
89 36cb577e Stavros Sachtouris
        if not self.LOG_TOKEN:
90 36cb577e Stavros Sachtouris
            token = request.headers.get('X-Auth-Token', '')
91 36cb577e Stavros Sachtouris
            if self.LOG_DATA:
92 36cb577e Stavros Sachtouris
                data = data.replace(token, '...') if token else data
93 36cb577e Stavros Sachtouris
        if self.LOG_DATA:
94 36cb577e Stavros Sachtouris
            recvlog.info(data)
95 2d1f5058 Stavros Sachtouris
        recvlog.info('-             -        -     -   -  - -')
96 85115c12 Stavros Sachtouris
97 85115c12 Stavros Sachtouris
    def _call_astakos(self, *args, **kwargs):
98 14b5cd0e Stavros Sachtouris
        r = super(LoggedAstakosClient, self)._call_astakos(*args, **kwargs)
99 85115c12 Stavros Sachtouris
        try:
100 85115c12 Stavros Sachtouris
            log_request = getattr(self, 'log_request', None)
101 85115c12 Stavros Sachtouris
            if log_request:
102 85115c12 Stavros Sachtouris
                req = RequestManager(
103 85115c12 Stavros Sachtouris
                    method=log_request['method'],
104 85115c12 Stavros Sachtouris
                    url='%s://%s' % (self.scheme, self.astakos_base_url),
105 85115c12 Stavros Sachtouris
                    path=log_request['path'],
106 85115c12 Stavros Sachtouris
                    data=log_request.get('body', None),
107 85115c12 Stavros Sachtouris
                    headers=log_request.get('headers', dict()))
108 36cb577e Stavros Sachtouris
                req.LOG_TOKEN, req.LOG_DATA = self.LOG_TOKEN, self.LOG_DATA
109 85115c12 Stavros Sachtouris
                req.dump_log()
110 85115c12 Stavros Sachtouris
                log_response = getattr(self, 'log_response', None)
111 85115c12 Stavros Sachtouris
                if log_response:
112 85115c12 Stavros Sachtouris
                    self._dump_response(
113 85115c12 Stavros Sachtouris
                        req,
114 85115c12 Stavros Sachtouris
                        status=log_response['status'],
115 85115c12 Stavros Sachtouris
                        message=log_response['message'],
116 85115c12 Stavros Sachtouris
                        data=log_response.get('data', ''))
117 20211cbb Stavros Sachtouris
        except Exception:
118 20211cbb Stavros Sachtouris
            pass
119 85115c12 Stavros Sachtouris
        finally:
120 85115c12 Stavros Sachtouris
            return r
121 85115c12 Stavros Sachtouris
122 85115c12 Stavros Sachtouris
123 36cb577e Stavros Sachtouris
class CachedAstakosClient(Client):
124 cabc72ae Stavros Sachtouris
    """Synnefo Astakos cached client wraper"""
125 44b8928d Giorgos Verigakis
126 fa7d08b6 Stavros Sachtouris
    @_astakos_error
127 528550d9 Stavros Sachtouris
    def __init__(self, base_url, token=None):
128 36cb577e Stavros Sachtouris
        super(CachedAstakosClient, self).__init__(base_url, token)
129 cabc72ae Stavros Sachtouris
        self._astakos = dict()
130 cabc72ae Stavros Sachtouris
        self._uuids = dict()
131 cabc72ae Stavros Sachtouris
        self._cache = dict()
132 cabc72ae Stavros Sachtouris
        self._uuids2usernames = dict()
133 cabc72ae Stavros Sachtouris
        self._usernames2uuids = dict()
134 cabc72ae Stavros Sachtouris
135 cabc72ae Stavros Sachtouris
    def _resolve_token(self, token):
136 cabc72ae Stavros Sachtouris
        """
137 cabc72ae Stavros Sachtouris
        :returns: (str) a single token
138 cabc72ae Stavros Sachtouris

139 cabc72ae Stavros Sachtouris
        :raises AssertionError: if no token exists (either param or member)
140 cabc72ae Stavros Sachtouris
        """
141 91478081 Stavros Sachtouris
        token = token or self.token
142 cabc72ae Stavros Sachtouris
        assert token, 'No token provided'
143 cabc72ae Stavros Sachtouris
        return token[0] if (
144 cabc72ae Stavros Sachtouris
            isinstance(token, list) or isinstance(token, tuple)) else token
145 cabc72ae Stavros Sachtouris
146 172ee8f9 Stavros Sachtouris
    def get_client(self, token=None):
147 172ee8f9 Stavros Sachtouris
        """Get the Synnefo AstakosClient instance used by client"""
148 172ee8f9 Stavros Sachtouris
        token = self._resolve_token(token)
149 172ee8f9 Stavros Sachtouris
        self._validate_token(token)
150 172ee8f9 Stavros Sachtouris
        return self._astakos[self._uuids[token]]
151 172ee8f9 Stavros Sachtouris
152 fa7d08b6 Stavros Sachtouris
    @_astakos_error
153 0238c167 Stavros Sachtouris
    def authenticate(self, token=None):
154 409371de Stavros Sachtouris
        """Get authentication information and store it in this client
155 36cb577e Stavros Sachtouris
        As long as the CachedAstakosClient instance is alive, the latest
156 409371de Stavros Sachtouris
        authentication information for this token will be available
157 409371de Stavros Sachtouris

158 0d79eb23 Stavros Sachtouris
        :param token: (str) custom token to authenticate
159 0d79eb23 Stavros Sachtouris
        """
160 cabc72ae Stavros Sachtouris
        token = self._resolve_token(token)
161 14b5cd0e Stavros Sachtouris
        astakos = LoggedAstakosClient(
162 14b5cd0e Stavros Sachtouris
            self.base_url, token, logger=getLogger('astakosclient'))
163 36cb577e Stavros Sachtouris
        astakos.LOG_TOKEN = getattr(self, 'LOG_TOKEN', False)
164 36cb577e Stavros Sachtouris
        astakos.LOG_DATA = getattr(self, 'LOG_DATA', False)
165 fa7d08b6 Stavros Sachtouris
        r = astakos.authenticate()
166 b44a5a37 Stavros Sachtouris
        uuid = r['access']['user']['id']
167 db93db3e Stavros Sachtouris
        self._uuids[token] = uuid
168 b44a5a37 Stavros Sachtouris
        self._cache[uuid] = r
169 cabc72ae Stavros Sachtouris
        self._astakos[uuid] = astakos
170 91478081 Stavros Sachtouris
        self._uuids2usernames[uuid] = dict()
171 91478081 Stavros Sachtouris
        self._usernames2uuids[uuid] = dict()
172 f24819ad Stavros Sachtouris
        return self._cache[uuid]
173 b44a5a37 Stavros Sachtouris
174 fa7d08b6 Stavros Sachtouris
    def remove_user(self, uuid):
175 fa7d08b6 Stavros Sachtouris
        self._uuids.pop(self.get_token(uuid))
176 fa7d08b6 Stavros Sachtouris
        self._cache.pop(uuid)
177 fa7d08b6 Stavros Sachtouris
        self._astakos.pop(uuid)
178 fa7d08b6 Stavros Sachtouris
        self._uuids2usernames.pop(uuid)
179 fa7d08b6 Stavros Sachtouris
        self._usernames2uuids.pop(uuid)
180 fa7d08b6 Stavros Sachtouris
181 b44a5a37 Stavros Sachtouris
    def get_token(self, uuid):
182 b44a5a37 Stavros Sachtouris
        return self._cache[uuid]['access']['token']['id']
183 5207c784 Stavros Sachtouris
184 cabc72ae Stavros Sachtouris
    def _validate_token(self, token):
185 cabc72ae Stavros Sachtouris
        if (token not in self._uuids) or (
186 cabc72ae Stavros Sachtouris
                self.get_token(self._uuids[token]) != token):
187 cabc72ae Stavros Sachtouris
            self._uuids.pop(token, None)
188 cabc72ae Stavros Sachtouris
            self.authenticate(token)
189 cabc72ae Stavros Sachtouris
190 528550d9 Stavros Sachtouris
    def get_services(self, token=None):
191 528550d9 Stavros Sachtouris
        """
192 528550d9 Stavros Sachtouris
        :returns: (list) [{name:..., type:..., endpoints:[...]}, ...]
193 528550d9 Stavros Sachtouris
        """
194 cabc72ae Stavros Sachtouris
        token = self._resolve_token(token)
195 cabc72ae Stavros Sachtouris
        self._validate_token(token)
196 cabc72ae Stavros Sachtouris
        r = self._cache[self._uuids[token]]
197 3950a864 Stavros Sachtouris
        return r['access']['serviceCatalog']
198 528550d9 Stavros Sachtouris
199 528550d9 Stavros Sachtouris
    def get_service_details(self, service_type, token=None):
200 528550d9 Stavros Sachtouris
        """
201 528550d9 Stavros Sachtouris
        :param service_type: (str) compute, object-store, image, account, etc.
202 528550d9 Stavros Sachtouris

203 528550d9 Stavros Sachtouris
        :returns: (dict) {name:..., type:..., endpoints:[...]}
204 528550d9 Stavros Sachtouris

205 528550d9 Stavros Sachtouris
        :raises ClientError: (600) if service_type not in service catalog
206 528550d9 Stavros Sachtouris
        """
207 528550d9 Stavros Sachtouris
        services = self.get_services(token)
208 528550d9 Stavros Sachtouris
        for service in services:
209 528550d9 Stavros Sachtouris
            try:
210 528550d9 Stavros Sachtouris
                if service['type'].lower() == service_type.lower():
211 528550d9 Stavros Sachtouris
                    return service
212 528550d9 Stavros Sachtouris
            except KeyError:
213 528550d9 Stavros Sachtouris
                self.log.warning('Misformated service %s' % service)
214 528550d9 Stavros Sachtouris
        raise ClientError(
215 528550d9 Stavros Sachtouris
            'Service type "%s" not in service catalog' % service_type, 600)
216 528550d9 Stavros Sachtouris
217 528550d9 Stavros Sachtouris
    def get_service_endpoints(self, service_type, version=None, token=None):
218 528550d9 Stavros Sachtouris
        """
219 528550d9 Stavros Sachtouris
        :param service_type: (str) can be compute, object-store, etc.
220 528550d9 Stavros Sachtouris

221 528550d9 Stavros Sachtouris
        :param version: (str) the version id of the service
222 528550d9 Stavros Sachtouris

223 528550d9 Stavros Sachtouris
        :returns: (dict) {SNF:uiURL, adminURL, internalURL, publicURL, ...}
224 528550d9 Stavros Sachtouris

225 528550d9 Stavros Sachtouris
        :raises ClientError: (600) if service_type not in service catalog
226 528550d9 Stavros Sachtouris

227 528550d9 Stavros Sachtouris
        :raises ClientError: (601) if #matching endpoints != 1
228 528550d9 Stavros Sachtouris
        """
229 528550d9 Stavros Sachtouris
        service = self.get_service_details(service_type, token)
230 528550d9 Stavros Sachtouris
        matches = []
231 528550d9 Stavros Sachtouris
        for endpoint in service['endpoints']:
232 528550d9 Stavros Sachtouris
            if (not version) or (
233 e7884f25 Stavros Sachtouris
                    endpoint['versionId'].lower() == version.lower()):
234 528550d9 Stavros Sachtouris
                matches.append(endpoint)
235 528550d9 Stavros Sachtouris
        if len(matches) != 1:
236 528550d9 Stavros Sachtouris
            raise ClientError(
237 528550d9 Stavros Sachtouris
                '%s endpoints match type %s %s' % (
238 528550d9 Stavros Sachtouris
                    len(matches), service_type,
239 e7884f25 Stavros Sachtouris
                    ('and versionId %s' % version) if version else ''),
240 528550d9 Stavros Sachtouris
                601)
241 528550d9 Stavros Sachtouris
        return matches[0]
242 528550d9 Stavros Sachtouris
243 528550d9 Stavros Sachtouris
    def list_users(self):
244 528550d9 Stavros Sachtouris
        """list cached users information"""
245 9a8861d1 Stavros Sachtouris
        if not self._cache:
246 9a8861d1 Stavros Sachtouris
            self.authenticate()
247 2182231b Stavros Sachtouris
        r = []
248 2182231b Stavros Sachtouris
        for k, v in self._cache.items():
249 3950a864 Stavros Sachtouris
            r.append(dict(v['access']['user']))
250 b44a5a37 Stavros Sachtouris
            r[-1].update(dict(auth_token=self.get_token(k)))
251 2182231b Stavros Sachtouris
        return r
252 76d3b2d7 Stavros Sachtouris
253 528550d9 Stavros Sachtouris
    def user_info(self, token=None):
254 2182231b Stavros Sachtouris
        """Get (cached) user information"""
255 cabc72ae Stavros Sachtouris
        token = self._resolve_token(token)
256 cabc72ae Stavros Sachtouris
        self._validate_token(token)
257 cabc72ae Stavros Sachtouris
        r = self._cache[self._uuids[token]]
258 3950a864 Stavros Sachtouris
        return r['access']['user']
259 76d3b2d7 Stavros Sachtouris
260 5207c784 Stavros Sachtouris
    def term(self, key, token=None):
261 5207c784 Stavros Sachtouris
        """Get (cached) term, from user credentials"""
262 f724cd35 Stavros Sachtouris
        return self.user_term(key, token)
263 f724cd35 Stavros Sachtouris
264 f724cd35 Stavros Sachtouris
    def user_term(self, key, token=None):
265 f724cd35 Stavros Sachtouris
        """Get (cached) term, from user credentials"""
266 528550d9 Stavros Sachtouris
        return self.user_info(token).get(key, None)
267 9d3cd179 Stavros Sachtouris
268 cabc72ae Stavros Sachtouris
    def post_user_catalogs(self, uuids=None, displaynames=None, token=None):
269 9d3cd179 Stavros Sachtouris
        """POST base_url/user_catalogs
270 9d3cd179 Stavros Sachtouris

271 9d3cd179 Stavros Sachtouris
        :param uuids: (list or tuple) user uuids
272 9d3cd179 Stavros Sachtouris

273 95641ecc Stavros Sachtouris
        :param displaynames: (list or tuple) usernames (mut. excl. to uuids)
274 95641ecc Stavros Sachtouris

275 95641ecc Stavros Sachtouris
        :returns: (dict) {uuid1: name1, uuid2: name2, ...} or oposite
276 9d3cd179 Stavros Sachtouris
        """
277 cabc72ae Stavros Sachtouris
        return self.uuids2usernames(uuids, token) if (
278 fa7d08b6 Stavros Sachtouris
            uuids) else self.usernames2uuids(displaynames, token)
279 cabc72ae Stavros Sachtouris
280 fa7d08b6 Stavros Sachtouris
    @_astakos_error
281 cabc72ae Stavros Sachtouris
    def uuids2usernames(self, uuids, token=None):
282 cabc72ae Stavros Sachtouris
        token = self._resolve_token(token)
283 cabc72ae Stavros Sachtouris
        self._validate_token(token)
284 91478081 Stavros Sachtouris
        uuid = self._uuids[token]
285 91478081 Stavros Sachtouris
        astakos = self._astakos[uuid]
286 5c5eb675 Stavros Sachtouris
        if set(uuids or []).difference(self._uuids2usernames[uuid]):
287 91478081 Stavros Sachtouris
            self._uuids2usernames[uuid].update(astakos.get_usernames(uuids))
288 91478081 Stavros Sachtouris
        return self._uuids2usernames[uuid]
289 cabc72ae Stavros Sachtouris
290 fa7d08b6 Stavros Sachtouris
    @_astakos_error
291 cabc72ae Stavros Sachtouris
    def usernames2uuids(self, usernames, token=None):
292 cabc72ae Stavros Sachtouris
        token = self._resolve_token(token)
293 cabc72ae Stavros Sachtouris
        self._validate_token(token)
294 91478081 Stavros Sachtouris
        uuid = self._uuids[token]
295 91478081 Stavros Sachtouris
        astakos = self._astakos[uuid]
296 5c5eb675 Stavros Sachtouris
        if set(usernames or []).difference(self._usernames2uuids[uuid]):
297 91478081 Stavros Sachtouris
            self._usernames2uuids[uuid].update(astakos.get_uuids(usernames))
298 91478081 Stavros Sachtouris
        return self._usernames2uuids[uuid]