Revision 16d7b9ff
b/docs/commands.rst | ||
---|---|---|
143 | 143 |
|
144 | 144 |
.. code-block:: text |
145 | 145 |
|
146 |
addr : List a server's nic address
|
|
146 |
addr : List a server nic address |
|
147 | 147 |
console : Get a VNC console |
148 | 148 |
create : Create a server |
149 | 149 |
delete : Delete a server |
150 |
firewall: Manage server's firewall profile
|
|
151 |
set : Set the server's firewall profile
|
|
152 |
get : Get the server's firewall profile
|
|
150 |
firewall: Manage server firewall profile |
|
151 |
set : Set the server firewall profile |
|
152 |
get : Get the server firewall profile |
|
153 | 153 |
ip : Manage floating IPs for the servers |
154 | 154 |
attach: Attach a floating ip to a server with server_id |
155 |
info : A floating IPs' details
|
|
155 |
info : A floating IP details |
|
156 | 156 |
detach: Detach floating ip from server |
157 | 157 |
list : List all floating ips |
158 | 158 |
create: Create a new floating IP |
... | ... | |
165 | 165 |
set : Add / update server metadata |
166 | 166 |
delete: Delete a piece of server metadata |
167 | 167 |
reboot : Reboot a server |
168 |
rename : Update a server's name
|
|
168 |
rename : Update a server name |
|
169 | 169 |
shutdown: Shutdown a server |
170 | 170 |
start : Start a server |
171 | 171 |
stats : Get server statistics |
... | ... | |
445 | 445 |
-rw-rw-r-- 1 ******** ******** 20M Nov 26 15:42 rndm_remote.file |
446 | 446 |
[file]: !diff rndm_local.file rndm_remote.file |
447 | 447 |
|
448 |
.. Note:: In kamaki shell, ! is used to execute OS shell commands (e.g. bash) |
|
448 |
.. Note:: In kamaki shell, ! is used to execute OS shell commands (e.g., bash)
|
|
449 | 449 |
|
450 | 450 |
.. warning:: The container:object/path syntax does not function if the |
451 | 451 |
container and / or the object path contain one or more : characters. To use |
452 | 452 |
containers and objects with : use the --container and --dst-container |
453 |
arguments, e.g. to copy test.py object from example:dev container to |
|
453 |
arguments, e.g., to copy test.py object from example:dev container to
|
|
454 | 454 |
example:deploy :: |
455 | 455 |
|
456 | 456 |
$ kamaki file copy --container=example:dev test.py --dst-container=example:deploy |
b/docs/developers/adding-commands.rst | ||
---|---|---|
2 | 2 |
=============== |
3 | 3 |
|
4 | 4 |
Kamaki commands are implemented as python classes, decorated with a special |
5 |
decorator called *command*. This decorator is a method of kamaki.cli that adds |
|
6 |
a new command in a CommandTree structure (kamaki.cli.commant_tree). The later |
|
7 |
is used by interfaces to manage kamaki commands. |
|
5 |
decorator called *command*. This decorator is a method of *kamaki.cli* that |
|
6 |
adds a new command in a *CommandTree* structure. A *CommandTree* (package |
|
7 |
*kamaki.cli.commant_tree*) is a data structure used by kamaki to manage command |
|
8 |
namespaces. |
|
8 | 9 |
|
9 |
In the following, a set of kamaki commands will be implemented:: |
|
10 |
For demonstration purposes, the following set of kamaki commands will be |
|
11 |
implemented in this document:: |
|
10 | 12 |
|
11 | 13 |
mygrp1 list all //show a list |
12 | 14 |
mygrp1 list details [--match=<>] //show list of details |
13 | 15 |
mygrp2 list all [regular expression] [-l] //list all subjects |
14 | 16 |
mygrp2 info <id> [name] //get information for subject with id |
15 | 17 |
|
16 |
There are two command sets to implement, namely mygrp1 and mygrp2. The first
|
|
17 |
will contain two commands, namely list-all and list-details. The second one
|
|
18 |
will also contain two commands, list-all and info. To avoid ambiguities,
|
|
19 |
command names should rather be prefixed with the group they belong to, e.g.
|
|
20 |
mygrp1-list-all and mygrp2-list-all.
|
|
18 |
There are two command groups to implement i.e., *mygrp1* and *mygrp2*,
|
|
19 |
containing two commands each (*list_all*, *list_details* and *list_all*, *info*
|
|
20 |
respectively). To avoid ambiguities, command names are prefixed with the
|
|
21 |
command group they belong to, e.g., *mygrp1_list_all* and *mygrp2_list_all*.
|
|
22 |
The underscore is used to separate command namespaces.
|
|
21 | 23 |
|
22 |
The first command has the simplest possible syntax: no parameters, no runtime
|
|
23 |
arguments. The second accepts an optional runtime argument with a value. The
|
|
24 |
third features an optional argument and an optional runtime flag argument. The
|
|
25 |
last is an example of a command with an obligatory and an optional argument.
|
|
24 |
The first command (*mygrp1_list_all*) has the simplest possible syntax: no
|
|
25 |
parameters, no runtime arguments. The second accepts an optional runtime argument with a value. The third features an optional parameter and an optional
|
|
26 |
runtime flag argument. The last is an example of a command with an obligatory
|
|
27 |
and an optional parameter.
|
|
26 | 28 |
|
27 |
Samples of the expected behavior in one-command mode are following:
|
|
29 |
Examples of the expected behavior in one-command mode:
|
|
28 | 30 |
|
29 | 31 |
.. code-block:: console |
30 | 32 |
|
... | ... | |
35 | 37 |
- - - - |
36 | 38 |
list |
37 | 39 |
$ kamaki mygrp1 list |
38 |
Syntax Error |
|
39 | 40 |
|
40 | 41 |
Options |
41 | 42 |
- - - - |
42 | 43 |
all show a list |
43 | 44 |
details show a list of details |
44 | 45 |
$ kamaki mygrp1 list all |
45 |
... (mygrp1 client method is called) ...
|
|
46 |
... (a mygrp1_list_all instance runs) ...
|
|
46 | 47 |
$ kamaki mygrp2 list all 'Z[.]' -l |
47 |
... (mygrp2 client method is called) ...
|
|
48 |
... (a mygrp2_list_all instance runs) ...
|
|
48 | 49 |
$ |
49 | 50 |
|
50 |
The above example will be used throughout the present guide. |
|
51 |
|
|
52 | 51 |
The CommandTree structure |
53 | 52 |
------------------------- |
54 | 53 |
|
55 |
CommandTree manages a command by its path. Each command is stored in multiple |
|
56 |
nodes on the tree, so that the last term is a leaf and the route from root to |
|
57 |
that leaf represents the command path. For example the commands *file upload*, |
|
58 |
*file list* and *file info* are stored together as shown bellow:: |
|
54 |
CommandTree manages a command by its namespace. Each command is stored in |
|
55 |
a tree path, where each node is a name. A leaf is the end term of a namespace and contains a pointer to the command class to be executed. |
|
56 |
|
|
57 |
Here is an example from the actual kamaki command structure, where the commands |
|
58 |
*file upload*, *file list* and *file info* are represented as shown bellow:: |
|
59 | 59 |
|
60 | 60 |
- file |
61 | 61 |
''''''''|- info |
62 | 62 |
|- list |
63 | 63 |
|- upload |
64 | 64 |
|
65 |
The example used in the present, should result to the creation of two trees::
|
|
65 |
Now, let's load the showcase example on CommandTrees::
|
|
66 | 66 |
|
67 | 67 |
- mygrp1 |
68 | 68 |
''''''''|- list |
... | ... | |
74 | 74 |
'''''''|- all |
75 | 75 |
|- info |
76 | 76 |
|
77 |
Each command group should be stored on a different CommandTree. For that |
|
78 |
reason, command specification modules should contain a list of CommandTree |
|
79 |
objects, named *_commands* |
|
77 |
Each command group should be stored on a different CommandTree. |
|
78 |
|
|
79 |
For that reason, command specification modules should contain a list of CommandTree objects, named *_commands*. This mechanism allows any interface |
|
80 |
application to load the list of commands from the *_commands* array. |
|
80 | 81 |
|
81 |
A command group information (name, description) is provided at CommandTree |
|
82 |
structure initialization: |
|
82 |
The first name of the command path and a description (name, description) are needed to initializeg a CommandTree: |
|
83 | 83 |
|
84 | 84 |
.. code-block:: python |
85 | 85 |
|
... | ... | |
88 | 88 |
|
89 | 89 |
_commands = [_mygrp1_commands, _mygrp2_commands] |
90 | 90 |
|
91 |
|
|
91 | 92 |
The command decorator |
92 | 93 |
--------------------- |
93 | 94 |
|
94 |
The *command* decorator mines all the information necessary to build a command |
|
95 |
specification which is then inserted in a CommanTree instance:: |
|
95 |
All commands are specified by subclasses of *kamaki.cli.commands._command_init* |
|
96 |
These classes are called "command specifications". |
|
97 |
|
|
98 |
The *command* decorator mines all the information needed to build a namespace |
|
99 |
from a command specification:: |
|
96 | 100 |
|
97 | 101 |
class code ---> command() --> updated CommandTree structure |
98 | 102 |
|
99 |
Kamaki interfaces make use of this CommandTree structure. Optimizations are
|
|
103 |
Kamaki interfaces make use of the CommandTree structure. Optimizations are
|
|
100 | 104 |
possible by using special parameters on the command decorator method. |
101 | 105 |
|
102 | 106 |
.. code-block:: python |
... | ... | |
108 | 112 |
|
109 | 113 |
:param prefix: of the commands allowed to be inserted ('' for all) |
110 | 114 |
|
111 |
:param descedants_depth: is the depth of the tree descedants of the |
|
115 |
:param descedants_depth: is the depth of the tree descendants of the
|
|
112 | 116 |
prefix command. |
113 | 117 |
""" |
114 | 118 |
|
115 | 119 |
Creating a new command specification set |
116 | 120 |
---------------------------------------- |
117 | 121 |
|
118 |
A command specification developer should create a new module (python file) with as many classes as the command specifications to be offered. Each class should be decorated with *command*. |
|
122 |
A command specification developer should create a new module (python file) with |
|
123 |
one command specification class per command. Each class should be decorated |
|
124 |
with *command*. |
|
119 | 125 |
|
120 | 126 |
.. code-block:: python |
121 | 127 |
|
... | ... | |
129 | 135 |
... |
130 | 136 |
|
131 | 137 |
A list of CommandTree structures must exist in the module scope, with the name |
132 |
_commands, as shown above. Different CommandTree objects correspond to
|
|
133 |
different command groups.
|
|
138 |
*_commands*. Different CommandTree objects correspond to different command
|
|
139 |
groups. |
|
134 | 140 |
|
135 |
Get command description
|
|
141 |
Set command description
|
|
136 | 142 |
----------------------- |
137 | 143 |
|
138 | 144 |
The description of each command is the first line of the class commend. The |
... | ... | |
143 | 149 |
|
144 | 150 |
... |
145 | 151 |
@command(_mygrp2_commands) |
146 |
class mygrp2_info() |
|
147 |
"""get information for subject with id""" |
|
152 |
class mygrp2_info(): |
|
153 |
"""get information for subject with id |
|
154 |
Anything from this point and bellow constitutes the long description |
|
155 |
Please, mind the indentation, pep8 is not forgiving. |
|
156 |
""" |
|
148 | 157 |
... |
149 | 158 |
|
159 |
Description placeholders |
|
160 |
------------------------ |
|
161 |
|
|
162 |
There is possible to create an empty command, that can act as a description |
|
163 |
placeholder. For example, the *mygrp1_list* namespace does not correspond to an |
|
164 |
executable command, but it can have a helpful description. In that case, create |
|
165 |
a command specification class with a command and no code: |
|
166 |
|
|
167 |
.. code-block:: python |
|
168 |
|
|
169 |
@command(_mygrp1_commands) |
|
170 |
class mygrp1_list(): |
|
171 |
"""List mygrp1 objects. |
|
172 |
There are two versions: short and detailed |
|
173 |
""" |
|
174 |
|
|
175 |
.. warning:: A command specification class with no description is invalid and |
|
176 |
will cause an error. |
|
177 |
|
|
150 | 178 |
Declare run-time argument |
151 | 179 |
------------------------- |
152 | 180 |
|
153 |
The argument mechanism allows the definition of run-time arguments. Some basic |
|
154 |
argument types are defined at the |
|
181 |
A special argument mechanism allows the definition of run-time arguments. This |
|
182 |
mechanism is based on argparse and is designed to simplify argument definitions |
|
183 |
when specifying commands. |
|
184 |
|
|
185 |
Some basic argument types are defined at the |
|
155 | 186 |
`argument module <code.html#module-kamaki.cli.argument>`_, but it is not |
156 |
uncommon to extent these classes in order to achieve specialized type checking
|
|
157 |
and syntax control (e.g. at
|
|
158 |
`pithos cli module <code.html#module-kamaki.cli.commands.pithos>`_).
|
|
187 |
a bad idea to extent these classes in order to achieve specialized type
|
|
188 |
checking and syntax control. Still, in most cases, the argument types of the
|
|
189 |
argument package are enough for most cases.
|
|
159 | 190 |
|
160 |
To declare a run-time argument on a specific command, the object class should
|
|
161 |
initialize a dict called *arguments* , where Argument objects are stored. Each
|
|
162 |
argument object is a possible run-time argument. Syntax checking happens at
|
|
163 |
client level, while the type checking is implemented in the Argument code
|
|
164 |
(thus, many different Argument types might be needed).`
|
|
191 |
To declare a run-time argument on a specific command, the specification class
|
|
192 |
should contain a dict called *arguments* , where Argument objects are stored.
|
|
193 |
Each argument object is a run-time argument. Syntax checking happens at client
|
|
194 |
level, while the type checking is implemented in the Argument code (e.g.,
|
|
195 |
IntArgument checks if the value is an int).
|
|
165 | 196 |
|
166 | 197 |
.. code-block:: python |
167 | 198 |
|
... | ... | |
190 | 221 |
|
191 | 222 |
arguments = dict( |
192 | 223 |
match=ValueArgument( |
193 |
'Filter output to match string', ('-m', --match')) |
|
224 |
'Filter output to match string', ('-m', --match'))
|
|
194 | 225 |
) |
195 | 226 |
|
196 | 227 |
Accessing run-time arguments |
197 | 228 |
---------------------------- |
198 | 229 |
|
199 |
To access run-time arguments, users can use the _command_init interface, which
|
|
200 |
implements __item__ accessors to handle run-time argument values. In specific,
|
|
201 |
an instance of _command_init can use brackets to set or read <argument>.value .
|
|
230 |
To access run-time arguments, users can use the *_command_init* interface,
|
|
231 |
which implements *__item__* accessors to handle run-time argument values. In
|
|
232 |
other words, one may get the value of an argument with *self[<argument>]*.
|
|
202 | 233 |
|
203 | 234 |
.. code-block:: python |
204 | 235 |
|
... | ... | |
222 | 253 |
The main method and command parameters |
223 | 254 |
-------------------------------------- |
224 | 255 |
|
225 |
The command behavior for each command / class is coded in *main*. The |
|
226 |
parameters of *main* method defines the command parameters part of the syntax. |
|
227 |
In specific:: |
|
256 |
The command behavior for each command class is coded in *main*. The |
|
257 |
parameters of *main* method affect the syntax of the command. In specific:: |
|
228 | 258 |
|
229 | 259 |
main(self, param) - obligatory parameter <param> |
230 | 260 |
main(self, param=None) - optional parameter [param] |
... | ... | |
236 | 266 |
main(self, *args) - arbitary number of params [...] |
237 | 267 |
main(self, param1____param2, *args) - <param1:param2> [...] |
238 | 268 |
|
239 |
The information that can be mined by *command* for each individual command is
|
|
240 |
presented in the following:
|
|
269 |
Let's have a look at the command specification class again, and highlight the
|
|
270 |
parts that affect the command syntax:
|
|
241 | 271 |
|
242 | 272 |
.. code-block:: python |
243 | 273 |
:linenos: |
... | ... | |
245 | 275 |
from kamaki.cli.argument import FlagArgument |
246 | 276 |
... |
247 | 277 |
|
248 |
_commands = [_mygrp1_commands, _mygrp2=commands]
|
|
278 |
_commands = [_mygrp1_commands, _mygrp2_commands]
|
|
249 | 279 |
... |
250 | 280 |
|
251 | 281 |
@command(_mygrp2_commands) |
252 | 282 |
class mygrp2_list_all(): |
253 |
"""List all subjects""" |
|
283 |
"""List all subjects |
|
284 |
Refers to the subject accessible by current user |
|
285 |
""" |
|
254 | 286 |
|
255 | 287 |
arguments = dict(FlagArgument('detailed list', '-l')) |
256 | 288 |
|
257 | 289 |
def main(self, reg_exp=None): |
258 | 290 |
... |
259 | 291 |
|
260 |
This will load the following information on the CommandTree: |
|
292 |
The above lines contain the following information: |
|
293 |
|
|
294 |
* Namespace and name (line 8): mygrp2 list all |
|
295 |
* Short (line 9) and long (line 10) description |
|
296 |
* Parameters (line 15): [reg exp] |
|
297 |
* Runtime arguments (line 13): [-l] |
|
298 |
* Runtime arguments help (line 13): detailed list |
|
261 | 299 |
|
262 |
* Syntax (from lines 8,12,19): mygrp list all [reg exp] [-l] |
|
263 |
* Description (form line 9): List all subjects |
|
264 |
* Arguments help (from line 13,14): -l: detailed list |
|
300 |
.. tip:: It is suggested to code the main functionality in a member method |
|
301 |
called *_run*. This allows the separation between syntax and logic. For |
|
302 |
example, an external library may need to call a command without caring |
|
303 |
about its command line behavior. |
|
265 | 304 |
|
266 | 305 |
Letting kamaki know |
267 | 306 |
------------------- |
... | ... | |
270 | 309 |
option. To demonstrate this, let the command specifications coded above be |
271 | 310 |
stored in a file named *grps.py*. |
272 | 311 |
|
273 |
The developer should move file *grps.py* to kamaki/cli/commands, the default |
|
274 |
place for command specifications, although running a command specification from |
|
275 |
a different path is also a kamaki feature. |
|
312 |
The developer should move the file *grps.py* to *kamaki/cli/commands*, the |
|
313 |
default place for command specifications |
|
276 | 314 |
|
277 |
The user has to use a configuration file where the following is added: |
|
315 |
These lines should be contained in the kamaki configuration file for a new |
|
316 |
command specification module to work: |
|
278 | 317 |
:: |
279 | 318 |
|
280 | 319 |
[global] |
... | ... | |
288 | 327 |
$ kamaki config set mygrp1_cli grps |
289 | 328 |
$ kamaki config set mygrp2_cli grps |
290 | 329 |
|
291 |
Command specification modules don't need to live in kamaki/cli/commands, |
|
292 |
although this is suggested for uniformity. If a command module exist in another |
|
293 |
path:: |
|
330 |
.. note:: running a command specification from a different path is supported. |
|
331 |
To achieve this, add a *<group>_cli = </path/to/module>* line in the |
|
332 |
configure file under the *global* section. |
|
333 |
:: |
|
294 | 334 |
|
295 | 335 |
[global] |
296 | 336 |
mygrp_cli = /another/path/grps.py |
... | ... | |
320 | 360 |
|
321 | 361 |
|
322 | 362 |
@command(_mygrp1_commands) |
363 |
class mygrp1_list(_command_init): |
|
364 |
"""List mygrp1 objects. |
|
365 |
There are two versions: short and detailed |
|
366 |
""" |
|
367 |
|
|
368 |
|
|
369 |
@command(_mygrp1_commands) |
|
323 | 370 |
class mygrp1_list_all(_command_init): |
324 | 371 |
"""show a list""" |
325 | 372 |
|
326 |
def main(self):
|
|
373 |
def _run():
|
|
327 | 374 |
... |
328 | 375 |
|
376 |
def main(self): |
|
377 |
self._run() |
|
378 |
|
|
329 | 379 |
|
330 | 380 |
@command(_mygrp1_commands) |
331 | 381 |
class mygrp1_list_details(_command_init): |
... | ... | |
336 | 386 |
'Filter output to match string', ('-m', --match')) |
337 | 387 |
) |
338 | 388 |
|
339 |
def main(self): |
|
340 |
... |
|
389 |
def _run(self): |
|
341 | 390 |
match_value = self['match'] |
342 | 391 |
... |
343 | 392 |
|
393 |
def main(self): |
|
394 |
self._run() |
|
395 |
|
|
396 |
|
|
397 |
#The following will also create a mygrp2_list command with no description |
|
398 |
|
|
344 | 399 |
|
345 | 400 |
@command(_mygrp2_commands) |
346 | 401 |
class mygrp2_list_all(_command_init): |
... | ... | |
350 | 405 |
list=FlagArgument('detailed listing', '-l') |
351 | 406 |
) |
352 | 407 |
|
353 |
def main(self, regular_expression=None): |
|
354 |
... |
|
355 |
detail_flag = self['list'] |
|
408 |
def _run(self, regexp): |
|
356 | 409 |
... |
357 |
if detail_flag:
|
|
410 |
if self['list']:
|
|
358 | 411 |
... |
359 |
... |
|
360 |
if regular_expression: |
|
412 |
else: |
|
361 | 413 |
... |
362 |
... |
|
414 |
|
|
415 |
def main(self, regular_expression=None): |
|
416 |
self._run(regular_expression) |
|
363 | 417 |
|
364 | 418 |
|
365 | 419 |
@command(_mygrp2_commands) |
366 | 420 |
class mygrp2_info(_command_init): |
367 | 421 |
"""get information for subject with id""" |
368 | 422 |
|
369 |
def main(self, id, name=''):
|
|
423 |
def _run(self, grp_id, grp_name):
|
|
370 | 424 |
... |
425 |
|
|
426 |
def main(self, id, name=''): |
|
427 |
self._run(id, name) |
b/docs/developers/clients-api.rst | ||
---|---|---|
3 | 3 |
|
4 | 4 |
Kamaki features a clients API for building third-party client applications that |
5 | 5 |
communicate with OpenStack and / or Synnefo cloud services. The package is |
6 |
called kamaki.clients and servers as a lib.
|
|
6 |
called *kamaki.clients* and serves as a lib.
|
|
7 | 7 |
|
8 |
A showcase of an application built on kamaki.clients is kamaki.cli, the command
|
|
9 |
line interface of kamaki. |
|
8 |
A showcase of an application built on *kamaki.clients* is *kamaki.cli*, the
|
|
9 |
command line interface of kamaki.
|
|
10 | 10 |
|
11 | 11 |
Since Synnefo services are build as OpenStack extensions, an inheritance |
12 | 12 |
approach has been chosen for implementing clients for both. In specific, |
13 |
the *compute*, *storage* and *image* modules are clients of the OS compute, OS
|
|
14 |
object-store, respectively. On the contrary, all the other modules are Synnefo
|
|
15 |
extensions (*cyclades* extents *compute*, *pithos* and *pithos_rest_api*
|
|
16 |
extent *storage*) or novel Synnefo services (e.g., *astakos* for IM, *image*
|
|
17 |
for *plankton*). |
|
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 | 18 |
|
19 | 19 |
Setup a client instance |
20 | 20 |
----------------------- |
21 | 21 |
|
22 |
External applications may instantiate one or more kamaki clients. |
|
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. |
|
23 | 26 |
|
24 | 27 |
.. code-block:: python |
25 | 28 |
:emphasize-lines: 1 |
26 | 29 |
|
27 |
Example 1.1: Instantiate Cyclades and Pithos client |
|
30 |
Example 1.1: Instantiate Cyclades and Pithos clients
|
|
28 | 31 |
|
29 | 32 |
|
30 | 33 |
from kamaki.clients.cyclades import CycladesClient |
... | ... | |
33 | 36 |
my_cyclades_client = CycladesClient(base_url, token) |
34 | 37 |
my_pithos_client = PithosClient(base_url, token, account, container) |
35 | 38 |
|
36 |
.. note:: *cyclades* and *pithos* clients inherit all methods of *compute*
|
|
37 |
and *storage* clients respectively. Separate compute or storage objects
|
|
38 |
should be used only when implementing applications for strict OS Compute or
|
|
39 |
OS Storage services.
|
|
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.
|
|
40 | 43 |
|
41 | 44 |
Using endpoints to get the base_url |
42 | 45 |
----------------------------------- |
43 | 46 |
|
44 |
In OpenStack, each service (e.g. `compute`, `object-store`, etc.) has a number |
|
45 |
of `endpoints`. These `endpoints` are actually URIs that are needed as prefixes
|
|
46 |
for the API calls the kamaki client generates. In this context, we need just
|
|
47 |
one of these these `endpoints`, the `publicURL`, which is also referred to as
|
|
48 |
`base_url` in kamaki client libraries. |
|
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.
|
|
49 | 52 |
|
50 |
In general, this is the suggested way of getting the base_url::
|
|
53 |
Here are instructions for getting the base_url for a service::
|
|
51 | 54 |
|
52 | 55 |
1. From the deployment UI get the AUTHENTICATION_URL and TOKEN |
53 | 56 |
(Example 1.2) |
... | ... | |
59 | 62 |
(Example 1.3) |
60 | 63 |
|
61 | 64 |
The AstakosClient is a client for the Synnefo/Astakos server. Synnefo/Astakos |
62 |
is an advanced identity server based on OpenStack identity specifications.
|
|
63 |
Therefore, it can be used to get the `base_url` values needed for initializing
|
|
64 |
kamaki clients. Kamaki simplifies this process with the astakos client library.
|
|
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. |
|
65 | 68 |
|
66 | 69 |
Let's review the process with examples. |
67 | 70 |
|
... | ... | |
93 | 96 |
pithos_endpoints = my_astakos_client.get_service_endpoints('object-store') |
94 | 97 |
pithos_base_url = pithos_endpoints['publicURL'] |
95 | 98 |
|
96 |
The ``get_service_endpoints`` method gets the service name as an argument. Here
|
|
97 |
are the service names for the most popular kamaki clients::
|
|
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::
|
|
98 | 101 |
|
99 |
storage, pithos --> object-store
|
|
100 |
compute, cyclades --> compute
|
|
101 |
image --> image |
|
102 |
astakos --> identity
|
|
102 |
storage.StorageClient, pithos.PithosClient --> object-store
|
|
103 |
compute.ComputeClient, cyclades.CycladesClient --> compute
|
|
104 |
image.ImageClient --> image
|
|
105 |
astakos.AstakosClient --> identity, account
|
|
103 | 106 |
|
104 | 107 |
Use client methods |
105 | 108 |
------------------ |
106 | 109 |
|
107 |
Client methods can now be called. Developers are advised to |
|
108 |
consult :ref:`the-client-api-ref` for details on the available methods and how |
|
109 |
to use them. |
|
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` |
|
110 | 116 |
|
111 | 117 |
In the following example, the *cyclades* and *pithos* clients of example 1.1 |
112 |
are used to extract some information, that is then printed to the standard |
|
113 |
output. |
|
118 |
are used to extract some information through the remote service APIs. The information is then printed to the standard output. |
|
114 | 119 |
|
115 | 120 |
|
116 | 121 |
.. code-block:: python |
... | ... | |
119 | 124 |
Example 1.4: Print server name and OS for server with server_id |
120 | 125 |
Print objects in container mycont |
121 | 126 |
|
122 |
|
|
123 | 127 |
srv = my_cyclades_client.get_server_info(server_id) |
124 | 128 |
print("Server Name: %s (with OS %s" % (srv['name'], srv['os'])) |
125 | 129 |
|
... | ... | |
130 | 134 |
.. code-block:: console |
131 | 135 |
:emphasize-lines: 1 |
132 | 136 |
|
133 |
Run of examples 1.1 + 1.4
|
|
137 |
* A run of examples 1.1 + 1.4 *
|
|
134 | 138 |
|
135 | 139 |
|
136 | 140 |
$ python test_script.py |
... | ... | |
143 | 147 |
Error handling |
144 | 148 |
-------------- |
145 | 149 |
|
146 |
The kamaki.clients standard error is ClientError. A ClientError is raised for
|
|
147 |
any kind of kamaki.clients errors (errors reported by servers, type errors in
|
|
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
|
|
148 | 152 |
arguments, etc.). |
149 | 153 |
|
150 | 154 |
A ClientError contains:: |
151 | 155 |
|
152 | 156 |
message The error message. |
153 |
status An optional error code, e.g. after a server error. |
|
157 |
status An optional error code, e.g., after a server error.
|
|
154 | 158 |
details Optional list of messages with error details. |
155 | 159 |
|
156 | 160 |
The following example concatenates examples 1.1 to 1.4 plus error handling |
... | ... | |
159 | 163 |
|
160 | 164 |
Example 1.5: Error handling |
161 | 165 |
|
166 |
from kamaki.clients import ClientError |
|
167 |
|
|
162 | 168 |
from kamaki.clients.astakos import AstakosClient |
163 | 169 |
from kamaki.clients.cyclades import CycladesClient |
164 | 170 |
from kamaki.clients.pithos import PithosClient |
165 | 171 |
|
166 | 172 |
try: |
167 | 173 |
my_astakos_client = AstakosClient(AUTHENTICATION_URL, TOKEN) |
174 |
my_astakos_client.authenticate() |
|
168 | 175 |
except ClientError: |
169 | 176 |
print('Failed to authenticate user token') |
170 | 177 |
return 1 |
... | ... | |
297 | 304 |
print 'Image %s registered with id %s' % (r['name'], r['id']) |
298 | 305 |
except ClientError: |
299 | 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']) |
b/docs/developers/extending-clients-api.rst | ||
---|---|---|
1 | 1 |
Extending kamaki.clients |
2 | 2 |
======================== |
3 | 3 |
|
4 |
By default, kamaki clients are REST clients (they manage HTTP requests and
|
|
5 |
responses to communicate with services).
|
|
4 |
By default, kamaki clients implement REST APIs, therefore they manage HTTP
|
|
5 |
requests and responses to communicate with services.
|
|
6 | 6 |
|
7 | 7 |
How to build a client |
8 | 8 |
--------------------- |
... | ... | |
17 | 17 |
|
18 | 18 |
from kamaki.clients import Client, ClientError |
19 | 19 |
|
20 |
|
|
20 | 21 |
class MyNewClient(Client): |
21 | 22 |
"""MyNewClient Description Here""" |
22 | 23 |
|
... | ... | |
100 | 101 |
----------- |
101 | 102 |
|
102 | 103 |
The kamaki.clients package contains a set of fine-grained unit-tests for all |
103 |
its packages.
|
|
104 |
APIs.
|
|
104 | 105 |
|
105 | 106 |
.. note:: unit tests require the optional python-mock package, version 1.X or |
106 | 107 |
better |
... | ... | |
123 | 124 |
|
124 | 125 |
$ python test.py |
125 | 126 |
|
126 |
To test a specific class, add the class name as an argument. E.g. for the
|
|
127 |
To test a specific class, add the class name as an argument. e.g., for the
|
|
127 | 128 |
Client class: |
128 | 129 |
|
129 | 130 |
.. code-block:: console |
130 | 131 |
|
131 | 132 |
$ python test.py Client |
132 | 133 |
|
133 |
To test a specific method in a class, apply an extra argument, e.g. for the |
|
134 |
To test a specific method in a class, apply an extra argument, e.g., for the
|
|
134 | 135 |
request method in the Client class: |
135 | 136 |
|
136 | 137 |
.. code-block:: console |
... | ... | |
138 | 139 |
$ python test.py Client request |
139 | 140 |
|
140 | 141 |
Each package contains a test module (test.py) which is also runnable from the |
141 |
command line. E.g. in the pithos package there is a test module which
|
|
142 |
command line. e.g., in the pithos package there is a test module which
|
|
142 | 143 |
contains, among others, the **download** sub-test: |
143 | 144 |
|
144 | 145 |
.. code-block:: console |
... | ... | |
154 | 155 |
# Test kamaki.clients.pithos.PithosClient.download |
155 | 156 |
$ python test.py Pithos download |
156 | 157 |
|
157 |
To fully test a specific package, run test.py from the package location. E.g.
|
|
158 |
To fully test a specific package, run test.py from the package location. e.g.,
|
|
158 | 159 |
to test everything in kamaki.clients.pithos package: |
159 | 160 |
|
160 | 161 |
.. code-block:: console |
... | ... | |
169 | 170 |
of this package. All test modules contain a set of classes that extent the |
170 | 171 |
TestCase class. They also contain a main method to run the tests. |
171 | 172 |
|
172 |
By convention, testing classes are named as <Tested Class> where <Test Class>
|
|
173 |
is the name of the tested class or module. Methods not grouped in classes are
|
|
174 |
tested by classes named after their respective module.
|
|
173 |
By convention, testing classes have the same name as the class they test.
|
|
174 |
Methods not grouped in classes are tested by classes named after their
|
|
175 |
respective module. |
|
175 | 176 |
|
176 |
For example, the kamaki.clients.pithos.PithosClient class is tested by the |
|
177 |
kamaki.clients.pithos.test.PithosClient class, while the methods in |
|
178 |
kamaki.clients.utils module are tested by the kamaki.clients.utils.test.Utils |
|
179 |
testing class. |
|
177 |
For example, the *kamaki.clients.pithos.PithosClient* class is tested by the |
|
178 |
*kamaki.clients.pithos.test.PithosClient* class, while the methods in |
|
179 |
*kamaki.clients.utils* module are tested by *kamaki.clients.utils.test.Utils*. |
|
180 | 180 |
|
181 | 181 |
Adding unit tests |
182 | 182 |
^^^^^^^^^^^^^^^^^ |
183 | 183 |
|
184 |
After modifying or extending kamaki.clients method, classes, modules or
|
|
184 |
After modifying or extending *kamaki.clients* method, classes, modules or
|
|
185 | 185 |
packages, it is a good practice to also modify or extend the corresponding |
186 | 186 |
unit tests. What's more, it is recommended to modify or implement the testing |
187 |
of new behavior before implementing the behavior itself. The aim for
|
|
188 |
kamaki.clients package is an 1 to 1 mapping between methods and their tests.
|
|
187 |
of new behavior before implementing the behavior itself. The goal is to
|
|
188 |
preserve an 1 to 1 mapping between methods and their tests.
|
|
189 | 189 |
|
190 | 190 |
Modifying an existing method |
191 | 191 |
"""""""""""""""""""""""""""" |
... | ... | |
195 | 195 |
test module under the same package, in a TestCase subclass that is named with a |
196 | 196 |
name similar to the package or class that contains the tested method. |
197 | 197 |
|
198 |
Example 1: to modify the kamaki.clients.utils.filter_in method, the programmer |
|
199 |
has to also adjust the kamaki.clients.utils.test.Utils.test_filter_in method. |
|
200 |
|
|
201 |
Example 2: to modify the kamaki.clients.pithos.PithosRestClient.object_get, the |
|
198 |
Example: to modify *kamaki.clients.pithos.PithosRestClient.object_get*, the |
|
202 | 199 |
programmer has to also adjust the |
203 |
kamaki.clients.pithos.test.PithosRestClient.test_object_get method.
|
|
200 |
*kamaki.clients.pithos.test.PithosRestClient.test.object_get* method.
|
|
204 | 201 |
|
205 | 202 |
Adding a new method |
206 | 203 |
""""""""""""""""""" |
207 | 204 |
|
208 |
Programmers who want to implement a new method in an existing class, are |
|
209 |
encouraged to implement the corresponding unit test first. In order to do that, |
|
210 |
they should find the testing class that is mapped to the class or module they |
|
211 |
need to extend. |
|
205 |
To implement a new method in an existing class, developers are encouraged to |
|
206 |
implement the corresponding unit test first. In order to do that, they should |
|
207 |
find the testing class that is mapped to the class or module they working on. |
|
212 | 208 |
|
213 |
Example 1: To add a **list_special** method to
|
|
214 |
kamaki.clients.astakos.AstakosClient, extend the
|
|
215 |
kamaki.clients.astakos.test.AstakosClient class, as shown bellow:
|
|
209 |
Example: Adding **list_special** to *kamaki.clients.astakos.AstakosClient*,
|
|
210 |
requires changes to *kamaki.clients.astakos.test.AstakosClient* too, as shown
|
|
211 |
bellow: |
|
216 | 212 |
|
217 | 213 |
.. code-block:: python |
218 | 214 |
|
... | ... | |
224 | 220 |
"""Test the list_special method""" |
225 | 221 |
... |
226 | 222 |
|
227 |
Example 2: To add a **get_random_int** method in kamaki.clients.utils module, |
|
228 |
extend the kamaki.clients.utils.test.Utils test class, as shown bellow: |
|
229 |
|
|
230 |
.. code-block:: python |
|
231 |
|
|
232 |
# file: ${kamaki}/kamaki/clients/utils/__init__.py |
|
233 |
|
|
234 |
class Utils(TestCase): |
|
235 |
... |
|
236 |
def test_get_random_int(self): |
|
237 |
"""Test the get_random_int method""" |
|
238 |
... |
|
239 |
|
|
240 | 223 |
Implementing a new class or module |
241 | 224 |
"""""""""""""""""""""""""""""""""" |
242 | 225 |
|
243 | 226 |
Each class or module needs a seperate test sub-module. By convention, each |
244 |
class or module under the kamaki.clients should be located in a separate
|
|
227 |
class or module under *kamaki.clients*, should be located in a separate
|
|
245 | 228 |
directory. |
246 | 229 |
|
247 |
Example 1: To add a NewService class that implements the kamaki.clients.Client |
|
248 |
interface: |
|
230 |
Example 1: To add a NewService class that implements *kamaki.clients.Client*: |
|
249 | 231 |
|
250 |
* create a new_service package and implement the unit tests in the kamaki.clients.new_service.test module: |
|
232 |
* create a new_service package and implement the unit tests in |
|
233 |
*kamaki.clients.new_service.test*: |
|
251 | 234 |
|
252 | 235 |
.. code-block:: console |
253 | 236 |
|
254 | 237 |
$ mkdir new_service && touch new_service/test.py |
255 | 238 |
|
256 |
* create the file that will hold the package code and implement the module there:
|
|
239 |
* create the file to code the package implementation:
|
|
257 | 240 |
|
258 | 241 |
.. code-block:: console |
259 | 242 |
|
260 | 243 |
$ touch new_service/__init__.py |
261 | 244 |
|
262 |
* Create the test class and methods in kamaki.clients.new_service.test
|
|
245 |
* Create the test class and methods in *kamaki.clients.new_service.test*
|
|
263 | 246 |
|
264 | 247 |
.. code-block:: python |
265 | 248 |
|
... | ... | |
283 | 266 |
def method1(self, ...): |
284 | 267 |
... |
285 | 268 |
|
286 |
* Expose the new tests to top test module, by importing the test class to kamaki.clients.test
|
|
269 |
* Import the test class to *kamaki.clients.test*:
|
|
287 | 270 |
|
288 |
..code-block:: python |
|
271 |
.. code-block:: python
|
|
289 | 272 |
|
290 | 273 |
# file: ${kamaki}/kamaki/clients/test.py |
291 |
|
|
292 | 274 |
from kamaki.clients.new_service.test import NewService |
293 | 275 |
|
294 | 276 |
.. note:: If the new class or module is part of an existing sub-package, it is |
b/docs/developers/logging.rst | ||
---|---|---|
1 | 1 |
Logging |
2 | 2 |
======= |
3 | 3 |
|
4 |
Kamaki uses the standard Python logger package to log some of its |
|
5 |
functionality. |
|
4 |
Kamaki uses the standard Python logger package to log some of its functionality. |
|
6 | 5 |
|
7 |
All kamaki loggers are named or prefixed after the package they log, e.g. |
|
8 |
a logger at `kamaki/cli/argument.__init__.py` should be called
|
|
6 |
All kamaki loggers are named or prefixed after the package they log, e.g.,
|
|
7 |
a logger at `kamaki/cli/argument/__init__.py` should be called
|
|
9 | 8 |
`kamaki.cli.argument` whereas a logger at `kamaki/clients/conf.py` should be |
10 |
named `kamaki.clients/conf`. In `kamaki/clients/__init__.py` there are two
|
|
9 |
named `kamaki.clients.conf`. In `kamaki/clients/__init__.py` there are two
|
|
11 | 10 |
loggers that use the package name as prefix, and they detailed bellow. |
12 | 11 |
|
13 | 12 |
Monitor requests and responses |
14 | 13 |
------------------------------ |
15 | 14 |
|
16 | 15 |
The `kamaki.clients` logger contains two subloggers that monitor the HTTP |
17 |
communication of with the servers::
|
|
16 |
API calls::
|
|
18 | 17 |
|
19 | 18 |
kamaki.clients.send for kamaki requests to the server |
20 | 19 |
kamaki.clients.recv for server responses to kamaki |
21 | 20 |
|
22 | 21 |
These are the only loggers used for purposes other than mere debugging. Both |
23 | 22 |
loggers are defined in the CLI code and are used to (a) log HTTP communication |
24 |
to the log file as well as to (b) show users the HTTP requests and responses if
|
|
25 |
kamaki cli is called with options like "verbose" or "debug".
|
|
23 |
to the log file as well as to (b) show users the HTTP requests and responses in
|
|
24 |
"verbose" or "debug" modes.
|
|
26 | 25 |
|
27 | 26 |
Logger in external code |
28 | 27 |
----------------------- |
... | ... | |
68 | 67 |
< content-type: application/json; charset=UTF-8 |
69 | 68 |
< data size: 2425 |
70 | 69 |
|
71 |
.. note:: user token and http body content are not logged by default |
|
70 |
.. note:: user token and http body content are not logged by default. This can |
|
71 |
be switched on and off by modifing the *kamaki.client.Client.LOG_TOKEN* and |
|
72 |
*kamaki.client.Client.LOG_DATA* flags |
|
72 | 73 |
|
73 | 74 |
As a second example, let's suppose that we need to see only the http requests |
74 |
of the `pithos.list_objects()` method. We decide to print these to the console.
|
|
75 |
To achieve that goal, we should get a stream logger and deactivate it when we
|
|
76 |
do not need it anymore.
|
|
75 |
of the `pithos.list_objects()` method and print these to stdout. To achieve
|
|
76 |
that goal, we should get a stream logger and deactivate it when we do not need
|
|
77 |
it anymore. |
|
77 | 78 |
|
78 | 79 |
|
79 | 80 |
.. code-block:: python |
b/docs/examplesdir/imageregister.rst | ||
---|---|---|
10 | 10 |
|
11 | 11 |
<container>:<object path> |
12 | 12 |
|
13 |
e.g.: |
|
13 |
e.g.,:
|
|
14 | 14 |
|
15 | 15 |
pithos:debian_base3.diskdump |
16 | 16 |
|
... | ... | |
301 | 301 |
|
302 | 302 |
We can use the same idea to change the values of other metadata like disk |
303 | 303 |
format, container format or status. On the other hand, we cannot modify the |
304 |
id, owner, location, checksum and dates. E.g., to publish and unpublish:
|
|
304 |
id, owner, location, checksum and dates. e.g., to publish and unpublish:
|
|
305 | 305 |
|
306 | 306 |
.. code-block:: console |
307 | 307 |
|
... | ... | |
552 | 552 |
|
553 | 553 |
#!/bin/bash |
554 | 554 |
|
555 |
userid=... # e.g. s0m3-u53r-1d |
|
556 |
container=... # e.g. pithos |
|
555 |
userid=... # e.g., s0m3-u53r-1d
|
|
556 |
container=... # e.g., pithos
|
|
557 | 557 |
|
558 | 558 |
for path in images/*.diskdump; do |
559 | 559 |
location=$container:${path} |
b/docs/examplesdir/server.rst | ||
---|---|---|
127 | 127 |
|
128 | 128 |
-p <local file path>[,<remote path>[,<remote owner>[,<remote group>[,<mode>]]]] |
129 | 129 |
|
130 |
e.g. |
|
130 |
e.g.,
|
|
131 | 131 |
|
132 | 132 |
-p /home/someuser/.ssh/id_rsa.pub,/root/.ssh/authorized_keys,root,root,0777 |
133 | 133 |
|
134 | 134 |
.. note:: In case of omitting an optional part of the personality string, the |
135 |
default behavior depends on the remote server, e.g. for a debian image we |
|
135 |
default behavior depends on the remote server, e.g., for a debian image we
|
|
136 | 136 |
expect the file to have root ownership, if the ownership is not specified. |
137 | 137 |
|
138 | 138 |
Create a virtual server while injecting current user public key to root account |
... | ... | |
194 | 194 |
|
195 | 195 |
.. note:: There is no reason to limit injections to ssh keys. Users with an |
196 | 196 |
adequate understanding of the remote OS are encouraged to prepare and |
197 |
inject all kinds of useful files, e.g. **lists of package sources**, |
|
197 |
inject all kinds of useful files, e.g., **lists of package sources**,
|
|
198 | 198 |
**default user profiles**, **device mount configurations**, etc. |
b/docs/index.rst | ||
---|---|---|
12 | 12 |
development API for managing clouds. |
13 | 13 |
|
14 | 14 |
As a development library, it implements the OpenStack APIs, |
15 |
along with custom extensions as used by Synnefo
|
|
15 |
along with the custom extensions introduced by Synnefo
|
|
16 | 16 |
(`Synnefo IaaS <http://www.synnefo.org/>`_). |
17 | 17 |
|
18 | 18 |
./kamaki is open source and released under a 2-clause BSD License. |
b/docs/installation.rst | ||
---|---|---|
27 | 27 |
|
28 | 28 |
.. warning:: Debian Squeeze users may replace "wheezy" with "squeeze" |
29 | 29 |
|
30 |
* Make sure the GPG public key for the Synnefo development team is added:
|
|
30 |
* Make sure the GPG public key for the Synnefo repository is added:
|
|
31 | 31 |
|
32 | 32 |
.. code-block:: console |
33 | 33 |
|
... | ... | |
86 | 86 |
|
87 | 87 |
$ sudo apt-get install python-mock |
88 | 88 |
|
89 |
.. warning:: kamaki.clients unit-tests need python-mock 1.X or better. e.g.:: |
|
89 |
.. warning:: kamaki.clients unit-tests need python-mock 1.X or better. e.g.,::
|
|
90 | 90 |
|
91 | 91 |
$ sudo apt-get install python-mock=1.0.1 |
92 | 92 |
|
... | ... | |
118 | 118 |
Setup a virtual enviroment (optional) |
119 | 119 |
""""""""""""""""""""""""""""""""""""" |
120 | 120 |
|
121 |
Use virtualenv to setup kamaki and Synnefo services in a sandbox |
|
122 |
environment. |
|
121 |
Use virtualenv to setup kamaki and Synnefo services in a sandbox environment. |
|
123 | 122 |
|
124 | 123 |
.. code-block:: console |
125 | 124 |
|
b/docs/overview.rst | ||
---|---|---|
5 | 5 |
------- |
6 | 6 |
|
7 | 7 |
Kamaki was created in 2011 by the `Synnefo <http://www.synnefo.org>`_ |
8 |
development team of the *Greek Research and Technology Network (GRNET)*,
|
|
8 |
development team at the *Greek Research and Technology Network (GRNET)*,
|
|
9 | 9 |
initially as an internal project and later as a multipurpose tool for all |
10 | 10 |
users. |
11 | 11 |
|
... | ... | |
24 | 24 |
Kamaki has been designed to act as a command line client as well as a python |
25 | 25 |
library for client developers. It is widely used in various Synnefo and okeanos |
26 | 26 |
components. Third parties are also encouraged to use the kamaki library for |
27 |
developing their own python-based cloud-client applications.
|
|
27 |
developing their own python-based cloud applications. |
|
28 | 28 |
|
29 | 29 |
As Synnefo became a full, scalable and stable cloud solution, kamaki also |
30 | 30 |
evolved to an intuitive, multipurpose tool, available to a wider user base. |
... | ... | |
42 | 42 |
|
43 | 43 |
* internally by the Synnefo development team to test the Synnefo software, |
44 | 44 |
|
45 |
* by the deployment team which operates GRNET's ~okeanos service
|
|
45 |
* by the deployment team which operates GRNET ~okeanos service |
|
46 | 46 |
|
47 | 47 |
* as the main Pithos+ client in Linux and other Unix-like environments |
48 | 48 |
|
b/docs/setup.rst | ||
---|---|---|
11 | 11 |
.. warning:: Users of kamaki 0.8.X or older should consult the |
12 | 12 |
`migration guide <#migrating-from-kamaki-0-8-x-to-0-9-or-better>`_ first. |
13 | 13 |
|
14 |
Kamaki has to be configured for a specific Synnefo deployment, with an
|
|
15 |
authentication url and user token pair. Users should also pick an alias to name
|
|
16 |
the cloud configuration. This can be any single word, e.g., "default", |
|
14 |
To set up Kamaki for a specific Synnefo deployment, users need an
|
|
15 |
**authentication URL** and a **user token**. Users should also pick an alias to
|
|
16 |
name the cloud configuration. This can be any single word, e.g., "default",
|
|
17 | 17 |
"mycloud"or whatever suits the user. |
18 | 18 |
|
19 | 19 |
.. code-block:: console |
... | ... | |
21 | 21 |
$ kamaki config set cloud.<cloud alias>.url <cloud-authentication-URL> |
22 | 22 |
$ kamaki config set cloud.<cloud alias>.token myt0k3n== |
23 | 23 |
|
24 |
If only one cloud is configured, kamaki automatically picks it as the default.
|
|
24 |
If only one cloud is configured, it is automatically considered the default.
|
|
25 | 25 |
Otherwise, a default cloud should be specified: |
26 | 26 |
|
27 | 27 |
.. code-block:: console |
... | ... | |
80 | 80 |
At this point, we should examine the kamaki output. Most options are renamed to |
81 | 81 |
match the latest configuration file version specifications. |
82 | 82 |
|
83 |
Let's take a look at the discarded options:
|
|
83 |
Lets take a look at the discarded options: |
|
84 | 84 |
|
85 | 85 |
* `global.account` and `user.account` are not used anymore. |
86 | 86 |
The same is true for the synonyms `store.account` and `pithos.account`. |
... | ... | |
118 | 118 |
--------------- |
119 | 119 |
|
120 | 120 |
The following refers to users of multiple Synnefo and/or Open Stack |
121 |
deployments. In the following, a Synnefo or Open Stack cloud deployment will
|
|
121 |
deployments. In the following, a Synnefo (or Open Stack) cloud deployment will
|
|
122 | 122 |
be called **a cloud**. |
123 | 123 |
|
124 | 124 |
Multiple clouds can be configured and manager in a single kamaki setup, since |
b/docs/usage.rst | ||
---|---|---|
40 | 40 |
|
41 | 41 |
In favor of interactive shell: |
42 | 42 |
|
43 |
* tab completion for commands (if supported by the user's shell)
|
|
44 |
* session history with ↑ or ↓ keys (if supported by the user's shell)
|
|
43 |
* tab completion for commands (if supported by the user shell) |
|
44 |
* session history with ↑ or ↓ keys (if supported by the user shell) |
|
45 | 45 |
* shorter commands with command context switching |
46 | 46 |
* re-run old commands with /history |
47 | 47 |
|
... | ... | |
248 | 248 |
filter by flavor id |
249 | 249 |
|
250 | 250 |
Details: |
251 |
Use filtering arguments (e.g. --name-like) to manage long server lists |
|
251 |
Use filtering arguments (e.g., --name-like) to manage long server lists
|
|
252 | 252 |
|
253 | 253 |
.. _using-history-ref: |
254 | 254 |
|
b/kamaki/cli/__init__.py | ||
---|---|---|
110 | 110 |
|
111 | 111 |
def command(cmd_tree, prefix='', descedants_depth=1): |
112 | 112 |
"""Load a class as a command |
113 |
e.g. spec_cmd0_cmd1 will be command spec cmd0 |
|
113 |
e.g., spec_cmd0_cmd1 will be command spec cmd0
|
|
114 | 114 |
|
115 | 115 |
:param cmd_tree: is initialized in cmd_spec file and is the structure |
116 | 116 |
where commands are loaded. Var name should be _commands |
b/kamaki/cli/command_tree/__init__.py | ||
---|---|---|
150 | 150 |
def find_best_match(self, terms): |
151 | 151 |
"""Find a command that best matches a given list of terms |
152 | 152 |
|
153 |
:param terms: (list of str) match against paths in cmd_tree, e.g. |
|
153 |
:param terms: (list of str) match against paths in cmd_tree, e.g.,
|
|
154 | 154 |
['aa', 'bb', 'cc'] matches aa_bb_cc |
155 | 155 |
|
156 | 156 |
:returns: (Command, list) the matching command, the remaining terms or |
b/kamaki/cli/commands/astakos.py | ||
---|---|---|
78 | 78 |
@command(user_cmds) |
79 | 79 |
class user_authenticate(_user_init, _optional_json): |
80 | 80 |
"""Authenticate a user |
81 |
Get user information (e.g. unique account name) from token |
|
81 |
Get user information (e.g., unique account name) from token
|
|
82 | 82 |
Token should be set in settings: |
83 | 83 |
* check if a token is set /config whoami cloud.default.token |
84 | 84 |
* permanently set a token /config set cloud.default.token <token> |
b/kamaki/cli/commands/cyclades.py | ||
---|---|---|
65 | 65 |
' SERVER_PATH: destination location inside server Image', |
66 | 66 |
' OWNER: virtual servers user id of the remote destination file', |
67 | 67 |
' GROUP: virtual servers group id or name of the destination file', |
68 |
' MODEL: permition in octal (e.g. 0777 or o+rwx)'] |
|
68 |
' MODEL: permition in octal (e.g., 0777 or o+rwx)']
|
|
69 | 69 |
|
70 | 70 |
|
71 | 71 |
class _service_wait(object): |
... | ... | |
136 | 136 |
@command(server_cmds) |
137 | 137 |
class server_list(_init_cyclades, _optional_json, _name_filter, _id_filter): |
138 | 138 |
"""List virtual servers accessible by user |
139 |
Use filtering arguments (e.g. --name-like) to manage long server lists |
|
139 |
Use filtering arguments (e.g., --name-like) to manage long server lists
|
|
140 | 140 |
""" |
141 | 141 |
|
142 | 142 |
PERMANENTS = ('id', 'name') |
... | ... | |
259 | 259 |
Contains: |
260 | 260 |
- name, id, status, create/update dates |
261 | 261 |
- network interfaces |
262 |
- metadata (e.g. os, superuser) and diagnostics |
|
262 |
- metadata (e.g., os, superuser) and diagnostics
|
|
263 | 263 |
- hardware flavor and os image ids |
264 | 264 |
""" |
265 | 265 |
|
b/kamaki/cli/commands/history.py | ||
---|---|---|
91 | 91 |
. 2. <order-id-1>-<order-id-2> : pick all commands ordered in the range |
92 | 92 |
. [<order-id-1> - <order-id-2>] |
93 | 93 |
. - the above can be mixed and repeated freely, separated by spaces |
94 |
. e.g. pick 2 4-7 -3 |
|
95 |
. - Use negative integers to count from the end of the list, e.g.: |
|
94 |
. e.g., pick 2 4-7 -3
|
|
95 |
. - Use negative integers to count from the end of the list, e.g.,:
|
|
96 | 96 |
. -2 means : the command before the last one |
97 | 97 |
. -2-5 means : last 2 commands + the first 5 |
98 | 98 |
. -5--2 means : the last 5 commands except the last 2 |
... | ... | |
151 | 151 |
. 1. <order-id> : pick the <order-id>th command |
152 | 152 |
. 2. <order-id-1>-<order-id-2> : pick all commands ordered in the range |
153 | 153 |
. [<order-id-1> - <order-id-2>] |
154 |
. - Use negative integers to count from the end of the list, e.g.: |
|
154 |
. - Use negative integers to count from the end of the list, e.g.,:
|
|
155 | 155 |
. -2 means : the command before the last one |
156 | 156 |
. -2-5 means : last 2 commands + the first 5 |
157 | 157 |
. -5--2 mean |
b/kamaki/cli/commands/image.py | ||
---|---|---|
407 | 407 |
class image_register(_init_image, _optional_json): |
408 | 408 |
"""(Re)Register an image file to an Image service |
409 | 409 |
The image file must be stored at a pithos repository |
410 |
Some metadata can be set by user (e.g. disk-format) while others are set |
|
411 |
only automatically (e.g. image id). There are also some custom user |
|
410 |
Some metadata can be set by user (e.g., disk-format) while others are set
|
|
411 |
only automatically (e.g., image id). There are also some custom user
|
|
412 | 412 |
metadata, called properties. |
413 | 413 |
A register command creates a remote meta file at |
414 | 414 |
. <container>:<image path>.meta |
b/kamaki/cli/commands/pithos.py | ||
---|---|---|
1884 | 1884 |
msg = 'Failed to convert %s to bytes' % user_input, |
1885 | 1885 |
raiseCLIError(qe, msg, details=[ |
1886 | 1886 |
'Syntax: containerlimit set <limit>[format] [container]', |
1887 |
'e.g.: containerlimit set 2.3GB mycontainer', |
|
1887 |
'e.g.,: containerlimit set 2.3GB mycontainer',
|
|
1888 | 1888 |
'Valid formats:', |
1889 | 1889 |
'(*1024): B, KiB, MiB, GiB, TiB', |
1890 | 1890 |
'(*1000): B, KB, MB, GB, TB']) |
... | ... | |
2036 | 2036 |
Deleted objects may still have versions that can be used to restore it and |
2037 | 2037 |
get information about its previous state. |
2038 | 2038 |
The version number can be used in a number of other commands, like info, |
2039 |
copy, move, meta. See these commands for more information, e.g. |
|
2039 |
copy, move, meta. See these commands for more information, e.g.,
|
|
2040 | 2040 |
/file info -h |
2041 | 2041 |
""" |
2042 | 2042 |
|
b/kamaki/cli/commands/snf-astakos.py | ||
---|---|---|
99 | 99 |
@command(snfastakos_cmds) |
100 | 100 |
class astakos_user_info(_astakos_init, _optional_json): |
101 | 101 |
"""Authenticate a user |
102 |
Get user information (e.g. unique account name) from token |
|
102 |
Get user information (e.g., unique account name) from token
|
|
103 | 103 |
Token should be set in settings: |
104 | 104 |
* check if a token is set /config get cloud.default.token |
105 | 105 |
* permanently set a token /config set cloud.default.token <token> |
... | ... | |
412 | 412 |
|
413 | 413 |
arguments = dict( |
414 | 414 |
accept=CommaSeparatedListArgument( |
415 |
'commission ids to accept (e.g. --accept=11,12,13,...', |
|
415 |
'commission ids to accept (e.g., --accept=11,12,13,...',
|
|
416 | 416 |
'--accept'), |
417 | 417 |
reject=CommaSeparatedListArgument( |
418 |
'commission ids to reject (e.g. --reject=11,12,13,...', |
|
418 |
'commission ids to reject (e.g., --reject=11,12,13,...',
|
|
419 | 419 |
'--reject'), |
420 | 420 |
) |
421 | 421 |
|
Also available in: Unified diff