1 Creating applications with kamaki API
2 =====================================
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.
8 A showcase of an application built on *kamaki.clients* is *kamaki.cli*, the
9 command line interface of kamaki.
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
19 Setup a client instance
20 -----------------------
22 There is a client for every API. An external applications should instantiate
23 the kamaki clients that fit their needs.
25 For example, to manage virtual servers and stored objects / files, an
26 application would probably need the CycladesClient and PithosClient
29 .. code-block:: python
32 Example 1.1: Instantiate Cyclades and Pithos clients
35 from kamaki.clients.cyclades import CycladesClient
36 from kamaki.clients.pithos import PithosClient
38 cyclades = CycladesClient(computeURL, token)
39 pithos = PithosClient(object-storeURL, token, account, container)
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.
46 Using endpoints to get the authentication url
47 ---------------------------------------------
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).
55 Here are instructions for getting the publicURL for a service::
57 1. From the deployment UI get the AUTHENTICATION_URL and TOKEN
59 2. Use them to instantiate an AstakosClient
61 3. Use AstakosClient instance to get endpoints for the service of interest
63 4. The 'publicURL' endpoint is the URL we are looking for
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.
71 Let's review with a few examples.
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.
76 .. code-block:: python
79 Example 1.2: Initialize an astakos client
81 from kamaki.clients.astakos import AstakosClient
82 astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
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``.
90 .. code-block:: python
93 Example 1.3: Retrieve cyclades and pithos publicURL values
95 cyclades_endpoints = astakos.get_service_endpoints('compute')
96 cyclades_URL = cyclades_endpoints['publicURL']
98 pithos_endpoints = astakos.get_service_endpoints('object-store')
99 pithos_URL = pithos_endpoints['publicURL']
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::
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
112 .. code-block:: python
115 Example 1.3.1 Initialize cyclades and pithos clients
117 from kamaki.clients.cyclades import CycladesClient
118 from kamaki.clients.pithos import PithosClient
120 cyclades = CycladesClient(cyclades_URL, TOKEN)
121 pithos = PithosClient(pithos_URL, TOKEN)
123 # Also, setup the account UUID and container for pithos client
124 pithos.account = astakos.user_info['id']
125 pithos.container = 'pithos'
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.
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`
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.
142 .. code-block:: python
143 :emphasize-lines: 1,2
145 Example 1.4: Print server name and OS for server with server_id
146 Print objects in default container
148 srv = cyclades.get_server_info(server_id)
149 print("Server Name: %s (OS: %s)" % (srv['name'], srv['metadata']['os']))
151 obj_list = pithos.list_objects()
152 print("Objects in container '%s':" % pithos.container)
154 print(' %s of %s bytes' % (obj['name'], obj['bytes']))
156 .. code-block:: console
159 * A run of examples 1.1 + 1.4 *
162 $ python test_script.py
163 Server Name: A Debian Server (OS: debian)
164 Objects in container 'pithos':
166 test.txt of 1232 bytes
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.).
177 A ClientError contains::
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.
183 The following example concatenates examples 1.1 to 1.4 plus error handling
185 .. code-block:: python
187 Example 1.5: Error handling
189 from kamaki.clients import ClientError
191 from kamaki.clients.astakos import AstakosClient
192 from kamaki.clients.cyclades import CycladesClient
193 from kamaki.clients.pithos import PithosClient
196 astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
198 print('Failed to authenticate user token')
202 cyclades_endpoints = astakos.get_service_endpoints('compute')
203 CYCLADES_URL = cyclades_endpoints['publicURL']
205 print('Failed to get endpoints for cyclades')
208 cyclades = CycladesClient(CYCLADES_URL, TOKEN)
210 print('Failed to initialize Cyclades client')
213 pithos_endpoints = astakos.get_service_endpoints('object-store')
214 PITHOS_URL = pithos_endpoints['publicURL']
216 print('Failed to get endpoints for pithos')
219 account, container = astakos.user_info['id'], 'pithos'
220 pithos = PithosClient(PITHOS_URL, TOKEN, account, container)
222 print('Failed to initialize Pithos+ client')
225 server_id = SERVER_ID
226 srv = cyclades.get_server_info(server_id)
227 print("Server Name: %s (OS: %s)" % (srv['name'], srv['metadata']['os']))
229 obj_list = pithos.list_objects()
230 print('Objects in container %s:' % pithos.container)
232 print(' %s of %s bytes' % (obj['name'], obj['bytes']))
233 except ClientError as e:
234 print('Error: %s' % e)
236 print('- error code: %s' % e.status)
238 for detail in e.details:
239 print('- %s' % detail)
248 .. code-block:: python
252 from kamaki.clients.astakos import AstakosClient
253 from kamaki.clients.cyclades import CycladesClient
255 AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
256 TOKEN = 'replace this with your token'
258 astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
260 CYCLADES_URL = astakos.get_service_endpoints('compute')['publicURL']
261 cyclades = CycladesClient(CYCLADES_URL, TOKEN)
263 # (name, flavor-id, image-id)
265 ('My Debian Server', 1, 'my-debian-base-image-id'),
266 ('My Windows Server', 3, 'my-windows-8-image-id'),
267 ('My Ubuntu Server', 3, 'my-ubuntu-12-image-id'),
271 for name, flavor_id, image_id in servers:
272 new_vm = cyclades.create_server(name, flavor_id, image_id, networks=[])
273 created.append(new_vm)
276 print 'Wait while vm "%s" (%s) is being build' % (vm['name'], vm['id'])
277 cyclades.wait_server(vm['id'])
279 .. note:: The `networks=[]` argument explicitly instructs `cyclades` to create
280 a virtual server without any network connections. If not used, `cyclades`
281 will apply the default policy (e.g., assign a public IP to the new virtual
284 Register a banch of pre-uploaded images
285 '''''''''''''''''''''''''''''''''''''''
287 .. code-block:: python
291 from kamaki.clients import ClientError
292 from kamaki.clients.astakos import AstakosClient
293 from kamaki.clients.pithos import PithosClient
294 from kamaki.clients.image import ImageClient
296 AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
297 TOKEN = 'replace this with your token'
298 IMAGE_CONTAINER = 'images'
300 astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
301 USER_UUID = astakos.user_info['id']
303 PITHOS_URL = astakos.get_service_endpoints('object-store')['publicURL']
304 pithos = PithosClient(
305 PITHOS_URL, TOKEN, account=USER_UUID, container=IMAGE_CONTAINER)
307 IMAGE_URL = astakos.get_service_endpoints('image')['publicURL']
308 plankton = ImageClient(IMAGE_URL, TOKEN)
310 for img in pithos.list_objects():
311 IMAGE_PATH = img['name']
313 r = plankton.register(
314 name='Image %s' % img,
315 location=(USER_UUID, IMAGE_CONTAINER, IMAGE_PATH))
316 print 'Image %s registered with id %s' % (r['name'], r['id'])
318 print 'Failed to register image %s' % IMAGE_PATH
320 .. note:: In `plankton.register`, the `location` argument can be either
321 `a triplet`, as shown above, or `a qualified URL` of the form
322 ``pithos://USER_UUID/IMAGE_CONTAINER/IMAGE_PATH``.
324 Two servers and a private network
325 '''''''''''''''''''''''''''''''''
327 .. code-block:: python
331 from kamaki.clients.astakos import AstakosClient
332 from kamaki.clients.cyclades import CycladesClient, CycladesNetworkClient
334 AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
335 TOKEN = 'replace this with your token'
337 astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
339 NETWORK_URL = astakos.get_service_endpoints('network')['publicURL']
340 network = CycladesNetworkClient(NETWORK_URL, TOKEN)
342 net = network.create_network(type='MAC_FILTERED', name='My private network')
344 CYCLADES_URL = astakos.get_service_endpoints('compute')['publicURL']
345 cyclades = CycladesClient(CYCLADES_URL, TOKEN)
347 FLAVOR_ID = 'put your flavor id here'
348 IMAGE_ID = 'put your image id here'
350 srv1 = cyclades.create_server(
351 'server 1', FLAVOR_ID, IMAGE_ID,
352 networks=[{'uuid': net['id']}])
353 srv2 = cyclades.create_server(
354 'server 2', FLAVOR_ID, IMAGE_ID,
355 networks=[{'uuid': net['id']}])
357 srv_state1 = cyclades.wait_server(srv1['id'])
358 assert srv_state1 in ('ACTIVE', ), 'Server 1 built failure'
360 srv_state2 = cyclades.wait_server(srv2['id'])
361 assert srv_state2 in ('ACTIVE', ), 'Server 2 built failure'