Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / astakos / __init__.py @ 0897ff84

History | View | Annotate | Download (10.9 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 cd42f8d8 Stavros Sachtouris
from astakosclient import AstakosClient as OriginalAstakosClient
36 cd42f8d8 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 cd42f8d8 Stavros Sachtouris
class AstakosClient(OriginalAstakosClient):
42 cd42f8d8 Stavros Sachtouris
    """Wrap Original AstakosClient to ensure compatibility in kamaki clients"""
43 cd42f8d8 Stavros Sachtouris
44 cd42f8d8 Stavros Sachtouris
    def __init__(self, *args, **kwargs):
45 cd42f8d8 Stavros Sachtouris
        if args:
46 cd42f8d8 Stavros Sachtouris
            args = list(args)
47 cd42f8d8 Stavros Sachtouris
            url = args.pop(0)
48 cd42f8d8 Stavros Sachtouris
            token = args.pop(0) if args else kwargs.pop('token', None)
49 cd42f8d8 Stavros Sachtouris
            args = tuple([token, url] + args)
50 cd42f8d8 Stavros Sachtouris
        elif 'base_url' in kwargs:
51 cd42f8d8 Stavros Sachtouris
            kwargs['auth_url'] = kwargs.get('auth_url', kwargs['base_url'])
52 cd42f8d8 Stavros Sachtouris
        super(AstakosClient, self).__init__(*args, **kwargs)
53 cd42f8d8 Stavros Sachtouris
54 0897ff84 Stavros Sachtouris
    def get_service_endpoints(self, service_type, version=None):
55 0897ff84 Stavros Sachtouris
        services = parse_endpoints(
56 0897ff84 Stavros Sachtouris
            self.get_endpoints(), ep_type=service_type, ep_version_id=version)
57 0897ff84 Stavros Sachtouris
        return services[0]['endpoints'][0] if services else []
58 0897ff84 Stavros Sachtouris
59 a22d311c Stavros Sachtouris
60 fa7d08b6 Stavros Sachtouris
def _astakos_error(foo):
61 fa7d08b6 Stavros Sachtouris
    def wrap(self, *args, **kwargs):
62 fa7d08b6 Stavros Sachtouris
        try:
63 fa7d08b6 Stavros Sachtouris
            return foo(self, *args, **kwargs)
64 36cb577e Stavros Sachtouris
        except AstakosClientException as sace:
65 fa7d08b6 Stavros Sachtouris
            self._raise_for_status(sace)
66 fa7d08b6 Stavros Sachtouris
    return wrap
67 fa7d08b6 Stavros Sachtouris
68 fa7d08b6 Stavros Sachtouris
69 cd42f8d8 Stavros Sachtouris
class LoggedAstakosClient(AstakosClient):
70 cd42f8d8 Stavros Sachtouris
    """An AstakosClient wrapper with modified logging
71 cd42f8d8 Stavros Sachtouris

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

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

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

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

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

214 528550d9 Stavros Sachtouris
        :param version: (str) the version id of the service
215 528550d9 Stavros Sachtouris

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

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

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

264 9d3cd179 Stavros Sachtouris
        :param uuids: (list or tuple) user uuids
265 9d3cd179 Stavros Sachtouris

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

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