Statistics
| Branch: | Tag: | Revision:

root / docs / developers / clients-api.rst @ 52b2b3bd

History | View | Annotate | Download (12.5 kB)

1
Creating applications with kamaki API
2
=====================================
3

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

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

    
11
Since Synnefo services are build as OpenStack extensions, an inheritance
12
approach has been chosen for implementing clients for both APIs. In specific,
13
the *compute*, *storage* and *image* modules are client implementations for the
14
OpenStack compute, OpenStack object-store and Image APIs respectively. The rest
15
of the modules implement the Synnefo extensions (i.e., *cyclades* and
16
*cyclades_rest_api* extents *compute*, *pithos* and *pithos_rest_api* extent
17
*storage*).
18

    
19
Setup a client instance
20
-----------------------
21

    
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.
28

    
29
.. code-block:: python
30
    :emphasize-lines: 1
31

    
32
    Example 1.1: Instantiate Cyclades and Pithos clients
33

    
34

    
35
    from kamaki.clients.cyclades import CycladesClient
36
    from kamaki.clients.pithos import PithosClient
37

    
38
    cyclades = CycladesClient(computeURL, token)
39
    pithos = PithosClient(object-storeURL, token, account, container)
40

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

    
46
Using endpoints to get the authentication url
47
---------------------------------------------
48

    
49
In OpenStack, each service (e.g., `compute`, `object-store`, etc.) has a number
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).
54

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

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

    
66
The AstakosClient is a client for the Synnefo/Astakos server. Synnefo/Astakos
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.
70

    
71
Let's review with a few examples.
72

    
73
First, an astakos client must be initialized (Example 1.2). An
74
AUTHENTICATION_URL and a TOKEN can be acquired from the Synnefo deployment UI.
75

    
76
.. code-block:: python
77
    :emphasize-lines: 1
78

    
79
    Example 1.2: Initialize an astakos client
80

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

    
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``.
89

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

    
93
    Example 1.3: Retrieve cyclades and pithos publicURL values
94

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

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

    
101
The ``get_service_endpoints`` method is called with the service name as an
102
argument. Here are the service names for the kamaki clients::
103

    
104
    storage.StorageClient, pithos.PithosClient            --> object-store
105
    compute.ComputeClient, cyclades.CycladesClient        --> compute
106
    network.NetworkClient, cyclades.CycladesNetworkClient --> network
107
    image.ImageClient                                     --> image
108
    astakos.AstakosClient                                 --> identity, account
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

    
127
Use client methods
128
------------------
129

    
130
At this point we assume that we can initialize a client, so the initialization
131
step will be omitted in most of the examples that follow.
132

    
133
The next step is to take a look at the member methods of each particular client.
134
A detailed catalog of the member methods for all client classes can be found at
135
:ref:`the-client-api-ref`
136

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

    
141

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

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

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

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

    
156
.. code-block:: console
157
    :emphasize-lines: 1
158

    
159
    * A run of examples 1.1 + 1.4 *
160

    
161

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

    
170
Error handling
171
--------------
172

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

    
177
A ClientError contains::
178

    
179
    message     The error message.
180
    status      An optional error code, e.g., after a server error.
181
    details     Optional list of messages with error details.
182

    
183
The following example concatenates examples 1.1 to 1.4 plus error handling
184

    
185
.. code-block:: python
186

    
187
    Example 1.5: Error handling
188

    
189
    from kamaki.clients import ClientError
190

    
191
    from kamaki.clients.astakos import AstakosClient
192
    from kamaki.clients.cyclades import CycladesClient
193
    from kamaki.clients.pithos import PithosClient
194

    
195
    try:
196
        astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
197
    except ClientError:
198
        print('Failed to authenticate user token')
199
        return 1
200

    
201
    try:
202
        cyclades_endpoints = astakos.get_service_endpoints('compute')
203
        cyclades_publicURL = cyclades_endpoints['publicURL']
204
    except ClientError:
205
        print('Failed to get endpoints for cyclades')
206

    
207
    try:
208
        cyclades = CycladesClient(cyclades_publicURL, token)
209
    except ClientError:
210
        print('Failed to initialize Cyclades client')
211

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

    
218
    try:
219
        pithos = PithosClient(pithos_publicURL, token, account, container)
220
    except ClientError:
221
        print('Failed to initialize Pithos+ client')
222

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

    
227
        obj_list = pithos.list_objects()
228
        for obj in obj_list:
229
            print('  %s of %s bytes' % (obj['name'], obj['bytes']))
230
    except ClientError as e:
231
        print('Error: %s' % e)
232
        if e.status:
233
            print('- error code: %s' % e.status)
234
        if e.details:
235
            for detail in e.details:
236
                print('- %s' % detail)
237

    
238

    
239
Scripts
240
-------
241

    
242
Batch-create servers
243
''''''''''''''''''''
244

    
245
.. code-block:: python
246

    
247
    #! /usr/bin/python
248

    
249
    from kamaki.clients.astakos import AstakosClient
250
    from kamaki.clients.cyclades import CycladesClient
251

    
252
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
253
    TOKEN = 'replace this with your token'
254

    
255
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
256

    
257
    CYCLADES_URL = astakos.get_service_endpoints('compute')['publicURL']
258
    cyclades = CycladesClient(CYCLADES_URL, TOKEN)
259

    
260
    #  (name, flavor-id, image-id)
261
    servers = [
262
        ('My Debian Server', 1, 'my-debian-base-image-id'),
263
        ('My Windows Server', 3, 'my-windows-8-image-id'),
264
        ('My Ubuntu Server', 3, 'my-ubuntu-12-image-id'),
265
    ]
266

    
267
    for name, flavor_id, image_id in servers:
268
        cyclades.create_server(name, flavor_id, image_id)
269

    
270

    
271
Batch-create 4 servers of the same kind
272
'''''''''''''''''''''''''''''''''''''''
273

    
274
.. code-block:: python
275

    
276
    #! /usr/bin/python
277

    
278
    from kamaki.clients.astakos import AstakosClient
279
    from kamaki.clients.cyclades import CycladesClient
280

    
281
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
282
    TOKEN = 'replace this with your token'
283

    
284
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
285

    
286
    CYCLADES_URL = astakos.get_service_endpoints('compute')['publicURL']
287
    cyclades = CycladesClient(CYCLADES_URL, TOKEN)
288

    
289
    for i in range(4):
290
        name, flavor_id, image_id = 'Server %s' % (i + 1), 3, 'some-image-id'
291
        cyclades.create_server(name, flavor_id, image_id)
292

    
293
Register a banch of pre-uploaded images
294
'''''''''''''''''''''''''''''''''''''''
295

    
296
.. code-block:: python
297

    
298
    #! /usr/bin/python
299

    
300
    from kamaki.clients import ClientError
301
    from kamaki.clients.astakos import AstakosClient
302
    from kamaki.clients.pithos import PithosClient
303
    from kamaki.clients.image import ImageClient
304

    
305
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
306
    TOKEN = 'replace this with your token'
307
    IMAGE_CONTAINER = 'images'
308

    
309
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
310
    USER_UUID = astakos.user_info['uuid']
311

    
312
    PITHOS_URL = astakos.get_service_endpoints('object-store')['publicURL']
313
    pithos = PithosClient(PITHOS_URL, TOKEN, USER_UUID, IMAGE_CONTAINER)
314

    
315
    IMAGE_URL = astakos.get_service_endpoints('image')['publicURL']
316
    plankton = ImageClient(IMAGE_URL, TOKEN)
317

    
318
    for img in pithos.list_objects():
319
        IMAGE_PATH = img['name']
320
        try:
321
            r = plankton.register(
322
                name='Image %s' % img,
323
                location=(USER_UUID, IMAGE_CONTAINER, IMAGE_PATH))
324
            print 'Image %s registered with id %s' % (r['name'], r['id'])
325
        except ClientError:
326
            print 'Failed to register image %s' % IMAGE_PATH
327

    
328
..note::
329

    
330
    In plankton.register, the `location` argument can be either a triplet, as
331
    shown above, or a qualified URL of the form
332
    pithos://USER_UUID/IMAGE_CONTAINER/IMAGE_PATH
333
    which is the format used by the image API.
334

    
335
Two servers and a private network
336
'''''''''''''''''''''''''''''''''
337

    
338
.. code-block:: python
339

    
340
    #! /user/bin/python
341

    
342
    from kamaki.clients.astakos import AstakosClient
343
    from kamaki.clients.cyclades import CycladesClient, CycladesNetworkClient
344

    
345
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
346
    TOKEN = 'replace this with your token'
347

    
348
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
349

    
350
    NETWORK_URL = astakos.get_service_endpoints('network')['publicURL']
351
    network = CycladesNetworkClient(NETWORK_URL, TOKEN)
352

    
353
    net = network.create_network(type='MAC_FILTERED', name='My private network')
354

    
355
    CYCLADES_URL = astakos.get_service_endpoints('compute')['publicURL']
356
    cyclades = CycladesClient(CYCLADES_URL, TOKEN)
357

    
358
    FLAVOR_ID = 'put your flavor id here'
359
    IMAGE_ID = 'put your image id here'
360

    
361
    srv1 = cyclades.create_server(
362
        'server 1', FLAVOR_ID, IMAGE_ID,
363
        networks=[{'uuid': net['id']}])
364
    srv2 = cyclades.create_server(
365
        'server 2', FLAVOR_ID, IMAGE_ID,
366
        networks=[{'uuid': net['id']}])
367

    
368
    srv_state1 = cyclades.wait_server(srv1['id'])
369
    assert srv_state1 in ('ACTIVE', ), 'Server 1 built failure'
370

    
371
    srv_state2 = cyclades.wait_server(srv2['id'])
372
    assert srv_state2 in ('ACTIVE', ), 'Server 2 built failure'