Statistics
| Branch: | Tag: | Revision:

root / docs / developers / clients-api.rst @ 16d7b9ff

History | View | Annotate | Download (11.9 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 and OpenStack object-store APIs, respectively. The rest of the
15
modules implement the Synnefo extensions (i.e., *cyclades* and 
16
*cyclades_rest_api* extents *compute*, *pithos* and *pithos_rest_api* extent
17
*storage*) or novel Synnefo services (*image* for *plankton*).
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 actually 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 the endpoints of 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
    image.ImageClient                               -->     image
105
    astakos.AstakosClient                           -->     identity, account
106

    
107
Use client methods
108
------------------
109

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

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

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

    
120

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

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

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

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

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

    
137
    * A run of examples 1.1 + 1.4 *
138

    
139

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

    
147
Error handling
148
--------------
149

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

    
154
A ClientError contains::
155

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

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

    
162
.. code-block:: python
163

    
164
    Example 1.5: Error handling
165

    
166
    from kamaki.clients import ClientError
167

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

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

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

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

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

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

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

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

    
216

    
217
Scripts
218
-------
219

    
220
Batch-create servers
221
''''''''''''''''''''
222

    
223
.. code-block:: python
224

    
225
    #! /usr/bin/python
226

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

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

    
233
    user = AstakosClient(AUTHENTICATION_URL, TOKEN)
234

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

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

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

    
249

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

    
253
.. code-block:: python
254

    
255
    #! /usr/bin/python
256

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

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

    
263
    user = AstakosClient(AUTHENTICATION_URL, TOKEN)
264

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

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

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

    
276
.. code-block:: python
277

    
278
    #! /usr/bin/python
279

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

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

    
289
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
290
    USER_UUID = astakos.user_term('uuid')
291

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

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

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

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

    
311
.. code-block:: python
312

    
313
    #! /user/bin/python
314

    
315
    from kamaki.clients.astakos import AstakosClient
316
    from kamaki.clients.cyclades import CycladesClient
317

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

    
321
    astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
322

    
323
    cyclades_endpoints = user.get_service_endpoints('compute')
324
    CYCLADES_URL = cyclades_endpoints['publicURL']
325

    
326
    FLAVOR_ID = 'put your flavor id here'
327
    IMAGE_ID = 'put your image id here'
328

    
329
    cyclades = CycladesClient(CYCLADES_URL, TOKEN)
330

    
331
    srv1 = cyclades.create_server('server 1', FLAVOR_ID, IMAGE_ID)
332
    srv2 = cyclades.create_server('server 2', FLAVOR_ID, IMAGE_ID)
333

    
334
    srv_state1 = cyclades.wait_server(srv1['id'])
335
    assert srv_state1 in ('ACTIVE'), 'Server 1 built failure'
336

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

    
340
    net = cyclades.create_network('My private network')
341
    net_state = cyclades.wait_network(net['id'])
342
    assert net_state in ('ACTIVE', ), 'Network built failure'
343

    
344
    cyclades.connect_server(srv1['id'], net['id'])
345
    cyclades.connect_server(srv2['id'], net['id'])