__init__.py 7.61 KB
Newer Older
1
# Copyright 2012-2013 GRNET S.A. All rights reserved.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

34
from logging import getLogger
35
from astakosclient import AstakosClient as SynnefoAstakosClient
36
37

from kamaki.clients import Client, ClientError
38
39


40
class AstakosClient(Client):
41
    """Synnefo Astakos cached client wraper"""
Giorgos Verigakis's avatar
Giorgos Verigakis committed
42

43
    def __init__(self, base_url, token=None):
44
        super(AstakosClient, self).__init__(base_url, token)
45
46
47
48
49
50
51
52
53
54
55
56
        self._astakos = dict()
        self._uuids = dict()
        self._cache = dict()
        self._uuids2usernames = dict()
        self._usernames2uuids = dict()

    def _resolve_token(self, token):
        """
        :returns: (str) a single token

        :raises AssertionError: if no token exists (either param or member)
        """
57
        token = token or self.token
58
59
60
61
        assert token, 'No token provided'
        return token[0] if (
            isinstance(token, list) or isinstance(token, tuple)) else token

62
63
64
65
66
67
    def get_client(self, token=None):
        """Get the Synnefo AstakosClient instance used by client"""
        token = self._resolve_token(token)
        self._validate_token(token)
        return self._astakos[self._uuids[token]]

68
    def authenticate(self, token=None):
69
70
71
72
        """Get authentication information and store it in this client
        As long as the AstakosClient instance is alive, the latest
        authentication information for this token will be available

73
74
        :param token: (str) custom token to authenticate
        """
75
76
        token = self._resolve_token(token)
        astakos = SynnefoAstakosClient(
77
            token, self.base_url, logger=getLogger('astakosclient'))
78
        r = astakos.get_endpoints()
79
        uuid = r['access']['user']['id']
80
        self._uuids[token] = uuid
81
        self._cache[uuid] = r
82
        self._astakos[uuid] = astakos
83
84
        self._uuids2usernames[uuid] = dict()
        self._usernames2uuids[uuid] = dict()
85
        return self._cache[uuid]
86
87
88

    def get_token(self, uuid):
        return self._cache[uuid]['access']['token']['id']
89

90
91
92
93
94
95
    def _validate_token(self, token):
        if (token not in self._uuids) or (
                self.get_token(self._uuids[token]) != token):
            self._uuids.pop(token, None)
            self.authenticate(token)

96
97
98
99
    def get_services(self, token=None):
        """
        :returns: (list) [{name:..., type:..., endpoints:[...]}, ...]
        """
100
101
102
        token = self._resolve_token(token)
        self._validate_token(token)
        r = self._cache[self._uuids[token]]
103
        return r['access']['serviceCatalog']
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

    def get_service_details(self, service_type, token=None):
        """
        :param service_type: (str) compute, object-store, image, account, etc.

        :returns: (dict) {name:..., type:..., endpoints:[...]}

        :raises ClientError: (600) if service_type not in service catalog
        """
        services = self.get_services(token)
        for service in services:
            try:
                if service['type'].lower() == service_type.lower():
                    return service
            except KeyError:
                self.log.warning('Misformated service %s' % service)
        raise ClientError(
            'Service type "%s" not in service catalog' % service_type, 600)

    def get_service_endpoints(self, service_type, version=None, token=None):
        """
        :param service_type: (str) can be compute, object-store, etc.

        :param version: (str) the version id of the service

        :returns: (dict) {SNF:uiURL, adminURL, internalURL, publicURL, ...}

        :raises ClientError: (600) if service_type not in service catalog

        :raises ClientError: (601) if #matching endpoints != 1
        """
        service = self.get_service_details(service_type, token)
        matches = []
        for endpoint in service['endpoints']:
            if (not version) or (
139
                    endpoint['versionId'].lower() == version.lower()):
140
141
142
143
144
                matches.append(endpoint)
        if len(matches) != 1:
            raise ClientError(
                '%s endpoints match type %s %s' % (
                    len(matches), service_type,
145
                    ('and versionId %s' % version) if version else ''),
146
147
148
149
150
                601)
        return matches[0]

    def list_users(self):
        """list cached users information"""
151
152
        if not self._cache:
            self.authenticate()
153
154
        r = []
        for k, v in self._cache.items():
155
            r.append(dict(v['access']['user']))
156
            r[-1].update(dict(auth_token=self.get_token(k)))
157
        return r
158

159
    def user_info(self, token=None):
160
        """Get (cached) user information"""
161
162
163
        token = self._resolve_token(token)
        self._validate_token(token)
        r = self._cache[self._uuids[token]]
164
        return r['access']['user']
165

166
    def term(self, key, token=None):
167
168
169
170
        """Get (cached) term, from user credentials"""
        return self.user_term(key, token)

    def user_term(self, key, token=None):
171
        """Get (cached) term, from user credentials"""
172
        return self.user_info(token).get(key, None)
173

174
    def post_user_catalogs(self, uuids=None, displaynames=None, token=None):
175
176
177
178
        """POST base_url/user_catalogs

        :param uuids: (list or tuple) user uuids

179
180
181
        :param displaynames: (list or tuple) usernames (mut. excl. to uuids)

        :returns: (dict) {uuid1: name1, uuid2: name2, ...} or oposite
182
        """
183
184
185
186
187
188
        return self.uuids2usernames(uuids, token) if (
            uuids) else self.usernnames2uuids(displaynames, token)

    def uuids2usernames(self, uuids, token=None):
        token = self._resolve_token(token)
        self._validate_token(token)
189
190
191
192
193
        uuid = self._uuids[token]
        astakos = self._astakos[uuid]
        if set(uuids).difference(self._uuids2usernames[uuid]):
            self._uuids2usernames[uuid].update(astakos.get_usernames(uuids))
        return self._uuids2usernames[uuid]
194
195
196
197

    def usernames2uuids(self, usernames, token=None):
        token = self._resolve_token(token)
        self._validate_token(token)
198
199
200
201
202
        uuid = self._uuids[token]
        astakos = self._astakos[uuid]
        if set(usernames).difference(self._usernames2uuids[uuid]):
            self._usernames2uuids[uuid].update(astakos.get_uuids(usernames))
        return self._usernames2uuids[uuid]