Statistics
| Branch: | Tag: | Revision:

root / docs / developers / clients-api.rst @ fe980c71

History | View | Annotate | Download (12 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 OpenStack and / or Synnefo cloud services. The package is
6
called *kamaki.clients* and serves as a lib.
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. 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, 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.
26

    
27
.. code-block:: python
28
    :emphasize-lines: 1
29

    
30
    Example 1.1: Instantiate Cyclades and Pithos clients
31

    
32

    
33
    from kamaki.clients.cyclades import CycladesClient
34
    from kamaki.clients.pithos import PithosClient
35

    
36
    my_cyclades_client = CycladesClient(base_url, token)
37
    my_pithos_client = PithosClient(base_url, token, account, container)
38

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

    
44
Using endpoints to get the base_url
45
-----------------------------------
46

    
47
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.
52

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

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

    
64
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.
68

    
69
Let's review the process with examples.
70

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

    
74
.. code-block:: python
75
    :emphasize-lines: 1
76

    
77
    Example 1.2: Initialize an astakos client
78

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

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

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

    
91
    Example 1.3: Retrieve cyclades and pithos base_url values
92

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

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

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

    
102
    storage.StorageClient, pithos.PithosClient            --> object-store
103
    compute.ComputeClient, cyclades.CycladesClient        --> compute
104
    network.NetworkClient, cyclades.CycladesNetworkClient --> network
105
    image.ImageClient                                     --> image
106
    astakos.AstakosClient                                 --> identity, account
107

    
108
Use client methods
109
------------------
110

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

    
114
The next step is to take a look at the member methods of each particular client.
115
A detailed catalog of the member methods for all client classes can be found at
116
:ref:`the-client-api-ref`
117

    
118
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.
120

    
121

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

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

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

    
131
    obj_list = my_pithos_client.list_objects(mycont)
132
    for obj in obj_list:
133
        print('  %s of %s bytes' % (obj['name'], obj['bytes']))
134

    
135
.. code-block:: console
136
    :emphasize-lines: 1
137

    
138
    * A run of examples 1.1 + 1.4 *
139

    
140

    
141
    $ python test_script.py
142
    Server Name: A Debian Server (with OS Debian Base)
143
      lala.txt of 34 bytes
144
      test.txt of 1232 bytes
145
      testDir/ of 0 bytes
146
    $ 
147

    
148
Error handling
149
--------------
150

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

    
155
A ClientError contains::
156

    
157
    message     The error message.
158
    status      An optional error code, e.g., after a server error.
159
    details     Optional list of messages with error details.
160

    
161
The following example concatenates examples 1.1 to 1.4 plus error handling
162

    
163
.. code-block:: python
164

    
165
    Example 1.5: Error handling
166

    
167
    from kamaki.clients import ClientError
168

    
169
    from kamaki.clients.astakos import AstakosClient
170
    from kamaki.clients.cyclades import CycladesClient
171
    from kamaki.clients.pithos import PithosClient
172

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

    
180
    try:
181
        cyclades_endpoints = my_astakos_client.get_service_endpoints('compute')
182
        cyclades_base_url = cyclades_endpoints['publicURL']
183
    except ClientError:
184
        print('Failed to get endpoints for cyclades')
185

    
186
    try:
187
        my_cyclades_client = CycladesClient(cyclades_base_url, token)
188
    except ClientError:
189
        print('Failed to initialize Cyclades client')
190

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

    
197
    try:
198
        my_pithos_client = PithosClient(pithos_base_url, token, account, container)
199
    except ClientError:
200
        print('Failed to initialize Pithos+ client')
201

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

    
206
        obj_list = my_pithos_client.list_objects(mycont)
207
        for obj in obj_list:
208
            print('  %s of %s bytes' % (obj['name'], obj['bytes']))
209
    except ClientError as e:
210
        print('Error: %s' % e)
211
        if e.status:
212
            print('- error code: %s' % e.status)
213
        if e.details:
214
            for detail in e.details:
215
                print('- %s' % detail)
216

    
217

    
218
Scripts
219
-------
220

    
221
Batch-create servers
222
''''''''''''''''''''
223

    
224
.. code-block:: python
225

    
226
    #! /usr/bin/python
227

    
228
    from kamaki.clients.astakos import AstakosClient
229
    from kamaki.clients.cyclades import CycladesClient
230

    
231
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
232
    TOKEN = 'replace this with your token'
233

    
234
    user = AstakosClient(AUTHENTICATION_URL, TOKEN)
235

    
236
    cyclades_endpoints = user.get_service_endpoints('compute')
237
    CYCLADES_URL = cyclades_endpoints['publicURL']
238
    cyclades = CycladesClient(CYCLADES_URL, TOKEN)
239

    
240
    #  (name, flavor-id, image-id)
241
    servers = [
242
        ('My Debian Server', 1, 'my-debian-base-image-id'),
243
        ('My Windows Server', 3, 'my-windows-8-image-id'),
244
        ('My Ubuntu Server', 3, 'my-ubuntu-12-image-id'),
245
    ]
246

    
247
    for name, flavor_id, image_id in servers:
248
        cyclades.create_server(name, flavor_id, image_id)
249

    
250

    
251
Batch-create 4 servers of the same kind
252
'''''''''''''''''''''''''''''''''''''''
253

    
254
.. code-block:: python
255

    
256
    #! /usr/bin/python
257

    
258
    from kamaki.clients.astakos import AstakosClient
259
    from kamaki.clients.cyclades import CycladesClient
260

    
261
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
262
    TOKEN = 'replace this with your token'
263

    
264
    user = AstakosClient(AUTHENTICATION_URL, TOKEN)
265

    
266
    cyclades_endpoints = user.get_service_endpoints('compute')
267
    CYCLADES_URL = cyclades_endpoints['publicURL']
268
    cyclades = CycladesClient(CYCLADES_URL, TOKEN)
269

    
270
    for i in range(4):
271
        name, flavor_id, image_id = 'Server %s' % (i + 1), 3, 'some-image-id'
272
        cyclades.create_server(name, flavor_id, image_id)
273

    
274
Register a banch of pre-uploaded images
275
'''''''''''''''''''''''''''''''''''''''
276

    
277
.. code-block:: python
278

    
279
    #! /usr/bin/python
280

    
281
    from kamaki.clients import ClientError
282
    from kamaki.clients.astakos import AstakosClient
283
    from kamaki.clients.pithos import PithosClient
284
    from kamaki.clients.image import ImageClient
285

    
286
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
287
    TOKEN = 'replace this with your token'
288
    IMAGE_CONTAINER = 'images'
289

    
290
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
291
    USER_UUID = astakos.user_info['uuid']
292

    
293
    PITHOS_URL = astakos.get_service_endpoints('object-store')['publicURL']
294
    pithos = PithosClient(PITHOS_URL, TOKEN, USER_UUID, IMAGE_CONTAINER)
295

    
296
    IMAGE_URL = astakos.get_service_endpoints('image')['publicURL']
297
    plankton = ImageClient(IMAGE_URL, TOKEN)
298

    
299
    for img in pithos.list_objects():
300
        IMAGE_PATH = img['name']
301
        try:
302
            r = plankton.register(
303
                name='Image %s' % img,
304
                location=(USER_UUID, IMAGE_CONTAINER, IMAGE_PATH))
305
            print 'Image %s registered with id %s' % (r['name'], r['id'])
306
        except ClientError:
307
            print 'Failed to register image %s' % IMAGE_PATH
308

    
309
Two servers and a private network
310
'''''''''''''''''''''''''''''''''
311

    
312
.. code-block:: python
313

    
314
    #! /user/bin/python
315

    
316
    from kamaki.clients.astakos import AstakosClient
317
    from kamaki.clients.cyclades import CycladesClient, CycladesNetworkClient
318

    
319
    AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
320
    TOKEN = 'replace this with your token'
321

    
322
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
323

    
324
    network_endpoints = user.get_service_endpoints('network')
325
    NETWORK_URL = network_endpoints['publicURL']
326

    
327
    network = CycladesNetworkClient(NETWORK_URL, TOKEN)
328
    net = cyclades.create_network('My private network')
329

    
330
    cyclades_endpoints = user.get_service_endpoints('compute')
331
    CYCLADES_URL = cyclades_endpoints['publicURL']
332

    
333
    FLAVOR_ID = 'put your flavor id here'
334
    IMAGE_ID = 'put your image id here'
335
    cyclades = CycladesClient(CYCLADES_URL, TOKEN)
336

    
337
    srv1 = cyclades.create_server(
338
        'server 1', FLAVOR_ID, IMAGE_ID,
339
        networks=[{'uuid': net['id']}])
340
    srv2 = cyclades.create_server(
341
        'server 2', FLAVOR_ID, IMAGE_ID,
342
        networks=[{'uuid': net['id']}])
343

    
344
    srv_state1 = cyclades.wait_server(srv1['id'])
345
    assert srv_state1 in ('ACTIVE'), 'Server 1 built failure'
346

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