Revision 8b4ba753

b/docs/developers/clients-api.rst
2 2
=====================================
3 3

  
4 4
Kamaki features a clients API for building third-party client applications that
5
communicate with OpenStack and / or Synnefo cloud services. The package is
6
called *kamaki.clients* and serves as a lib.
5
communicate with Synnefo and (in most cases) OpenStack cloud services. The package is
6
called *kamaki.clients* and serves as a library.
7 7

  
8 8
A showcase of an application built on *kamaki.clients* is *kamaki.cli*, the
9 9
command line interface of kamaki.
10 10

  
11 11
Since Synnefo services are build as OpenStack extensions, an inheritance
12
approach has been chosen for implementing clients for both. In specific,
12
approach has been chosen for implementing clients for both APIs. In specific,
13 13
the *compute*, *storage* and *image* modules are client implementations for the
14 14
OpenStack compute, OpenStack object-store and Image APIs respectively. The rest
15 15
of the modules implement the Synnefo extensions (i.e., *cyclades* and
......
19 19
Setup a client instance
20 20
-----------------------
21 21

  
22
There is a client for every API, therefore an external applications should
23
instantiate they kamaki clients they need. For example, to manage virtual
24
servers and stored objects / files, an application would probably need to
25
instantiate the CycladesClient and PithosClient respectively.
22
There is a client for every API. An external applications should instantiate
23
the kamaki clients that fit their needs.
24

  
25
For example, to manage virtual servers and stored objects / files, an
26
application would probably need the CycladesClient and PithosClient
27
respectively.
26 28

  
27 29
.. code-block:: python
28 30
    :emphasize-lines: 1
......
33 35
    from kamaki.clients.cyclades import CycladesClient
34 36
    from kamaki.clients.pithos import PithosClient
35 37

  
36
    my_cyclades_client = CycladesClient(base_url, token)
37
    my_pithos_client = PithosClient(base_url, token, account, container)
38
    cyclades = CycladesClient(computeURL, token)
39
    pithos = PithosClient(object-storeURL, token, account, container)
38 40

  
39 41
.. note:: *cyclades* and *pithos* clients inherit ComputeClient from *compute*
40 42
    and StorageClient from *storage*, respectively. Separate ComputeClient or
41 43
    StorageClient objects should be used only when implementing applications for
42 44
    strict OpenStack Compute or Storage services.
43 45

  
44
Using endpoints to get the base_url
45
-----------------------------------
46
Using endpoints to get the authentication url
47
---------------------------------------------
46 48

  
47 49
In OpenStack, each service (e.g., `compute`, `object-store`, etc.) has a number
48
of `endpoints`. These `endpoints` are URIs that are used by kamaki as
49
prefixes to form the corresponding API calls. Client applications need just
50
one of these these `endpoints`, namely the `publicURL`, which is also referred
51
to as `base_url` in kamaki client libraries.
50
of `endpoints`. These `endpoints` are URIs which are used by kamaki as prefixes
51
to form the corresponding API calls. Client applications need just one of these
52
`endpoints`, namely the `publicURL` (also referred to as `publicURL` in the
53
internals of kamaki client libraries).
52 54

  
53
Here are instructions for getting the base_url for a service::
55
Here are instructions for getting the publicURL for a service::
54 56

  
55 57
    1. From the deployment UI get the AUTHENTICATION_URL and TOKEN
56 58
        (Example 1.2)
......
58 60
        (Example 1.2)
59 61
    3. Use AstakosClient instance to get endpoints for the service of interest
60 62
        (Example 1.3)
61
    4. The 'publicURL' endpoint is the base_url we are looking for
63
    4. The 'publicURL' endpoint is the URL we are looking for
62 64
        (Example 1.3)
63 65

  
64 66
The AstakosClient is a client for the Synnefo/Astakos server. Synnefo/Astakos
65
is an identity server that implements the OpenStack identity API. Therefore, it
66
can be used to get the `base_url` values needed for initializing kamaki clients.
67
Kamaki simplifies this process with the astakos client library.
67
is an identity server that implements the OpenStack identity API and it
68
can be used to get the URLs needed for API calls URL construction. The astakos
69
kamaki client library simplifies this process.
68 70

  
69
Let's review the process with examples.
71
Let's review with a few examples.
70 72

  
71 73
First, an astakos client must be initialized (Example 1.2). An
72 74
AUTHENTICATION_URL and a TOKEN can be acquired from the Synnefo deployment UI.
......
77 79
    Example 1.2: Initialize an astakos client
78 80

  
79 81
    from kamaki.clients.astakos import AstakosClient
80
    my_astakos_client = AstakosClient(AUTHENTICATION_URL, TOKEN)
82
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
81 83
        
82 84

  
83
Next, the astakos client can be used to retrieve the base_url values for the
84
servers of interest. In this case (Example 1.3) they are *cyclades*
85
and *pithos*. A number of endpoints is assigned to each service, but kamaki
86
clients only need the one labeled as ``publicURL``.
85
Next, the astakos client can be used to retrieve the `publicURL` values for the
86
services of interest. In this case (Example 1.3) they are *cyclades* (compute)
87
and *pithos* (object-store). A number of endpoints is related to each service,
88
but kamaki clients only need the ones labeled ``publicURL``.
87 89

  
88 90
.. code-block:: python
89 91
    :emphasize-lines: 1
90 92

  
91
    Example 1.3: Retrieve cyclades and pithos base_url values
93
    Example 1.3: Retrieve cyclades and pithos publicURL values
92 94

  
93
    cyclades_endpoints = my_astakos_client.get_service_endpoints('compute')
94
    cyclades_base_url = cyclades_endpoints['publicURL']
95
    cyclades_endpoints = astakos.get_service_endpoints('compute')
96
    cyclades_URL = cyclades_endpoints['publicURL']
95 97

  
96
    pithos_endpoints = my_astakos_client.get_service_endpoints('object-store')
97
    pithos_base_url = pithos_endpoints['publicURL']
98
    pithos_endpoints = astakos.get_service_endpoints('object-store')
99
    pithos_URL = pithos_endpoints['publicURL']
98 100

  
99 101
The ``get_service_endpoints`` method is called with the service name as an
100 102
argument. Here are the service names for the kamaki clients::
......
105 107
    image.ImageClient                                     --> image
106 108
    astakos.AstakosClient                                 --> identity, account
107 109

  
110
For example
111

  
112
.. code-block:: python
113
    :emphasize-lines: 1
114

  
115
    Example 1.3.1 Initialize cyclades and pithos clients
116

  
117
    from kamaki.clients.cyclades import CycladesClient
118
    from kamaki.clients.pithos import PithosClient
119

  
120
    cyclades = CycladesClient(cyclades_URL, TOKEN)
121
    pithos = PithosClient(pithos_URL, TOKEN)
122

  
123
    #  Also, setup the account UUID and container for pithos client
124
    pithos.account = astakos.user_info['id']
125
    pithos.container = 'pithos'
126

  
108 127
Use client methods
109 128
------------------
110 129

  
......
116 135
:ref:`the-client-api-ref`
117 136

  
118 137
In the following example, the *cyclades* and *pithos* clients of example 1.1
119
are used to extract some information through the remote service APIs. The information is then printed to the standard output.
138
are used to extract some information through the remote service APIs. The
139
information is then printed to the standard output.
120 140

  
121 141

  
122 142
.. code-block:: python
123 143
    :emphasize-lines: 1,2
124 144

  
125 145
    Example 1.4: Print server name and OS for server with server_id
126
                Print objects in container mycont
146
                Print objects in default container
127 147

  
128
    srv = my_cyclades_client.get_server_info(server_id)
148
    srv = cyclades.get_server_info(server_id)
129 149
    print("Server Name: %s (with OS %s" % (srv['name'], srv['os']))
130 150

  
131
    obj_list = my_pithos_client.list_objects(mycont)
151
    obj_list = pithos.list_objects()
152
    print("Objects in container '%s':" % pithos.container)
132 153
    for obj in obj_list:
133 154
        print('  %s of %s bytes' % (obj['name'], obj['bytes']))
134 155

  
......
140 161

  
141 162
    $ python test_script.py
142 163
    Server Name: A Debian Server (with OS Debian Base)
164
    Objects in container 'pithos':
143 165
      lala.txt of 34 bytes
144 166
      test.txt of 1232 bytes
145 167
      testDir/ of 0 bytes
......
150 172

  
151 173
The *kamaki.clients* error class is ClientError. A ClientError is raised for
152 174
any kind of *kamaki.clients* errors (errors reported by servers, type errors in
153
arguments, etc.).
175
method arguments, etc.).
154 176

  
155 177
A ClientError contains::
156 178

  
......
171 193
    from kamaki.clients.pithos import PithosClient
172 194

  
173 195
    try:
174
        my_astakos_client = AstakosClient(AUTHENTICATION_URL, TOKEN)
175
        my_astakos_client.authenticate()
196
        astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
176 197
    except ClientError:
177 198
        print('Failed to authenticate user token')
178 199
        return 1
179 200

  
180 201
    try:
181
        cyclades_endpoints = my_astakos_client.get_service_endpoints('compute')
182
        cyclades_base_url = cyclades_endpoints['publicURL']
202
        cyclades_endpoints = astakos.get_service_endpoints('compute')
203
        cyclades_publicURL = cyclades_endpoints['publicURL']
183 204
    except ClientError:
184 205
        print('Failed to get endpoints for cyclades')
185 206

  
186 207
    try:
187
        my_cyclades_client = CycladesClient(cyclades_base_url, token)
208
        cyclades = CycladesClient(cyclades_publicURL, token)
188 209
    except ClientError:
189 210
        print('Failed to initialize Cyclades client')
190 211

  
191 212
    try:
192
        pithos_endpoints = my_astakos_client.get_service_endpoints('object-store')
193
        pithos_base_url = pithos_endpoints['publicURL']
213
        pithos_endpoints = astakos.get_service_endpoints('object-store')
214
        pithos_publicURL = pithos_endpoints['publicURL']
194 215
    except ClientError:
195 216
        print('Failed to get endpoints for pithos')
196 217

  
197 218
    try:
198
        my_pithos_client = PithosClient(pithos_base_url, token, account, container)
219
        pithos = PithosClient(pithos_publicURL, token, account, container)
199 220
    except ClientError:
200 221
        print('Failed to initialize Pithos+ client')
201 222

  
202 223
    try:
203
        srv = my_cyclades_client.get_server_info(server_id)
224
        srv = cyclades.get_server_info(server_id)
204 225
        print("Server Name: %s (with OS %s" % (srv['name'], srv['os']))
205 226

  
206
        obj_list = my_pithos_client.list_objects(mycont)
227
        obj_list = pithos.list_objects()
207 228
        for obj in obj_list:
208 229
            print('  %s of %s bytes' % (obj['name'], obj['bytes']))
209 230
    except ClientError as e:
b/kamaki/cli/commands/astakos.py
133 133
class user_uuid2name(_init_synnefo_astakosclient, _optional_json):
134 134
    """Get user name(s) from uuid(s)"""
135 135

  
136
    @errors.generic.all
137
    @errors.user.astakosclient
136
    #@errors.generic.all
137
    #@errors.user.astakosclient
138 138
    def _run(self, uuids):
139 139
        r = self.client.get_usernames(uuids)
140 140
        self._print(r, self.print_dict)
b/kamaki/clients/astakos/__init__.py
38 38
from kamaki.clients import Client, ClientError, RequestManager, recvlog
39 39

  
40 40

  
41
class AstakosClientError(AstakosClientException, ClientError):
42
    """Join AstakosClientException as ClientError in one class"""
43

  
44

  
45
def _astakos_error(foo):
46
    def wrap(self, *args, **kwargs):
47
        try:
48
            return foo(self, *args, **kwargs)
49
        except AstakosClientException as sace:
50
            raise AstakosClientError('%s' % sace, sace.status, sace.details)
51
    return wrap
52

  
53

  
41 54
class AstakosClient(OriginalAstakosClient):
42 55
    """Wrap Original AstakosClient to ensure compatibility in kamaki clients"""
43 56

  
57
    @_astakos_error
44 58
    def __init__(self, *args, **kwargs):
45 59
        if args:
46 60
            args = list(args)
......
64 78
        return self.user_info[term]
65 79

  
66 80

  
67
def _astakos_error(foo):
68
    def wrap(self, *args, **kwargs):
69
        try:
70
            return foo(self, *args, **kwargs)
71
        except AstakosClientException as sace:
72
            self._raise_for_status(sace)
73
    return wrap
81
#  Wrap AstakosClient public methods to raise AstakosClientError
82
from inspect import getmembers
83
for m in getmembers(AstakosClient):
84
    if hasattr(m[1], '__call__') and not ('%s' % m[0]).startswith('_'):
85
        setattr(AstakosClient, m[0], _astakos_error(m[1]))
74 86

  
75 87

  
76 88
class LoggedAstakosClient(AstakosClient):
b/kamaki/clients/pithos/__init__.py
619 619
                    tries -= 1
620 620
                old_failures = len(missing)
621 621
            if missing:
622
                raise ClientError(
623
                    '%s blocks failed to upload' % len(missing),
624
                    details=['%s' % thread.exception for thread in missing])
622
                raise ClientError('%s blocks failed to upload' % len(missing))
625 623
        except KeyboardInterrupt:
626 624
            sendlog.info('- - - wait for threads to finish')
627 625
            for thread in activethreads():

Also available in: Unified diff