root / docs / developers / clients-api.rst @ f0bddbda
History | View | Annotate | Download (12.3 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 (OS: %s)" % (srv['name'], srv['metadata']['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 (OS: debian) |
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 |
raise |
200 |
|
201 |
try: |
202 |
cyclades_endpoints = astakos.get_service_endpoints('compute') |
203 |
CYCLADES_URL = cyclades_endpoints['publicURL'] |
204 |
except ClientError: |
205 |
print('Failed to get endpoints for cyclades') |
206 |
|
207 |
try: |
208 |
cyclades = CycladesClient(CYCLADES_URL, 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_URL = pithos_endpoints['publicURL'] |
215 |
except ClientError: |
216 |
print('Failed to get endpoints for pithos') |
217 |
|
218 |
try: |
219 |
account, container = astakos.user_info['id'], 'pithos' |
220 |
pithos = PithosClient(PITHOS_URL, TOKEN, account, container) |
221 |
except ClientError: |
222 |
print('Failed to initialize Pithos+ client') |
223 |
|
224 |
try: |
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'])) |
228 |
|
229 |
obj_list = pithos.list_objects() |
230 |
print('Objects in container %s:' % pithos.container) |
231 |
for obj in obj_list: |
232 |
print(' %s of %s bytes' % (obj['name'], obj['bytes'])) |
233 |
except ClientError as e: |
234 |
print('Error: %s' % e) |
235 |
if e.status: |
236 |
print('- error code: %s' % e.status) |
237 |
if e.details: |
238 |
for detail in e.details: |
239 |
print('- %s' % detail) |
240 |
|
241 |
|
242 |
Scripts |
243 |
------- |
244 |
|
245 |
Batch-create servers |
246 |
'''''''''''''''''''' |
247 |
|
248 |
.. code-block:: python |
249 |
|
250 |
#! /usr/bin/python |
251 |
|
252 |
from kamaki.clients.astakos import AstakosClient |
253 |
from kamaki.clients.cyclades import CycladesClient |
254 |
|
255 |
AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0' |
256 |
TOKEN = 'replace this with your token' |
257 |
|
258 |
astakos = AstakosClient(AUTHENTICATION_URL, TOKEN) |
259 |
|
260 |
CYCLADES_URL = astakos.get_service_endpoints('compute')['publicURL'] |
261 |
cyclades = CycladesClient(CYCLADES_URL, TOKEN) |
262 |
|
263 |
# (name, flavor-id, image-id) |
264 |
servers = [ |
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'), |
268 |
] |
269 |
|
270 |
created = [] |
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) |
274 |
|
275 |
for vm in created: |
276 |
print 'Wait while vm "%s" (%s) is being build' % (vm['name'], vm['id']) |
277 |
cyclades.wait_server(vm['id']) |
278 |
|
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 |
282 |
server). |
283 |
|
284 |
Register a banch of pre-uploaded images |
285 |
''''''''''''''''''''''''''''''''''''''' |
286 |
|
287 |
.. code-block:: python |
288 |
|
289 |
#! /usr/bin/python |
290 |
|
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 |
295 |
|
296 |
AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0' |
297 |
TOKEN = 'replace this with your token' |
298 |
IMAGE_CONTAINER = 'images' |
299 |
|
300 |
astakos = AstakosClient(AUTHENTICATION_URL, TOKEN) |
301 |
USER_UUID = astakos.user_info['id'] |
302 |
|
303 |
PITHOS_URL = astakos.get_service_endpoints('object-store')['publicURL'] |
304 |
pithos = PithosClient( |
305 |
PITHOS_URL, TOKEN, account=USER_UUID, container=IMAGE_CONTAINER) |
306 |
|
307 |
IMAGE_URL = astakos.get_service_endpoints('image')['publicURL'] |
308 |
plankton = ImageClient(IMAGE_URL, TOKEN) |
309 |
|
310 |
for img in pithos.list_objects(): |
311 |
IMAGE_PATH = img['name'] |
312 |
try: |
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']) |
317 |
except ClientError: |
318 |
print 'Failed to register image %s' % IMAGE_PATH |
319 |
|
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``. |
323 |
|
324 |
Two servers and a private network |
325 |
''''''''''''''''''''''''''''''''' |
326 |
|
327 |
.. code-block:: python |
328 |
|
329 |
#! /user/bin/python |
330 |
|
331 |
from kamaki.clients.astakos import AstakosClient |
332 |
from kamaki.clients.cyclades import CycladesClient, CycladesNetworkClient |
333 |
|
334 |
AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0' |
335 |
TOKEN = 'replace this with your token' |
336 |
|
337 |
astakos = AstakosClient(AUTHENTICATION_URL, TOKEN) |
338 |
|
339 |
NETWORK_URL = astakos.get_service_endpoints('network')['publicURL'] |
340 |
network = CycladesNetworkClient(NETWORK_URL, TOKEN) |
341 |
|
342 |
net = network.create_network(type='MAC_FILTERED', name='My private network') |
343 |
|
344 |
CYCLADES_URL = astakos.get_service_endpoints('compute')['publicURL'] |
345 |
cyclades = CycladesClient(CYCLADES_URL, TOKEN) |
346 |
|
347 |
FLAVOR_ID = 'put your flavor id here' |
348 |
IMAGE_ID = 'put your image id here' |
349 |
|
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']}]) |
356 |
|
357 |
srv_state1 = cyclades.wait_server(srv1['id']) |
358 |
assert srv_state1 in ('ACTIVE', ), 'Server 1 built failure' |
359 |
|
360 |
srv_state2 = cyclades.wait_server(srv2['id']) |
361 |
assert srv_state2 in ('ACTIVE', ), 'Server 2 built failure' |