root / docs / developers / clients-api.rst @ f8636965
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, 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 |
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 |
cyclades_endpoints = user.get_service_endpoints('compute') |
325 |
CYCLADES_URL = cyclades_endpoints['publicURL'] |
326 |
|
327 |
FLAVOR_ID = 'put your flavor id here' |
328 |
IMAGE_ID = 'put your image id here' |
329 |
|
330 |
cyclades = CycladesClient(CYCLADES_URL, TOKEN) |
331 |
|
332 |
srv1 = cyclades.create_server('server 1', FLAVOR_ID, IMAGE_ID) |
333 |
srv2 = cyclades.create_server('server 2', FLAVOR_ID, IMAGE_ID) |
334 |
|
335 |
srv_state1 = cyclades.wait_server(srv1['id']) |
336 |
assert srv_state1 in ('ACTIVE'), 'Server 1 built failure' |
337 |
|
338 |
srv_state2 = cyclades.wait_server(srv2['id']) |
339 |
assert srv_state2 in ('ACTIVE'), 'Server 2 built failure' |
340 |
|
341 |
net = cyclades.create_network('My private network') |
342 |
net_state = cyclades.wait_network(net['id']) |
343 |
assert net_state in ('ACTIVE', ), 'Network built failure' |
344 |
|
345 |
cyclades.connect_server(srv1['id'], net['id']) |
346 |
cyclades.connect_server(srv2['id'], net['id']) |