Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (7.3 kB)

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

    
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"""
42

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

    
51
    def _resolve_token(self, token):
52
        """
53
        :returns: (str) a single token
54

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

    
62
    def authenticate(self, token=None):
63
        """Get authentication information and store it in this client
64
        As long as the AstakosClient instance is alive, the latest
65
        authentication information for this token will be available
66

67
        :param token: (str) custom token to authenticate
68
        """
69
        token = self._resolve_token(token)
70
        astakos = SynnefoAstakosClient(
71
            token, self.base_url, logger=getLogger('_my_.astakosclient'))
72
        r = astakos.get_endpoints()
73
        uuid = r['access']['user']['id']
74
        self._uuids[token] = uuid
75
        self._cache[uuid] = r
76
        self._astakos[uuid] = astakos
77
        self._uuids2usernames[token] = dict()
78
        self._usernames2uuids[token] = dict()
79

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

    
83
    def _validate_token(self, token):
84
        if (token not in self._uuids) or (
85
                self.get_token(self._uuids[token]) != token):
86
            self._uuids.pop(token, None)
87
            self.authenticate(token)
88

    
89
    def get_services(self, token=None):
90
        """
91
        :returns: (list) [{name:..., type:..., endpoints:[...]}, ...]
92
        """
93
        token = self._resolve_token(token)
94
        self._validate_token(token)
95
        r = self._cache[self._uuids[token]]
96
        return r['access']['serviceCatalog']
97

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

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

104
        :raises ClientError: (600) if service_type not in service catalog
105
        """
106
        services = self.get_services(token)
107
        for service in services:
108
            try:
109
                if service['type'].lower() == service_type.lower():
110
                    return service
111
            except KeyError:
112
                self.log.warning('Misformated service %s' % service)
113
        raise ClientError(
114
            'Service type "%s" not in service catalog' % service_type, 600)
115

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

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

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

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

126
        :raises ClientError: (601) if #matching endpoints != 1
127
        """
128
        service = self.get_service_details(service_type, token)
129
        matches = []
130
        for endpoint in service['endpoints']:
131
            if (not version) or (
132
                    endpoint['versionId'].lower() == version.lower()):
133
                matches.append(endpoint)
134
        if len(matches) != 1:
135
            raise ClientError(
136
                '%s endpoints match type %s %s' % (
137
                    len(matches), service_type,
138
                    ('and versionId %s' % version) if version else ''),
139
                601)
140
        return matches[0]
141

    
142
    def list_users(self):
143
        """list cached users information"""
144
        if not self._cache:
145
            self.authenticate()
146
        r = []
147
        for k, v in self._cache.items():
148
            r.append(dict(v['access']['user']))
149
            r[-1].update(dict(auth_token=self.get_token(k)))
150
        return r
151

    
152
    def user_info(self, token=None):
153
        """Get (cached) user information"""
154
        token = self._resolve_token(token)
155
        self._validate_token(token)
156
        r = self._cache[self._uuids[token]]
157
        return r['access']['user']
158

    
159
    def term(self, key, token=None):
160
        """Get (cached) term, from user credentials"""
161
        return self.user_term(key, token)
162

    
163
    def user_term(self, key, token=None):
164
        """Get (cached) term, from user credentials"""
165
        return self.user_info(token).get(key, None)
166

    
167
    def post_user_catalogs(self, uuids=None, displaynames=None, token=None):
168
        """POST base_url/user_catalogs
169

170
        :param uuids: (list or tuple) user uuids
171

172
        :param displaynames: (list or tuple) usernames (mut. excl. to uuids)
173

174
        :returns: (dict) {uuid1: name1, uuid2: name2, ...} or oposite
175
        """
176
        return self.uuids2usernames(uuids, token) if (
177
            uuids) else self.usernnames2uuids(displaynames, token)
178

    
179
    def uuids2usernames(self, uuids, token=None):
180
        token = self._resolve_token(token)
181
        self._validate_token(token)
182
        astakos = self._astakos[self._uuids[token]]
183
        if set(uuids).difference(self._uuids2usernames[token]):
184
            self._uuids2usernames[token].update(astakos.get_usernames(uuids))
185
        return self._uuids2usernames[token]
186

    
187
    def usernames2uuids(self, usernames, token=None):
188
        token = self._resolve_token(token)
189
        self._validate_token(token)
190
        astakos = self._astakos[self._uuids[token]]
191
        if set(usernames).difference(self._usernames2uuids[token]):
192
            self._usernames2uuids[token].update(astakos.get_uuids(usernames))
193
        return self._usernames2uuids