__init__.py 6.21 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
36

from kamaki.clients import Client, ClientError
37
38


39
class AstakosClient(Client):
40
    """Synnefo Astakos API client"""
Giorgos Verigakis's avatar
Giorgos Verigakis committed
41

42
    def __init__(self, base_url, token=None):
43
        super(AstakosClient, self).__init__(base_url, token)
44
        self._cache = {}
45
        self._uuids = {}
46
        self.log = getLogger('__name__')
47

48
    def authenticate(self, token=None):
49
50
51
52
        """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

53
54
55
56
        :param token: (str) custom token to authenticate

        :returns: (dict) authentication information
        """
57
        self.token = token or self.token
58
        body = dict(auth=dict(token=dict(id=self.token)))
59
60
61
62
63
64
65
66
        r = self.post('/tokens', json=body).json
        uuid = r['access']['user']['id']
        self._uuids[self.token] = uuid
        self._cache[uuid] = r
        return self._cache[uuid]

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

68
69
70
71
72
73
74
    def get_services(self, token=None):
        """
        :returns: (list) [{name:..., type:..., endpoints:[...]}, ...]
        """
        token_bu = self.token or token
        token = token or self.token
        try:
75
            r = self._cache[self._uuids[token]]
76
77
78
79
        except KeyError:
            r = self.authenticate(token)
        finally:
            self.token = token_bu
80
        return r['access']['serviceCatalog']
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

    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 (
116
                    endpoint['versionId'].lower() == version.lower()):
117
118
119
120
121
                matches.append(endpoint)
        if len(matches) != 1:
            raise ClientError(
                '%s endpoints match type %s %s' % (
                    len(matches), service_type,
122
                    ('and versionId %s' % version) if version else ''),
123
124
125
126
127
                601)
        return matches[0]

    def list_users(self):
        """list cached users information"""
128
129
        if not self._cache:
            self.authenticate()
130
131
        r = []
        for k, v in self._cache.items():
132
            r.append(dict(v['access']['user']))
133
            r[-1].update(dict(auth_token=self.get_token(k)))
134
        return r
135

136
    def user_info(self, token=None):
137
        """Get (cached) user information"""
138
        token_bu = self.token or token
139
140
        token = token or self.token
        try:
141
            r = self._cache[self._uuids[token]]
142
        except KeyError:
143
            r = self.authenticate(token)
144
145
        finally:
            self.token = token_bu
146
        return r['access']['user']
147

148
    def term(self, key, token=None):
149
150
151
152
        """Get (cached) term, from user credentials"""
        return self.user_term(key, token)

    def user_term(self, key, token=None):
153
        """Get (cached) term, from user credentials"""
154
        return self.user_info(token).get(key, None)
155

156
    def post_user_catalogs(self, uuids=None, displaynames=None):
157
158
159
160
        """POST base_url/user_catalogs

        :param uuids: (list or tuple) user uuids

161
162
163
        :param displaynames: (list or tuple) usernames (mut. excl. to uuids)

        :returns: (dict) {uuid1: name1, uuid2: name2, ...} or oposite
164
165
166
        """
        account_url = self.get_service_endpoints('account')['publicURL']
        account = AstakosClient(account_url, self.token)
167
168
        json_data = dict(uuids=uuids) if (
            uuids) else dict(displaynames=displaynames)
169
        return account.post('user_catalogs', json=json_data)